diff --git a/docs/administration/recent_activity/recent_activity.md b/docs/administration/recent_activity/recent_activity.md index 0f531270f7..aec993b4de 100644 --- a/docs/administration/recent_activity/recent_activity.md +++ b/docs/administration/recent_activity/recent_activity.md @@ -83,7 +83,7 @@ In the following example, log groups that contain at least one creation of a Con It uses the default `admin` user that has a [permission](#permission-and-security) to list everyone's entries. ```php hl_lines="34-38" -[[= include_file('code_samples/recent_activity/src/Command/MonitorRecentContentCreationCommand.php') =]] +[[= include_code('code_samples/recent_activity/src/Command/MonitorRecentContentCreationCommand.php') =]] ``` ```console @@ -143,7 +143,7 @@ In the following example, an event subscriber is subscribing to an event dispatc This event has the information needed by a log entry (see details after the example). ```php -[[= include_file('code_samples/recent_activity/src/EventSubscriber/MyFeatureEventSubscriber.php') =]] +[[= include_code('code_samples/recent_activity/src/EventSubscriber/MyFeatureEventSubscriber.php') =]] ``` `ActivityLogService::build()` function returns an `Ibexa\Contracts\ActivityLog\Values\CreateActivityLogStruct` which can then be passed to `ActivityLogService::save`. @@ -190,7 +190,7 @@ In the following example, several actions are logged into one context group, eve - `complete` ``` php -[[= include_file('code_samples/recent_activity/src/Command/ActivityLogContextTestCommand.php', 46, 66, remove_indent=True) =]] +[[= include_code('code_samples/recent_activity/src/Command/ActivityLogContextTestCommand.php', 47, 66, remove_indent=True) =]] ``` Context groups can't be nested. @@ -227,13 +227,13 @@ First, follow an example of a default template overriding the one from the bundl It can be used during development as a fallback for classes that aren't mapped yet. ``` twig -[[= include_file('code_samples/recent_activity/templates/themes/admin/activity_log/ui/default.html.twig') =]] +[[= include_code('code_samples/recent_activity/templates/themes/admin/activity_log/ui/default.html.twig') =]] ``` Here is an example of a `ClassNameMapperInterface` associating the class `App\MyFeature\MyFeature` with the identifier `my_feature`: ``` php -[[= include_file('code_samples/recent_activity/src/ActivityLog/ClassNameMapper/MyFeatureNameMapper.php') =]] +[[= include_code('code_samples/recent_activity/src/ActivityLog/ClassNameMapper/MyFeatureNameMapper.php') =]] ``` This mapper also provides a translation for the class name in the **Filters** menu. @@ -242,13 +242,13 @@ This translation can be extracted with `php bin/console jms:translation:extract To be taken into account, this mapper must be registered as a service: ``` yaml -[[= include_file('code_samples/recent_activity/config/append_to_services.yaml') =]] +[[= include_code('code_samples/recent_activity/config/append_to_services.yaml') =]] ``` Here is an example of a `PostActivityListLoadEvent` subscriber which loads the related object when it's an `App\MyFeature\MyFeature`, and attaches it to the log entry: ``` php -[[= include_file('code_samples/recent_activity/src/EventSubscriber/MyFeaturePostActivityListLoadEventSubscriber.php') =]] +[[= include_code('code_samples/recent_activity/src/EventSubscriber/MyFeaturePostActivityListLoadEventSubscriber.php') =]] ``` The following template is made to display the object of `App\MyFeature\MyFeature` (now identified as `my_feature`) when the action is `simulate`, @@ -256,7 +256,7 @@ so, it's named in `templates/themes/admin/activity_log/ui/my_feature/simulate.ht Thanks to the previous subscriber, the related object is available at display time: ``` twig -[[= include_file('code_samples/recent_activity/templates/themes/admin/activity_log/ui/my_feature/simulate.html.twig') =]] +[[= include_code('code_samples/recent_activity/templates/themes/admin/activity_log/ui/my_feature/simulate.html.twig') =]] ``` ## REST API diff --git a/docs/api/rest_api/rest_api_usage/rest_requests.md b/docs/api/rest_api/rest_api_usage/rest_requests.md index 81c3038f1a..0fe823b589 100644 --- a/docs/api/rest_api/rest_api_usage/rest_requests.md +++ b/docs/api/rest_api/rest_api_usage/rest_requests.md @@ -169,13 +169,13 @@ This script: === "XML" ``` php - [[= include_file('code_samples/api/rest_api/create_image.xml.php', 0, None, ' ') =]] + [[= include_code('code_samples/api/rest_api/create_image.xml.php', 1, None, 1) =]] ``` === "JSON" ``` php - [[= include_file('code_samples/api/rest_api/create_image.json.php', 0, None, ' ') =]] + [[= include_code('code_samples/api/rest_api/create_image.json.php', indent_level=1) =]] ``` ### Search (`/views`) diff --git a/docs/search/search_api.md b/docs/search/search_api.md index dd0f39a8aa..f41061d603 100644 --- a/docs/search/search_api.md +++ b/docs/search/search_api.md @@ -29,10 +29,12 @@ The following command takes the content type identifier as an argument and lists ``` php hl_lines="14 16" // ... -[[= include_file('code_samples/api/public_php_api/src/Command/FindContentCommand.php', 4, 7) =]]// ... -[[= include_file('code_samples/api/public_php_api/src/Command/FindContentCommand.php', 17, 19) =]] // ... -[[= include_file('code_samples/api/public_php_api/src/Command/FindContentCommand.php', 32, 48) =]] -[[= include_file('code_samples/api/public_php_api/src/Command/FindContentCommand.php', 48, 49) =]] +[[= include_code('code_samples/api/public_php_api/src/Command/FindContentCommand.php', 5, 7) =]] +// ... +[[= include_code('code_samples/api/public_php_api/src/Command/FindContentCommand.php', 18, 19) =]] + // ... +[[= include_code('code_samples/api/public_php_api/src/Command/FindContentCommand.php', 33, 48) =]] +[[= include_code('code_samples/api/public_php_api/src/Command/FindContentCommand.php', 49, 49) =]] ``` [`SearchService::findContentInfo`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Core-Repository-SearchService.html#method_findContentInfo) (line 16) @@ -120,18 +122,22 @@ For example, the following command lists all content items under the specified p ``` php hl_lines="15-18" // ... -[[= include_file('code_samples/api/public_php_api/src/Command/FilterCommand.php', 4, 9) =]] -// ... -[[= include_file('code_samples/api/public_php_api/src/Command/FilterCommand.php', 19, 21) =]][[= include_file('code_samples/api/public_php_api/src/Command/FilterCommand.php', 33, 53) =]] +[[= include_code('code_samples/api/public_php_api/src/Command/FilterCommand.php', 5, 9) =]] + +[[= include_code('code_samples/api/public_php_api/src/Command/FilterCommand.php', 20, 21) =]] + // ... +[[= include_code('code_samples/api/public_php_api/src/Command/FilterCommand.php', 34, 53) =]] ``` The same Filter can be applied to find locations instead of content items, for example: ``` php hl_lines="20" // ... -[[= include_file('code_samples/api/public_php_api/src/Command/FilterLocationCommand.php', 4, 9) =]] -[[= include_file('code_samples/api/public_php_api/src/Command/FilterCommand.php', 19, 21) =]]// ... -[[= include_file('code_samples/api/public_php_api/src/Command/FilterLocationCommand.php', 33, 53) =]] +[[= include_code('code_samples/api/public_php_api/src/Command/FilterLocationCommand.php', 5, 9) =]] + +[[= include_code('code_samples/api/public_php_api/src/Command/FilterCommand.php', 20, 21) =]] + // ... +[[= include_code('code_samples/api/public_php_api/src/Command/FilterLocationCommand.php', 34, 53) =]] ``` !!! caution @@ -183,8 +189,9 @@ For example, in the code below, `locationId` is provided to list all children of ``` php hl_lines="22-24" // ... -[[= include_file('code_samples/api/public_php_api/src/Controller/CustomController.php', 4, 12) =]] // ... -[[= include_file('code_samples/api/public_php_api/src/Controller/CustomController.php', 16, 32) =]] +[[= include_code('code_samples/api/public_php_api/src/Controller/CustomController.php', 5, 12) =]] + // ... +[[= include_code('code_samples/api/public_php_api/src/Controller/CustomController.php', 17, 32) =]] ``` The rendering of results is then relegated to [templates](templates.md) (lines 22-24). @@ -193,8 +200,9 @@ When using Repository filtering, provide the results of `ContentService::find()` ``` php hl_lines="19" // ... -[[= include_file('code_samples/api/public_php_api/src/Controller/CustomFilterController.php', 4, 12) =]] // ... -[[= include_file('code_samples/api/public_php_api/src/Controller/CustomFilterController.php', 16, 31) =]] +[[= include_code('code_samples/api/public_php_api/src/Controller/CustomFilterController.php', 5, 12) =]] + // ... +[[= include_code('code_samples/api/public_php_api/src/Controller/CustomFilterController.php', 17, 31) =]] ``` ### Paginate search results @@ -203,14 +211,15 @@ To paginate search or filtering results, it's recommended to use the [Pagerfanta ``` php // ... -[[= include_file('code_samples/api/public_php_api/src/Controller/PaginationController.php', 8, 15) =]] // ... -[[= include_file('code_samples/api/public_php_api/src/Controller/PaginationController.php', 19, 39) =]] +[[= include_code('code_samples/api/public_php_api/src/Controller/PaginationController.php', 9, 15) =]] + // ... +[[= include_code('code_samples/api/public_php_api/src/Controller/PaginationController.php', 20, 39) =]] ``` Pagination can then be rendered for example using the following template: ``` html+twig -[[= include_file('code_samples/api/public_php_api/templates/themes/standard/full/custom_pagination.html.twig') =]] +[[= include_code('code_samples/api/public_php_api/templates/themes/standard/full/custom_pagination.html.twig') =]] ``` For more information and examples, see [PagerFanta documentation](https://www.babdev.com/open-source/packages/pagerfanta/docs/2.x/usage). @@ -241,9 +250,10 @@ For more complex searches, you need to combine multiple Criteria. You can do it using logical operators: `LogicalAnd`, `LogicalOr`, and `LogicalNot`. ``` php -[[= include_file('code_samples/api/public_php_api/src/Command/FindComplexCommand.php', 44, 49) =]][[= include_file('code_samples/api/public_php_api/src/Command/FindComplexCommand.php', 53, 54) =]] -[[= include_file('code_samples/api/public_php_api/src/Command/FindComplexCommand.php', 60, 65) =]] +[[= include_code('code_samples/api/public_php_api/src/Command/FindComplexCommand.php', 45, 49) =]] +[[= include_code('code_samples/api/public_php_api/src/Command/FindComplexCommand.php', 54, 54) =]] +[[= include_code('code_samples/api/public_php_api/src/Command/FindComplexCommand.php', 61, 65) =]] ``` This example takes three parameters from a command — `$text`, `$contentTypeId`, and `$locationId`. @@ -257,7 +267,7 @@ The example below uses the `LogicalNot` operator to search for all content conta that doesn't belong to the provided Section: ``` php -[[= include_file('code_samples/api/public_php_api/src/Command/FindComplexCommand.php', 46, 54) =]] +[[= include_code('code_samples/api/public_php_api/src/Command/FindComplexCommand.php', 47, 54) =]] ``` ### Combine independent Criteria @@ -290,7 +300,7 @@ To sort the results of a query, use one of more [Sort Clauses](sort_clause_refer For example, to order search results by their publication date, from oldest to newest, and then alphabetically by content name, add the following Sort Clauses to the query: ``` php -[[= include_file('code_samples/api/public_php_api/src/Command/FindComplexCommand.php', 55, 59) =]] +[[= include_code('code_samples/api/public_php_api/src/Command/FindComplexCommand.php', 56, 59) =]] ``` !!! tip @@ -308,7 +318,7 @@ With aggregations you can find the count of search results or other result infor To do this, you use of the query's `$aggregations` property: ``` php -[[= include_file('code_samples/api/public_php_api/src/Command/FindWithAggregationCommand.php', 30, 35) =]] +[[= include_code('code_samples/api/public_php_api/src/Command/FindWithAggregationCommand.php', 31, 35) =]] ``` The name of the aggregation must be unique in the given query. @@ -316,13 +326,13 @@ The name of the aggregation must be unique in the given query. Access the results by using the `get()` method of the aggregation: ``` php -[[= include_file('code_samples/api/public_php_api/src/Command/FindWithAggregationCommand.php', 39, 40) =]] +[[= include_code('code_samples/api/public_php_api/src/Command/FindWithAggregationCommand.php', 40, 40) =]] ``` Aggregation results contain the name of the result and the count of found items: ``` php -[[= include_file('code_samples/api/public_php_api/src/Command/FindWithAggregationCommand.php', 42, 45) =]] +[[= include_code('code_samples/api/public_php_api/src/Command/FindWithAggregationCommand.php', 43, 45) =]] ``` With field aggregations you can group search results according to the value of a specific field. @@ -331,14 +341,14 @@ In this case the aggregation takes the content type identifier and the field ide The following example creates an aggregation named `selection` that groups results according to the value of the `topic` field in the `article` content type: ``` php -[[= include_file('code_samples/api/public_php_api/src/Command/FindWithAggregationCommand.php', 35, 36) =]] +[[= include_code('code_samples/api/public_php_api/src/Command/FindWithAggregationCommand.php', 36, 36) =]] ``` With term aggregation you can define additional limits to the results. The following example limits the number of terms returned to 5 and only considers terms that have 10 or more results: ``` php -[[= include_file('code_samples/api/public_php_api/src/Command/FindWithAggregationCommand.php', 30, 33) =]] +[[= include_code('code_samples/api/public_php_api/src/Command/FindWithAggregationCommand.php', 31, 33) =]] ``` To use a range aggregation, you must provide a `ranges` array containing a set of `Range` objects that define the borders of the specific range sets. @@ -394,7 +404,7 @@ You build an `EmbeddingQuery` instance by using a builder and pass it to the sea This example shows a minimal embedding query executed directly through the search service: ``` php hl_lines="38-39 41-47 49" -[[= include_file('code_samples/api/public_php_api/src/Command/FindByTaxonomyEmbeddingCommand.php') =]] +[[= include_code('code_samples/api/public_php_api/src/Command/FindByTaxonomyEmbeddingCommand.php') =]] ``` For more information, see [Embeddings reference](embeddings_reference.md). @@ -411,8 +421,9 @@ For a list of supported Criteria and Sort Clauses, see [Search in trash referenc Searching through the trashed content items operates directly on the database, therefore you cannot use external search engines, such as Solr or Elasticsearch, and it's impossible to reindex the data. ``` php -[[= include_file('code_samples/api/public_php_api/src/Command/FindInTrashCommand.php', 4, 6) =]]//... -[[= include_file('code_samples/api/public_php_api/src/Command/FindInTrashCommand.php', 35, 42) =]] +[[= include_code('code_samples/api/public_php_api/src/Command/FindInTrashCommand.php', 5, 6) =]] +//... +[[= include_code('code_samples/api/public_php_api/src/Command/FindInTrashCommand.php', 36, 42, remove_indent=True) =]] ``` !!! caution diff --git a/docs/templating/twig_function_reference/image_twig_functions.md b/docs/templating/twig_function_reference/image_twig_functions.md index 32bb7e9c49..f7eb49c639 100644 --- a/docs/templating/twig_function_reference/image_twig_functions.md +++ b/docs/templating/twig_function_reference/image_twig_functions.md @@ -13,7 +13,7 @@ page_type: reference To render images, use the [`ibexa_render_field()`](field_twig_functions.md#ibexa_render_field) Twig function with the variation name passed as an argument, for example: ``` html+twig -[[= include_file('docs/templating/twig_function_reference/field_twig_functions.md', 40, 48) =]] +[[= include_code('docs/templating/twig_function_reference/field_twig_functions.md', 42, 48) =]] ``` ## Image information diff --git a/main.py b/main.py index 168977021f..3c38638782 100644 --- a/main.py +++ b/main.py @@ -32,6 +32,7 @@ def define_env(env): @env.macro def include_file(filename, start_line=0, end_line=None, glue='', remove_indent=False): """ + DEPRECATED: Use include_code instead. Include a file, optionally indicating start_line and end_line (start counting from 0) optionally set a glue string to lead every string except the first one (can be used for indent) @@ -52,6 +53,18 @@ def include_file(filename, start_line=0, end_line=None, glue='', remove_indent=F return glue.join(line_range) + @env.macro + def include_code(file_path, start_line=1, end_line=None, indent_level=0, remove_indent=False): + """ + Include a file + file_path (string): The path to the file from project root + start_line (int): The line number to start including from (start counting from 1) - default is 1 (include first line) + end_line (int or None): The line number to end including to. If None, include until the end of the file - default is None (include end of file) + indent_level (int): The number of indent (4 spaces) to add to the beginning of each line - default is 0 (no indent added). + remove_indent (bool): Whether to remove absolute indent, the maximum of leading whitespaces without breaking relative indent - default is False (no indent removed) + """ + return include_file(file_path, start_line-1, end_line, ' ' * indent_level, remove_indent).rstrip() + @env.macro def cards(pages, columns=1, style="cards", force_version=False): current_page = env.variables.page diff --git a/tools/code_samples/code_samples_usage.php b/tools/code_samples/code_samples_usage.php index 2d7716d4a0..c0813f00f0 100644 --- a/tools/code_samples/code_samples_usage.php +++ b/tools/code_samples/code_samples_usage.php @@ -35,7 +35,7 @@ function displayBlocks(array $docFileBlocks, ?string $docFilePath = null, $lineO try { $blockContents = getBlockContents($block); foreach ($blockContents['contents'] as $contentLineNumber => $contentLine) { - $prefixedBlockContentLines[] = str_pad($contentLineNumber, 3, 0, STR_PAD_LEFT) . (in_array($contentLineNumber, $blockContents['highlights']) ? '❇️' : '⫶') . $contentLine; + $prefixedBlockContentLines[] = str_pad($contentLineNumber, 3, '0', STR_PAD_LEFT) . (in_array($contentLineNumber, $blockContents['highlights']) ? '❇️' : '⫶') . $contentLine; } echo implode(PHP_EOL, $prefixedBlockContentLines) . PHP_EOL . PHP_EOL; } catch (Exception $exception) { @@ -55,9 +55,9 @@ function displayBlocks(array $docFileBlocks, ?string $docFilePath = null, $lineO */ function getIncludingFileList(?string $codeSampleFilePath = null): array { - $pattern = null === $codeSampleFilePath ? '= include_file' : $codeSampleFilePath; + $pattern = null === $codeSampleFilePath ? '= (include_file|include_code)' : $codeSampleFilePath; $pattern = escapeshellarg($pattern); - $command = "grep $pattern -Rl docs | sort"; + $command = "grep -E $pattern -Rl docs | sort"; exec($command, $rawIncludingFileList, $commandResultCode); if (0 === $commandResultCode) { return $rawIncludingFileList; @@ -73,7 +73,7 @@ function getIncludingFileList(?string $codeSampleFilePath = null): array */ function getInclusionBlocks(string $docFilePath, ?string $codeSampleFilePath = null): array { - $pattern = null === $codeSampleFilePath ? '= include_file' : $codeSampleFilePath; + $pattern = null === $codeSampleFilePath ? '@= (include_file|include_code)@' : "@$codeSampleFilePath@"; $docFileLines = file($docFilePath, FILE_IGNORE_NEW_LINES); if (!$docFileLines) { @@ -87,7 +87,7 @@ function getInclusionBlocks(string $docFilePath, ?string $codeSampleFilePath = n if ($includingFileLineIndex <= $blockEndingLineIndex + 1) { continue; } - if (str_contains($includingFileLine, $pattern)) { + if (preg_match($pattern, $includingFileLine)) { for ($blockStartingLineIndex = $includingFileLineIndex - 1; 0 <= $blockStartingLineIndex; $blockStartingLineIndex--) { $previousLine = $docFileLines[$blockStartingLineIndex]; if (str_contains($previousLine, '```')) { @@ -147,8 +147,8 @@ function getBlockContents(array $block): array $blockHighlightedLines[] = (int)$rawHighlightedLine; } } - } elseif (str_contains($blockSourceLine, '[[= include_file')) { - preg_match_all("@\[\[= include_file\('(?[^']+)'(, (?[0-9]+)(, (?([0-9]+|None))(, '(?[^']+)')?)?)?\) =\]\]@", $blockSourceLine, $matches); + } elseif (str_contains($blockSourceLine, '[[= include_file') || str_contains($blockSourceLine, '[[= include_code')) { + preg_match_all("@\[\[= (?include_file|include_code)\('(?[^']+)'(, (?[^,\)]+)(, (?([^,\)]+))(, (?[^,\)]+)(, (?[^,\)]+))?)?)?)?\) =\]\]@", $blockSourceLine, $matches); $solvedLine = $blockSourceLine; if (empty($matches['file'])) { throw new RuntimeException("The following line doesn't include file correctly: $blockSourceLine"); @@ -161,15 +161,55 @@ function getBlockContents(array $block): array throw new RuntimeException("The following included file can't be opened: $includedFilePath"); } } + + // Named argument mapping + foreach(['start', 'end', 'indent_level', 'remove_indent'] as $key) { + if (str_starts_with($matches[$key][$matchIndex], 'glue=')) { + $matches['indent_level'][$matchIndex] = str_replace('glue=', '', $matches[$key][$matchIndex]); + $matches[$key][$matchIndex] = ''; + } + elseif (str_starts_with($matches[$key][$matchIndex], 'indent_level=')) { + $matches['indent_level'][$matchIndex] = str_replace('indent_level=', '', $matches[$key][$matchIndex]); + $matches[$key][$matchIndex] = ''; + } + elseif (str_starts_with($matches[$key][$matchIndex], 'remove_indent=')) { + $matches['remove_indent'][$matchIndex] = str_replace('remove_indent=', '', $matches[$key][$matchIndex]); + $matches[$key][$matchIndex] = ''; + } + } + if ('None' === $matches['end'][$matchIndex]) { $matches['end'][$matchIndex] = count($includedFilesLines[$includedFilePath]); } if ('' === $matches['start'][$matchIndex]) { $sample = $includedFilesLines[$includedFilePath]; } else { + if ('include_code' === $matches['function'][$matchIndex]) { + $matches['start'][$matchIndex] = (int)$matches['start'][$matchIndex] -1; + } $sample = array_slice($includedFilesLines[$includedFilePath], (int)$matches['start'][$matchIndex], (int)$matches['end'][$matchIndex] - (int)$matches['start'][$matchIndex]); } - $solvedLine = str_replace($matchString, implode(PHP_EOL . $matches['glue'][$matchIndex], $sample) . PHP_EOL, $solvedLine); + if (!empty($matches['indent_level'][$matchIndex])) { + if ('include_code' === $matches['function'][$matchIndex]) { + $matches['indent_level'][$matchIndex] = str_repeat(' ', $matches['indent_level'][$matchIndex]); + } elseif ('include_file' === $matches['function'][$matchIndex]) { + $matches['indent_level'][$matchIndex] = trim($matches['indent_level'][$matchIndex], '"\''); + } + } + if ('True' === $matches['remove_indent'][$matchIndex]) { + $removable = null; + foreach ($sample as $line) { + if ('' !== $line) { + $removable = min($removable ?? strlen($line), strlen($line) - strlen(ltrim($line))); + } + } + if ($removable) { + foreach ($sample as $n => $line) { + $sample[$n] = substr($line, $removable); + } + } + } + $solvedLine = str_replace($matchString, implode(PHP_EOL . $matches['indent_level'][$matchIndex], $sample) . ('include_code' === $matches['function'][$matchIndex] ? '' : PHP_EOL), $solvedLine); } $rawBlockCodeLines = array_merge($rawBlockCodeLines, explode(PHP_EOL, $solvedLine)); } elseif (str_contains($blockSourceLine, '--8<--')) {