Skip to content

포맷 ​

English.

이 문서는 템플릿의 토큰과 포매터를 정한다. 포매터는 태그 안의 포맷 스타일, 줄의 들여쓰기, 포매터가 유지하는 부분, 안전 불변식으로 정한다. 모든 구현은 적합성 계약의 명령 tokens와 format으로 둘을 제공하고(CNF-4), 적합성 케이스가 그 결과를 비교한다(CNF-2, CNF-9). 명령줄 도구 template-fmt, 언어 서버, 에디터 어댑터는 포매터, 언어 서버, 에디터에서 설명한다.

토큰 ​

FMT-1 템플릿의 토큰은 템플릿을 분석한 결과다. 파서가 받아들인 모든 태그의 모든 부분을 EDT-8의 타입(에디터 지원)으로 분류하며, 위치 순으로 정렬되고 서로 겹치지 않는다. 태그 밖의 텍스트에는 토큰이 없다. 모든 구현은 같은 소스와 구분자에 대해 같은 토큰을 계산한다.

FMT-2 토큰은 [start, end, type]이다. start와 end는 소스의 UTF-8 바이트 오프셋이며, AST span처럼 byte order mark 다음의 첫 바이트부터 센다(LEX-2, AST-2). 파싱되지 않는 소스에는 토큰이 없다. 명령 tokens는 파서 오류를 보고한다.

포맷 스타일 ​

FMT-3 포매터는 태그 안의 공백(FMT-4~FMT-6)과 줄 앞의 공백과 탭(FMT-9~FMT-12)만 바꾼다. 태그 밖의 다른 텍스트는 바꾸지 않는다.

FMT-4 각 태그 종류는 하나의 포맷 결과를 갖는다.

태그포맷 결과
echo{= expr}
if, else-if, else, close{? expr}, {:? expr}, {:}, {/}
loop{@ name = expr}
대입{:name = expr}, {:name += expr}, {:name++}
include{+ path}
block{# id path name name:value}
if-block{?# id}

여는 구분자와 시길 사이와 닫는 구분자 앞에는 공백을 두지 않고, 본문이 있는 태그는 시길 뒤에 공백 하나를 둔다.

FMT-5 표현식 안에서는 다음을 따른다.

  • 이항 연산자, 삼항 연산자의 ?와 :, ?:, ??, in, =>, 파이프 |의 앞뒤에 공백 하나를 둔다.
  • , 뒤에 공백 하나를 두고 , 앞에는 공백을 두지 않는다.
  • (, [, ..., 단항 !와 - 뒤에 공백을 두지 않고, )와 ] 앞에 공백을 두지 않는다.
  • 함수나 메서드 이름과 ( 사이, 피연산자와 인덱스 [ 사이, 클래스 호출의 :: 앞뒤에 공백을 두지 않는다.
  • 접근자(.name, .0)는 왼쪽 피연산자에 붙인다.

FMT-6 block 태그 본문은 단어를 유지하고 단어 사이의 연속 공백을 공백 하나로 바꾼다.

FMT-7 포매터는 다음 부분을 작성된 그대로 둔다.

  • 공백과 줄 종결자를 포함한 태그 밖의 텍스트. 줄의 들여쓰기는 예외다.
  • 주석 {* ... *}과 구분자 지시문 {% delimiter ..}
  • 본문에 줄 종결자가 있는 태그
  • 래퍼 태그의 래퍼, 그리고 래퍼와 겹친 구분자 사이의 공백
  • 문자열 리터럴, 숫자 리터럴, include 경로의 문자
  • 소스 시작의 byte order mark

FMT-8 포매터는 엔진 구분자(LEX-22)로 태그를 읽고, {% delimiter ..} 지시문 뒤의 태그에는 그 지시문의 구분자를 적용한다.

들여쓰기 ​

FMT-9 옵션 indent는 들여쓰기 단위이며 공백 여러 개 또는 탭 하나다. 기본값은 공백 두 개다. 포매터는 각 줄의 들여쓰기를 들여쓰기 단위를 그 줄의 깊이만큼 반복한 값으로 정한다. indent: null은 모든 줄의 들여쓰기를 유지하므로 태그 안의 공백만 바뀐다. 옵션 templateBlocks는 indent(기본값) 또는 flat이다.

<div class="board-list">
  <h2>{= t["list.heading"]}</h2>
  {? length(p.props.posts) == 0}
    <p>{= t["list.empty"]}</p>
  {:}
    <ul>
      {@ post = p.props.posts}
        <li><a href="{= post.href}">{= post.title}</a></li>
      {/}
    </ul>
  {/}
</div>

FMT-10 줄의 깊이는 그 줄이 시작하는 위치에서 열려 있는 HTML 요소와 템플릿 블록의 수다. 템플릿 블록은 if, loop, if-block 태그부터 그 닫는 태그까지다.

  • 공백이 아닌 첫 텍스트가 HTML 끝 태그, 템플릿 닫는 태그 {/}, 분기 태그 {:}나 {:? }인 줄은 한 단계 덜 들여쓴다.
  • templateBlocks: 'flat'이면 템플릿 블록은 단계를 더하지 않는다. 블록의 태그와 그 안의 줄은 HTML 요소만으로 정한 깊이를 갖는다. 'indent'는 템플릿 블록을 요소처럼 센다.
  • 여러 줄에 걸친 HTML 시작 태그 안의 줄은 그 태그보다 한 단계 더 들여쓴다. 따옴표로 감싼 속성 값 안에서 시작하는 줄은 들여쓰기를 유지한다.
  • void 요소(area, base, br, col, embed, hr, img, input, link, meta, param, source, track, wbr)와 />로 끝나는 시작 태그는 요소를 열지 않는다.

FMT-11 포매터는 <pre>, <textarea>, <script>, <style>, HTML 주석, 태그 안에서 시작하는 줄의 들여쓰기를 유지하며, 끝 태그가 있는 줄도 포함한다. <script>, <style>, <textarea> 안의 HTML 태그는 읽지 않는다. 빈 줄은 비운다. 들여쓰기 뒤의 줄 텍스트는 바뀌지 않으며, 포매터는 요소를 다른 줄로 옮기지 않는다.

FMT-12 들여쓰기에는 짝이 맞는 HTML 구조가 필요하다. 끝 태그가 열린 요소와 맞지 않거나, 템플릿 끝까지 닫히지 않은 요소가 있거나, 템플릿 블록의 분기 사이 또는 시작 태그와 닫는 태그 사이에서 열린 HTML 요소가 다르면, 포매터는 구조가 어긋난 태그의 위치와 함께 오류 html을 돌려주고 아무것도 바꾸지 않는다. indent: null이면 구조를 읽지 않는다.

줄 앞의 공백과 탭은 렌더 출력의 일부다. 다만 블록 태그만 있는 줄은 렌더러가 그 공백과 함께 제거한다. 따라서 들여쓰기는 렌더된 줄 앞의 공백을 바꾼다. 브라우저는 그 공백을 표시하지 않는다. 예외는 포매터가 유지하는 <pre>와 <textarea> 안, 그리고 CSS white-space가 공백을 유지하는 요소 안이다.

안전 불변식 ​

FMT-13 포매터는 소스를 파싱하고, 파서의 태그 본문과 토큰으로 각 태그를 포맷한 뒤 결과를 다시 파싱한다. 두 AST가 모든 span 필드를 제거한 상태에서, 그리고 들여쓰기를 바꿀 때는 텍스트의 모든 줄 앞 공백과 탭도 제거한 상태에서 같을 때만 결과를 돌려준다. 다르면 오류를 돌려주고 호출자는 소스를 유지한다.

FMT-14 포매터의 결과는 포맷한 텍스트이거나 reason, code, line, col, message를 가진 오류다.

reason조건code, line, col
parse소스가 파싱되지 않는다파서 오류
invariant포맷한 텍스트가 파싱되지 않거나 다른 AST로 파싱된다null, 처음 바뀐 곳
htmlHTML 구조의 짝이 맞지 않는다(FMT-12)null, 구조가 어긋난 태그

line과 col은 1부터 센다. col은 파서 오류처럼 바이트를 센다(ERR-1). 바뀌는 것이 없으면 텍스트는 소스와 같다.

FMT-15 포맷은 멱등이다. 포맷한 텍스트를 같은 옵션으로 다시 포맷하면 같은 텍스트가 나온다.