Nette Documentation Preview

syntax
Trivia
******

.[perex]
Bílé znaky a komentáře nejsou uzly, ale trivia přivěšená k tokenům. Pravidlo, podle kterého se dělí, a jak je číst a psát, aniž by se rozbil zbytek souboru.


Komu patří mezera
=================

Mezi dvěma tokeny může být cokoli: mezery, konce řádků, prázdné řádky, komentáře. Aby se dalo o tom všem mluvit přesně, má každý token dvě pole trivia a jedno pravidlo říká, co kam patří:

**Koncová trivia** tokenu je všechno za ním až po první konec řádku včetně. **Úvodní trivia** dalšího tokenu je zbytek: prázdné řádky, odsazení, komentáře na vlastních řádcích, dokumentační komentáře.

```php
$sum = 0; // running total
		return $sum;
```

Středník za `0` má koncová trivia `[Whitespace " ", Comment "// running total", EndOfLine "\n"]`; `return` má úvodní trivia `[Whitespace "\t\t"]`. Komentář na řádku patří k řádku, na kterém je, a dokumentační komentář nad metodou patří k metodě, což je přesně to, co byste čekali, když se metoda přesouvá.

Každá trivia má druh (`TriviaKind::Whitespace`, `EndOfLine`, `Comment`, `DocComment`, `OpenTag`), text a řádek v původním souboru. Bílé znaky jsou rozdělené na běhy mezer a jednotlivé konce řádků, takže úvodní trivia tokenu, který začíná řádek, vypadá `[..., EndOfLine, Whitespace]` a prázdný řádek je `EndOfLine` následovaný `EndOfLine`.

Tři zvláštnosti, které stojí za zapamatování:

- **`<?php` není token**, ale trivia druhu `OpenTag` včetně povinné mezery nebo konce řádku za ním; je vždy úvodní trivia následujícího tokenu.
- **`?>` je token** (`TokenKind::CloseTag`), který si nese i konec řádku, jenž PHP za ním polyká; parser ho vidí jako středník.
- **Bílé znaky, které PHP počítá do tokenu, zůstávají v textu tokenu**: inline HTML, obsah heredocu včetně odsazení uzavíracího návěští, `( int )`. Trivia uvnitř interpolovaného řetězce (`"{$a /* c */}"`) nesou `inInterpolation` a metody na úpravu trivia je odmítnou, protože ta mezera je součást hodnoty řetězce.


Čtení
=====

Nejčastější otázky mají hotové odpovědi, aby nikdo nemusel procházet pole trivia ručně:

```php
$token->getTrailingSpace();       // vodorovná mezera za tokenem na témže řádku, null když řádek končí nebo je tam komentář
$token->startsLine();             // začíná řádek
$token->getLineIndentation();     // odsazení řádku, na kterém token stojí
$token->hasComment();             // komentář v jeho trivia
$token->getComments();            // ty komentáře jako pole Trivia
$token->hasCommentUpTo($other);   // komentář kdekoli mezi dvěma tokeny
$node->hasComment();              // komentář uvnitř uzlu, kromě jeho okrajů
$node->getComments();
$node->getDocComment();           // dokumentační komentář nad uzlem, jako Trivia
$token->getLineWidth($style);     // šířka řádku tak, jak ji vidíte, tabulátor podle stylu
```

Sama trivia komentáře odpoví na to, čím je, a vydá svůj text bez značek:

```php
$comment->isLineComment();    // // nebo #
$comment->isDocComment();     // /** */
$comment->isMultiLine();
$comment->getCommentText();   // 'running total'
```


Zápis
=====

Text a trivia tokenu jsou `private(set)`, takže se mění jen metodami; jsou to metody a ne vlastnosti proto, že `?->` nesmí stát vlevo od přiřazení, a `$node->getFirstToken()?->setText('x')` je zápis, který se používá pořád. Trivia jde přepsat celá (`setLeadingTrivia()`, `setTrailingTrivia()`), ale skoro nikdy to není potřeba: běžné úpravy mají vlastní metody, které dodrží pravidlo o tom, komu mezera patří.

```php
$token->setTrailingSpace(' ');        // mezera za tokenem; odmítne, když řádek končí nebo následuje komentář
$token->ensureLeadingNewline();       // token na vlastní řádek, konec řádku jde do koncových trivia předchozího tokenu
$token->setBlankLinesBefore(2);       // přesně dva prázdné řádky nad tokenem, nad komentářem, který k němu patří
$token->setIndentation("\t\t");       // odsazení řádku tokenu; token musí řádek začínat
$token->removeTrailingWhitespace();   // mezery na konci řádku, který token končí; komentář a konec řádku zůstanou
$token->removeTrivia($comment);       // jedna trivia, obvykle komentář, i s mezerou nebo řádkem, které by po ní zbyly
$token->replaceTrivia($old, $new);    // výměna na místě
$node->setEdgeTrivia(leading: []);    // trivia na vnějších okrajích uzlu, kde první nebo poslední token nemusí být
$node->replaceDocComment($trivia);    // dokumentační komentář uzlu
$node->removeDocComment();
```

Jedno pravidlo je za tím vším: **konec řádku, který uzavírá řádek tokenu, patří do jeho koncových trivia, nikdy do úvodních trivia dalšího tokenu.** Kdo ho dá jinam, rozbije `getTrailingSpace()` a všechno, co na něm stojí; kdo staví na `ensureLeadingNewline()` a `setBlankLinesBefore()`, má to za sebe vyřešené.

Vlastnost s hookem nesnese nepřímou změnu, takže `end($token->trailingTrivia)` potřebuje kopii pole, ne referenci na vlastnost.


Styl a odsazení
===============

`Style` je odsazovací jednotka, konec řádku a šířka tabulátoru souboru: výchozí tabulátor, `"\n"` a 4. `Style::detectEol($code)` pozná převládající konec řádku, `withIndent()` a `withEol()` odvodí styl, `indent($level)` vrátí odsazení dané úrovně.

`Indentation` je pomocník nad odsazením celého řádku včetně komentářů nad tokenem: `Indentation::set($token, $indentation, $commentIndentation)` odsadí řádek i komentáře nad ním, `normalize()` převede odsazení do znaků stylu, `opensLine($token)` řekne, zda je konec řádku nad tokenem trivia (a odsazení tedy smí měnit), nebo text (za inline HTML, heredocem, `?>`), kde je odsazení součástí obsahu. `findOwner()`, `findRole()` a `infer()` odpovídají na otázku, co řádek pokračuje a jaké odsazení by takový řádek konvenčně měl; na nich stojí rozvržení kódu podle stromu.

Trivia

Bílé znaky a komentáře nejsou uzly, ale trivia přivěšená k tokenům. Pravidlo, podle kterého se dělí, a jak je číst a psát, aniž by se rozbil zbytek souboru.

Komu patří mezera

Mezi dvěma tokeny může být cokoli: mezery, konce řádků, prázdné řádky, komentáře. Aby se dalo o tom všem mluvit přesně, má každý token dvě pole trivia a jedno pravidlo říká, co kam patří:

Koncová trivia tokenu je všechno za ním až po první konec řádku včetně. Úvodní trivia dalšího tokenu je zbytek: prázdné řádky, odsazení, komentáře na vlastních řádcích, dokumentační komentáře.

$sum = 0; // running total
		return $sum;

Středník za 0 má koncová trivia [Whitespace " ", Comment "// running total", EndOfLine "\n"]; return má úvodní trivia [Whitespace "\t\t"]. Komentář na řádku patří k řádku, na kterém je, a dokumentační komentář nad metodou patří k metodě, což je přesně to, co byste čekali, když se metoda přesouvá.

Každá trivia má druh (TriviaKind::Whitespace, EndOfLine, Comment, DocComment, OpenTag), text a řádek v původním souboru. Bílé znaky jsou rozdělené na běhy mezer a jednotlivé konce řádků, takže úvodní trivia tokenu, který začíná řádek, vypadá [..., EndOfLine, Whitespace] a prázdný řádek je EndOfLine následovaný EndOfLine.

Tři zvláštnosti, které stojí za zapamatování:

  • <?php není token, ale trivia druhu OpenTag včetně povinné mezery nebo konce řádku za ním; je vždy úvodní trivia následujícího tokenu.
  • ?> je token (TokenKind::CloseTag), který si nese i konec řádku, jenž PHP za ním polyká; parser ho vidí jako středník.
  • Bílé znaky, které PHP počítá do tokenu, zůstávají v textu tokenu: inline HTML, obsah heredocu včetně odsazení uzavíracího návěští, ( int ). Trivia uvnitř interpolovaného řetězce ("{$a /* c */}") nesou inInterpolation a metody na úpravu trivia je odmítnou, protože ta mezera je součást hodnoty řetězce.

Čtení

Nejčastější otázky mají hotové odpovědi, aby nikdo nemusel procházet pole trivia ručně:

$token->getTrailingSpace();       // vodorovná mezera za tokenem na témže řádku, null když řádek končí nebo je tam komentář
$token->startsLine();             // začíná řádek
$token->getLineIndentation();     // odsazení řádku, na kterém token stojí
$token->hasComment();             // komentář v jeho trivia
$token->getComments();            // ty komentáře jako pole Trivia
$token->hasCommentUpTo($other);   // komentář kdekoli mezi dvěma tokeny
$node->hasComment();              // komentář uvnitř uzlu, kromě jeho okrajů
$node->getComments();
$node->getDocComment();           // dokumentační komentář nad uzlem, jako Trivia
$token->getLineWidth($style);     // šířka řádku tak, jak ji vidíte, tabulátor podle stylu

Sama trivia komentáře odpoví na to, čím je, a vydá svůj text bez značek:

$comment->isLineComment();    // // nebo #
$comment->isDocComment();     // /** */
$comment->isMultiLine();
$comment->getCommentText();   // 'running total'

Zápis

Text a trivia tokenu jsou private(set), takže se mění jen metodami; jsou to metody a ne vlastnosti proto, že ?-> nesmí stát vlevo od přiřazení, a $node->getFirstToken()?->setText('x') je zápis, který se používá pořád. Trivia jde přepsat celá (setLeadingTrivia(), setTrailingTrivia()), ale skoro nikdy to není potřeba: běžné úpravy mají vlastní metody, které dodrží pravidlo o tom, komu mezera patří.

$token->setTrailingSpace(' ');        // mezera za tokenem; odmítne, když řádek končí nebo následuje komentář
$token->ensureLeadingNewline();       // token na vlastní řádek, konec řádku jde do koncových trivia předchozího tokenu
$token->setBlankLinesBefore(2);       // přesně dva prázdné řádky nad tokenem, nad komentářem, který k němu patří
$token->setIndentation("\t\t");       // odsazení řádku tokenu; token musí řádek začínat
$token->removeTrailingWhitespace();   // mezery na konci řádku, který token končí; komentář a konec řádku zůstanou
$token->removeTrivia($comment);       // jedna trivia, obvykle komentář, i s mezerou nebo řádkem, které by po ní zbyly
$token->replaceTrivia($old, $new);    // výměna na místě
$node->setEdgeTrivia(leading: []);    // trivia na vnějších okrajích uzlu, kde první nebo poslední token nemusí být
$node->replaceDocComment($trivia);    // dokumentační komentář uzlu
$node->removeDocComment();

Jedno pravidlo je za tím vším: konec řádku, který uzavírá řádek tokenu, patří do jeho koncových trivia, nikdy do úvodních trivia dalšího tokenu. Kdo ho dá jinam, rozbije getTrailingSpace() a všechno, co na něm stojí; kdo staví na ensureLeadingNewline() a setBlankLinesBefore(), má to za sebe vyřešené.

Vlastnost s hookem nesnese nepřímou změnu, takže end($token->trailingTrivia) potřebuje kopii pole, ne referenci na vlastnost.

Styl a odsazení

Style je odsazovací jednotka, konec řádku a šířka tabulátoru souboru: výchozí tabulátor, "\n" a 4. Style::detectEol($code) pozná převládající konec řádku, withIndent() a withEol() odvodí styl, indent($level) vrátí odsazení dané úrovně.

Indentation je pomocník nad odsazením celého řádku včetně komentářů nad tokenem: Indentation::set($token, $indentation, $commentIndentation) odsadí řádek i komentáře nad ním, normalize() převede odsazení do znaků stylu, opensLine($token) řekne, zda je konec řádku nad tokenem trivia (a odsazení tedy smí měnit), nebo text (za inline HTML, heredocem, ?>), kde je odsazení součástí obsahu. findOwner(), findRole() a infer() odpovídají na otázku, co řádek pokračuje a jaké odsazení by takový řádek konvenčně měl; na nich stojí rozvržení kódu podle stromu.