diff --git a/resources/css/app.scss b/resources/css/app.scss
index 94a6e9693..70b5f042c 100644
--- a/resources/css/app.scss
+++ b/resources/css/app.scss
@@ -612,3 +612,8 @@ label.required:before {
.text-right {
text-align: right;
}
+
+.anchor-link {
+ margin-left: .1em;
+ padding: .1em .3em;
+}
diff --git a/resources/docs/integration-to-new-project.md b/resources/docs/integration-to-new-project.md
index 959d5aee1..810caf36b 100644
--- a/resources/docs/integration-to-new-project.md
+++ b/resources/docs/integration-to-new-project.md
@@ -93,97 +93,13 @@ Again, we run Rector, apply changes, create pull-request, and go for merge. Slow
-Every codebase is different, and sometimes, we come across a rule that is not safe to apply. We can skip it for now:
+### Skip what you don't need
-```diff
- withPaths([__DIR__ . '/src', __DIR__ . '/tests'])
-+ ->withSkip([RemoveReferenceFromCallRector::class])
- ->withPhpSets(php54: true);
-```
-
-This is an entirely valid upgrade process. We will deal with skips later once our codebase is upgraded to our PHP version and much more robust.
-
-Check [other ways to use `withSkip()`](/documentation/ignoring-rules-or-paths).
-
-## 4. Type Coverage Level
-
-What is a PHP 8.3 project without a single type declaration?
-
-A horse with Tesla bodywork.
-
-
-
-Type coverage is one of the most influential metrics in a modern PHP project. We can have a high PHP version in `composer.json`, but our code can still be full of `mixed` types, giving us zero confidence. There is a PHPStan package - [`type-coverage`](https://github.com/TomasVotruba/type-coverage)- that helps raise the bar 1% at a time.
-
-How can we use Rector to help out with type coverage? We can add a prepared set:
-
-```diff
- withPaths([__DIR__ . '/src', __DIR__ . '/tests'])
-+ ->withPreparedSets(typeDeclarations: true);
-```
-
-Let's run Rector:
-
-```bash
-vendor/bin/rector --dry-run
-```
-
-Wow, over 90 % of files were changed. That's going to be a very long review. We can do better than that.
-
-## 5. One Level at a Time
-
-Instead of applying ~50 type declaration rules at once, we can apply them individually. This is much easier to review and explain to your team. But which one should we start with?
-
-We took the liberty of sorting the rules from the easy-pick to more complex ones. You can enable them yourself and go one level at a time:
-
-```diff
- withPaths([__DIR__ . '/src', __DIR__ . '/tests'])
-- ->withPreparedSets(typeDeclaration: true);
-+ ->withTypeCoverageLevel(1);
-```
-
-Now run Rector to see the changed files:
-
-```php
-vendor/bin/rector
-```
-
-Only five files? We can do that in a day. We create a pull request, get a review, and merge. The next day, we can continue with level 2. You get the idea.
-
-## 6. Dead Code Level
-
-Are you done with the type level and reached [99 % type coverage](https://github.com/tomasVotruba/type-coverage)? It's time to move on to dead code removal.
-
-Again, we could use the prepared dead-code set, but the number of changes would be huge. Instead, we make use of the dead-code level:
-
-```php
-withPaths([__DIR__ . '/src', __DIR__ . '/tests'])
- ->withTypeCoverageLevel(40)
- ->withDeadCodeLevel(1);
-```
+Every codebase is different, and sometimes, we come across a rule that is not safe to apply, or files we want to skip.
+Use [ignoring](/documentation/ignoring-rules-or-paths) for that.
+## Prepared sets, one level at a time
-We increase it by 1, run Rector, create a pull request, get a review, and merge.
+Rector provides dozens of [prepared sets](/documentation/set-lists). But the same way we don't read every book in library we visit for first time, we don't enable all prepared sets at once.
-Once we reach the highest dead code level, we can move on to [next prepared sets](/documentation/set-lists).
+Instead, **use [level methods](/documentation/levels) and take it step by step**. It more relaxing path to reach the goal.
diff --git a/resources/docs/levels.md b/resources/docs/levels.md
new file mode 100644
index 000000000..a20e2deb1
--- /dev/null
+++ b/resources/docs/levels.md
@@ -0,0 +1,76 @@
+What is a PHP 8.3 project without a single type declaration?
+
+A horse with Tesla bodywork.
+
+
+
+Type coverage is one of the most influential metrics in a modern PHP project. We can have a high PHP version in `composer.json`, but our code can still be full of `mixed` types, giving us zero confidence. There is a [`type-coverage`](https://github.com/TomasVotruba/type-coverage) package that helps raise the bar 1 % at a time.
+
+How can we use Rector to help out with type coverage?
+
+**What happens if we add full set:**
+
+```diff
+ withPaths([__DIR__ . '/src', __DIR__ . '/tests'])
++ ->withPreparedSets(typeDeclarations: true);
+```
+
+Let's run Rector:
+
+```bash
+vendor/bin/rector --dry-run
+```
+
+Wow, over **90 % of files have changed**. That's going to be a very long review. We can do better than that.
+
+## One Level at a Time
+
+Instead of applying ~50 type declaration rules at once, we can apply them individually. This is much easier to review and explain to your team. But which one should we start with?
+
+We took the liberty of sorting the rules from the easy-pick to more complex ones. You can enable them yourself and go one level at a time:
+
+```diff
+ withPaths([__DIR__ . '/src', __DIR__ . '/tests'])
+- ->withPreparedSets(typeDeclaration: true);
++ ->withTypeCoverageLevel(1);
+```
+
+Now run Rector to see the changed files:
+
+```php
+vendor/bin/rector
+```
+
+Only five files? We can do that in a day. We create a pull request, get a review, and merge. The next day, we can continue with level 2. You get the idea.
+
+## Dead Code and Code Quality Level
+
+Are you done with the type level and reached [99 % type coverage](https://github.com/tomasVotruba/type-coverage)? It's time to move on to dead code removal and improve code quality.
+
+Again, we avoid full-blown prepared set, and make use of level methods:
+
+```php
+withPaths([__DIR__ . '/src', __DIR__ . '/tests'])
+ ->withTypeCoverageLevel(1)
+ ->withDeadCodeLevel(1)
+ ->withCodeQualityLevel(1);
+```
+
+We increase it by 1, run Rector, create a pull request, get a review, and merge.
+
+Once we reach the highest level, we can move on to [next prepared sets](/documentation/set-lists#content-prepared-sets).
diff --git a/resources/docs/set-lists.md b/resources/docs/set-lists.md
index 404ed71f9..26e0c392c 100644
--- a/resources/docs/set-lists.md
+++ b/resources/docs/set-lists.md
@@ -1,3 +1,5 @@
+## Prepared Sets
+
You can use particular single rules, or whole list of rules, called "set lists":
```php
@@ -6,11 +8,22 @@ You can use particular single rules, or whole list of rules, called "set lists":
use Rector\Config\RectorConfig;
return RectorConfig::configure()
- ->withPreparedSets(deadCode: true, codeQuality: true);
+ ->withPreparedSets(
+ deadCode: true,
+ codeQuality: true,
+ codingStyle: true,
+ naming: true,
+ privatization: true,
+ typeDeclarations: true,
+ rectorPreset: true,
+ // ...
+ );
```
That way you can include group of rules that focus on certain topic, e.g. in this case on dead detection. It makes config small and clear.
+Try autocomplete in your IDE to see all available prepared sets.
+
## PHP Sets
The best practise is to use PHP version defined in `composer.json`. Rector will automatically pick it up with empty `->withPhpSets()` method:
diff --git a/src/Documentation/DocumentationMenuFactory.php b/src/Documentation/DocumentationMenuFactory.php
index 37402a9aa..fc2aa4b5c 100644
--- a/src/Documentation/DocumentationMenuFactory.php
+++ b/src/Documentation/DocumentationMenuFactory.php
@@ -26,6 +26,7 @@ public function create(): array
$this->documentationMenuItemFactory->createSection('integration-to-new-project', 'New Project', true),
$this->documentationMenuItemFactory->createSection('define-paths', 'Define Paths'),
$this->documentationMenuItemFactory->createSection('set-lists', 'Set Lists'),
+ $this->documentationMenuItemFactory->createSection('levels', 'Levels', true),
$this->documentationMenuItemFactory->createSection('composer-based-sets', 'Composer-Based Sets', true),
$this->documentationMenuItemFactory->createInternalLink(FindRuleController::class, 'Find Rules'),
$this->documentationMenuItemFactory->createSection(
diff --git a/src/Markdown/GithubMarkdownConverter.php b/src/Markdown/GithubMarkdownConverter.php
new file mode 100644
index 000000000..05175ec4b
--- /dev/null
+++ b/src/Markdown/GithubMarkdownConverter.php
@@ -0,0 +1,33 @@
+ $config
+ */
+ public function __construct(array $config = [])
+ {
+ $environment = new Environment($config);
+ $environment->addExtension(new CommonMarkCoreExtension());
+ $environment->addExtension(new GithubFlavoredMarkdownExtension());
+
+ // @see https://commonmark.thephpleague.com/1.5/extensions/heading-permalinks/
+ $environment->addExtension(new HeadingPermalinkExtension());
+
+ parent::__construct($environment);
+ }
+}
diff --git a/src/Thumbnail/ThumbnailGenerator.php b/src/Thumbnail/ThumbnailGenerator.php
index 9f4db1f4b..9bf97f11c 100644
--- a/src/Thumbnail/ThumbnailGenerator.php
+++ b/src/Thumbnail/ThumbnailGenerator.php
@@ -45,10 +45,14 @@ public function addRectorLogo(ImageInterface $image): void
{
$rectorLogoImage = $this->imagine->open(__DIR__ . '/../../public/assets/images/new-logo/rector-square.png');
- $currentWidth = $rectorLogoImage->getSize()->getWidth();
- $currentHeight = $rectorLogoImage->getSize()->getHeight();
+ $currentWidth = $rectorLogoImage->getSize()
+ ->getWidth();
+ $currentHeight = $rectorLogoImage->getSize()
+ ->getHeight();
- $rectorLogoImage->resize(new Box($currentWidth * self::RESIZE_LOGO_RATIO, $currentHeight * self::RESIZE_LOGO_RATIO));
+ $rectorLogoImage->resize(
+ new Box($currentWidth * self::RESIZE_LOGO_RATIO, $currentHeight * self::RESIZE_LOGO_RATIO)
+ );
$image->paste($rectorLogoImage, new Point(1450, 50));
}
diff --git a/src/functions.php b/src/functions.php
index a9a2b95af..10b5450cc 100644
--- a/src/functions.php
+++ b/src/functions.php
@@ -5,8 +5,8 @@
// @see https://github.com/thephpleague/commonmark
use App\Enum\FlashType;
+use App\Markdown\GithubMarkdownConverter;
use Illuminate\Http\RedirectResponse;
-use League\CommonMark\GithubFlavoredMarkdownConverter;
function latestPhp(): string
{
@@ -21,8 +21,15 @@ function slugify(string $value): string
function markdown(string $contents): Stringable
{
- $markdownConverter = new GithubFlavoredMarkdownConverter([
+ // @see https://commonmark.thephpleague.com/1.5/extensions/heading-permalinks/
+ $markdownConverter = new GithubMarkdownConverter([
'allow_unsafe_links' => false,
+ 'heading_permalink' => [
+ 'html_class' => 'anchor-link', // CSS class for the anchor
+ 'symbol' => '#', // Symbol for the link
+ 'id_prefix' => 'content', // Prefix for the ID
+ 'insert' => 'after', // Where to insert the link: 'before', 'after', or 'none'
+ ],
]);
return $markdownConverter->convert($contents);