diff --git a/inline_documentation.module b/inline_documentation.module
index 4712be5..0121f02 100644
--- a/inline_documentation.module
+++ b/inline_documentation.module
@@ -4,7 +4,8 @@
  * @file
  * Contains Drupal\inline_documentation\inline_documentation.module.
  */
-
+use Drupal\Core\Hook\Attribute\LegacyHook;
+use Drupal\inline_documentation\Hook\InlineDocumentationHooks;
 use Drupal\Core\Form\FormState;
 use Drupal\Core\Url;
 use Drupal\node\Entity\Node;
@@ -18,97 +19,9 @@ use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
  *
  * Perform some alterations on the inline_documentation node forms.
  */
+#[LegacyHook]
 function inline_documentation_form_node_form_alter(&$form, FormState $form_state, $form_id) {
-  switch ($form_id) {
-    case 'node_inline_documentation_form':
-    case 'node_inline_documentation_edit_form':
-      // Create a visibility group in the right-hand column.
-      $form['visibility'] = [
-        '#type' => 'details',
-        '#title' => t('Visibility'),
-        '#group' => 'advanced',
-        '#attributes' => [
-          'class' => ['node-form-options'],
-        ],
-        '#attached' => [
-          'library' => ['node/drupal.node'],
-        ],
-        '#weight' => -10,
-        '#optional' => FALSE,
-        '#open' => TRUE,
-      ];
-
-      // Move the visibility settings to the new group.
-      $form['field_inline_doc_page_element']['#group'] = 'visibility';
-      $form['field_inline_doc_color']['#group'] = 'visibility';
-      $form['field_inline_doc_pages']['#group'] = 'visibility';
-      $form['field_inline_doc_weight']['#group'] = 'visibility';
-      break;
-
-    case 'node_inline_documentation_inline_documentation_form':
-      // Create a button for activating the element selector.
-      $select_button = [
-        'select_element' => [
-          '#type' => 'button',
-          '#value' => t('Select element'),
-          '#attributes' => [
-            'class' => ['inline-documentation-select-element'],
-          ],
-        ],
-      ];
-
-      // Add the button as first widget to the field_inline_doc_page_element
-      // field.
-      array_unshift($form['field_inline_doc_page_element']['widget'], $select_button);
-
-      // Change the field type of the field_inline_doc_color field to 'color'.
-      $form['field_inline_doc_color']['widget'][0]['value']['#type'] = 'color';
-
-      // Get the node object.
-      $form_object = $form_state->getFormObject();
-      $node = $form_object->getEntity();
-
-      // Set the current url as default value in the field_inline_doc_pages
-      // field in new nodes.
-      if ($node->isNew()) {
-        $form['field_inline_doc_pages']['widget'][0]['value']['#default_value'] = Url::fromRoute('<current>');
-      }
-
-      // Add a custom submit handler for our redirect.
-      $form['actions']['submit']['#submit'][] = 'inline_documentation_node_edit_form_submit';
-
-      // Change the advanced group to the accordion type.
-      $form['advanced']['#type'] = 'accordion';
-
-      // Close the revision information group by default.
-      $form['revision_information']['#open'] = FALSE;
-
-      // Move the meta group to the bottom of the form.
-      $form['meta']['#weight'] = 21;
-
-      // Create a new visibility group.
-      $form['visibility'] = [
-        '#type' => 'details',
-        '#title' => t('Visibility'),
-        '#group' => 'advanced',
-        '#attributes' => [
-          'class' => ['node-form-options'],
-        ],
-        '#attached' => [
-          'library' => ['node/drupal.node'],
-        ],
-        '#weight' => 10,
-        '#optional' => FALSE,
-        '#open' => FALSE,
-      ];
-
-      // Add these fields to the visibility group.
-      $form['field_inline_doc_pages']['#group'] = 'visibility';
-      $form['field_inline_doc_weight']['#group'] = 'visibility';
-
-      // Breaky breaky.
-      break;
-  }
+  \Drupal::service(InlineDocumentationHooks::class)->formNodeFormAlter($form, $form_state, $form_id);
 }
 
 /**
@@ -122,78 +35,10 @@ function inline_documentation_node_edit_form_submit($form, FormState &$form_stat
 /**
  * Implements hook_page_bottom().
  */
-function inline_documentation_page_bottom(array &$page_bottom) {
-  // Get the current user.
-  $user = \Drupal::currentUser();
-
-  // Stop if the user hasn't got the permission to view inline documentation.
-  if (!$user->hasPermission('view inline documentation overview')) {
-    return;
-  }
-
-  $page_whitelist = \Drupal::config('inline_documentation.settings')->get('inline_documentation_page_whitelist');
-  $page_blacklist = \Drupal::config('inline_documentation.settings')->get('inline_documentation_page_blacklist');
-  $show_inline_documentation_overview = FALSE;
-
-  $pages = inline_documentation_get_current_page();
-  if (!empty($pages)) {
-    foreach ($pages as $page) {
-      if (empty($page_whitelist) || \Drupal::service('path.matcher')->matchPath($page, $page_whitelist)) {
-        $show_inline_documentation_overview = TRUE;
-      }
-      if (!empty($page_blacklist) && \Drupal::service('path.matcher')->matchPath($page, $page_blacklist)) {
-        $show_inline_documentation_overview = FALSE;
-        break;
-      }
-    }
-  }
-
-  if (!$show_inline_documentation_overview) {
-    return;
-  }
-
-  // This array will contain the inline documentation nodes in a renderable
-  // array.
-  $renderable_nodes = [];
-
-  // Load inline documentation for the current page.
-  $nodes = inline_documentation_load_by_page();
-
-  // Check if there's documentation.
-  if (!empty($nodes)) {
-    foreach ($nodes as $node) {
-      // Only show unpublished nodes to its owner or anyone with the
-      // administer nodes permission.
-      if (
-        !$node->isPublished() &&
-        $node->getOwnerId() != $user->id() &&
-        !$user->hasPermission('administer nodes')
-      ) {
-        continue;
-      }
-
-      // Load the inline_documentation view display.
-      $view_builder = \Drupal::entityTypeManager()->getViewBuilder('node');
-      $renderable_nodes[] = $view_builder->view($node, 'inline_documentation', $node->language()->getId());
-    }
-  }
-
-  $form = inline_documentation_get_node_form();
-
-  // Don't show anything if no nodes and no edit form are found.
-  if (empty($renderable_nodes) && empty($form)) {
-    return;
-  }
-
-  // Add the inline documentation to the page.
-  $page_bottom['inline_documentation'] = [
-    '#theme' => 'inline_documentation_overview',
-    '#nodes' => $renderable_nodes,
-    '#form' => $form,
-    '#attached' => [
-      'library' => 'inline_documentation/inline_documentation',
-    ],
-  ];
+#[LegacyHook]
+function inline_documentation_page_bottom(array &$page_bottom)
+{
+    \Drupal::service(InlineDocumentationHooks::class)->pageBottom($page_bottom);
 }
 
 /**
@@ -298,38 +143,17 @@ function inline_documentation_form_error($message) {
 /**
  * Implements hook_entity_type_build().
  */
+#[LegacyHook]
 function inline_documentation_entity_type_build(array &$entity_types) {
-  // Add our custom form display mode to the classes so the display can be
-  // called programmatically.
-  $entity_types['node']->setFormClass('inline_documentation', 'Drupal\node\NodeForm');
+  \Drupal::service(InlineDocumentationHooks::class)->entityTypeBuild($entity_types);
 }
 
 /**
  * Implements hook_preprocess_node().
  */
+#[LegacyHook]
 function inline_documentation_preprocess_node(&$variables) {
-  // Check for the inline_documentation node type.
-  if ($variables['node']->getType() == 'inline_documentation' && $variables['view_mode'] == 'inline_documentation') {
-    // Get the value of field_inline_doc_page_element.
-    $field_inline_doc_page_element = $variables['node']->get('field_inline_doc_page_element')->getValue();
-    // Add the value as a data attribute.
-    if (!empty($field_inline_doc_page_element[0]['value'])) {
-      $variables['attributes']['data-field-inline-doc-page-element'] = $field_inline_doc_page_element[0]['value'];
-    }
-
-    // Get the value of field_inline_doc_color.
-    $field_inline_doc_color = $variables['node']->get('field_inline_doc_color')->getValue();
-    // Add the value as a data attribute.
-    if (!empty($field_inline_doc_color[0]['value'])) {
-      $variables['attributes']['data-field-inline-doc-color'] = $field_inline_doc_color[0]['value'];
-      $variables['attributes']['style'] = 'border-color: ' . $field_inline_doc_color[0]['value'];
-    }
-
-    // Add extra class if the node is not published.
-    if (!$variables['node']->isPublished()) {
-      $variables['attributes']['class'][] = 'unpublished';
-    }
-  }
+  \Drupal::service(InlineDocumentationHooks::class)->preprocessNode($variables);
 }
 
 /**
@@ -481,49 +305,17 @@ function inline_documentation_placeholderize_path($path) {
  *
  * Force a newline after the field_inline_doc_pages value.
  */
+#[LegacyHook]
 function inline_documentation_entity_presave(EntityInterface $entity) {
-  // Only do this on inline_documentation nodes.
-  if ($entity->bundle() == 'inline_documentation') {
-    // Explode the pages on newlines.
-    $pages = explode("\r\n", $entity->field_inline_doc_pages->value);
-
-    // Create new string for the sanitized pages.
-    $new_pages = '';
-
-    // Check if there are pages.
-    if (!empty($pages)) {
-      // Iterate through them.
-      foreach ($pages as $page) {
-        // Remove surrounding whitespaces.
-        $page = trim($page);
-        // Add it to the new var if it's not empty.
-        if (!empty($page)) {
-          $new_pages .= $page . "\r\n";
-        }
-      }
-    }
-
-    // Save the new value.
-    $entity->field_inline_doc_pages->value = $new_pages;
-  }
+  \Drupal::service(InlineDocumentationHooks::class)->entityPresave($entity);
 }
 
 /**
  * Implements hook_theme().
  */
+#[LegacyHook]
 function inline_documentation_theme($existing, $type, $theme, $path) {
-  return [
-    'inline_documentation_overview' => [
-      'variables' => [
-        'nodes' => NULL,
-        'form' => NULL,
-      ],
-    ],
-    'node__inline_documentation__inline_documentation' => [
-      'template' => 'node--inline-documentation--inline-documentation',
-      'base hook' => 'node',
-    ],
-  ];
+  return \Drupal::service(InlineDocumentationHooks::class)->theme($existing, $type, $theme, $path);
 }
 
 /**
@@ -532,31 +324,10 @@ function inline_documentation_theme($existing, $type, $theme, $path) {
  * Add metadata to the contextual links so the node edit url can be changed in
  * hook_contextual_view_alter().
  */
-function inline_documentation_node_view_alter(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display) {
-  $bundle = $entity->bundle();
-
-  // Check if the node is of the type inline_documentation and the view mode
-  // also is inline_documentation. Also check if the node has contextual links.
-  if ($bundle == 'inline_documentation' && $build['#view_mode'] == 'inline_documentation' && isset($build['#contextual_links']['node'])) {
-    // Put the node type and view mode in the contextual metadata.
-    $build['#contextual_links']['node']['metadata']['bundle'] = $bundle;
-    $build['#contextual_links']['node']['metadata']['view_mode'] = $build['#view_mode'];
-
-    // Get the current url + query as shown in the browser.
-    $edit_url = \Drupal::request()->getRequestUri();
-    // Add a ? or & to the url.
-    if (strstr($edit_url, '?')) {
-      $edit_url .= '&';
-    }
-    else {
-      $edit_url .= '?';
-    }
-    // Add the inline doc to edit to the url.
-    $edit_url .= 'edit-inline-doc=' . $entity->id();
-
-    // Add the url to the metadata so it can be read by the alter hook.
-    $build['#contextual_links']['node']['metadata']['edit_url'] = $edit_url;
-  }
+#[LegacyHook]
+function inline_documentation_node_view_alter(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display)
+{
+    \Drupal::service(InlineDocumentationHooks::class)->nodeViewAlter($build, $entity, $display);
 }
 
 /**
@@ -565,44 +336,16 @@ function inline_documentation_node_view_alter(array &$build, EntityInterface $en
  * Set a new node edit url in the contextual links for inline documentation
  * nodes in the overview.
  */
+#[LegacyHook]
 function inline_documentation_contextual_links_view_alter(&$element, $items) {
-  // Check if the node bundle and view mode are set in the metadata and check
-  // if they contain 'inline_documentation'. Also check if the edit url is set.
-  if (
-    isset($element['#contextual_links']['node']['metadata']['bundle']) &&
-    $element['#contextual_links']['node']['metadata']['bundle'] == 'inline_documentation' &&
-    isset($element['#contextual_links']['node']['metadata']['view_mode']) &&
-    $element['#contextual_links']['node']['metadata']['view_mode'] == 'inline_documentation' &&
-    isset($element['#contextual_links']['node']['metadata']['edit_url'])
-  ) {
-    // Get the node edit url.
-    $edit_url = $element['#contextual_links']['node']['metadata']['edit_url'];
-
-    // Overwrite the node edit url with ours.
-    if (!empty($element['#links']['entitynodeedit-form']['url'])) {
-      $element['#links']['entitynodeedit-form']['url'] = Url::fromUserInput($edit_url);
-    }
-  }
+  \Drupal::service(InlineDocumentationHooks::class)->contextualLinksViewAlter($element, $items);
 }
 
 /**
  * Implements hook_help().
  */
-function inline_documentation_help($route_name, RouteMatchInterface $route_match) {
-  switch ($route_name) {
-    case 'help.page.inline_documentation':
-      $text = file_get_contents(__DIR__ . '/README.md');
-      if (!\Drupal::moduleHandler()->moduleExists('markdown')) {
-        return '<pre>' . Html::escape($text) . '</pre>';
-      }
-      else {
-        // Use the Markdown filter to render the README.
-        $filter_manager = \Drupal::service('plugin.manager.filter');
-        $settings = \Drupal::configFactory()->get('markdown.settings')->getRawData();
-        $config = ['settings' => $settings];
-        $filter = $filter_manager->createInstance('markdown', $config);
-        return $filter->process($text, 'en');
-      }
-  }
-  return NULL;
+#[LegacyHook]
+function inline_documentation_help($route_name, RouteMatchInterface $route_match)
+{
+    return \Drupal::service(InlineDocumentationHooks::class)->help($route_name, $route_match);
 }
diff --git a/inline_documentation.services.yml b/inline_documentation.services.yml
new file mode 100644
index 0000000..0b64c4a
--- /dev/null
+++ b/inline_documentation.services.yml
@@ -0,0 +1,5 @@
+
+services:
+  Drupal\inline_documentation\Hook\InlineDocumentationHooks:
+    class: Drupal\inline_documentation\Hook\InlineDocumentationHooks
+    autowire: true
diff --git a/src/Hook/InlineDocumentationHooks.php b/src/Hook/InlineDocumentationHooks.php
new file mode 100644
index 0000000..9370893
--- /dev/null
+++ b/src/Hook/InlineDocumentationHooks.php
@@ -0,0 +1,340 @@
+<?php
+
+namespace Drupal\inline_documentation\Hook;
+
+use Drupal\Core\Form\FormState;
+use Drupal\Core\Url;
+use Drupal\node\Entity\Node;
+use Drupal\Component\Utility\Html;
+use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
+use Drupal\Core\Hook\Attribute\Hook;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+/**
+ * Hook implementations for inline_documentation.
+ */
+class InlineDocumentationHooks
+{
+    use StringTranslationTrait;
+    /**
+     * Implements hook_form_FORM_ID_alter().
+     *
+     * Perform some alterations on the inline_documentation node forms.
+     */
+    #[Hook('form_node_form_alter')]
+    public function formNodeFormAlter(&$form, \Drupal\Core\Form\FormState $form_state, $form_id)
+    {
+        switch ($form_id) {
+            case 'node_inline_documentation_form':
+            case 'node_inline_documentation_edit_form':
+                // Create a visibility group in the right-hand column.
+                $form['visibility'] = [
+                    '#type' => 'details',
+                    '#title' => $this->t('Visibility'),
+                    '#group' => 'advanced',
+                    '#attributes' => [
+                        'class' => [
+                            'node-form-options',
+                        ],
+                    ],
+                    '#attached' => [
+                        'library' => [
+                            'node/drupal.node',
+                        ],
+                    ],
+                    '#weight' => -10,
+                    '#optional' => FALSE,
+                    '#open' => TRUE,
+                ];
+                // Move the visibility settings to the new group.
+                $form['field_inline_doc_page_element']['#group'] = 'visibility';
+                $form['field_inline_doc_color']['#group'] = 'visibility';
+                $form['field_inline_doc_pages']['#group'] = 'visibility';
+                $form['field_inline_doc_weight']['#group'] = 'visibility';
+                break;
+            case 'node_inline_documentation_inline_documentation_form':
+                // Create a button for activating the element selector.
+                $select_button = [
+                    'select_element' => [
+                        '#type' => 'button',
+                        '#value' => $this->t('Select element'),
+                        '#attributes' => [
+                            'class' => [
+                                'inline-documentation-select-element',
+                            ],
+                        ],
+                    ],
+                ];
+                // Add the button as first widget to the field_inline_doc_page_element
+                // field.
+                array_unshift($form['field_inline_doc_page_element']['widget'], $select_button);
+                // Change the field type of the field_inline_doc_color field to 'color'.
+                $form['field_inline_doc_color']['widget'][0]['value']['#type'] = 'color';
+                // Get the node object.
+                $form_object = $form_state->getFormObject();
+                $node = $form_object->getEntity();
+                // Set the current url as default value in the field_inline_doc_pages
+                // field in new nodes.
+                if ($node->isNew()) {
+                    $form['field_inline_doc_pages']['widget'][0]['value']['#default_value'] = \Drupal\Core\Url::fromRoute('<current>');
+                }
+                // Add a custom submit handler for our redirect.
+                $form['actions']['submit']['#submit'][] = 'inline_documentation_node_edit_form_submit';
+                // Change the advanced group to the accordion type.
+                $form['advanced']['#type'] = 'accordion';
+                // Close the revision information group by default.
+                $form['revision_information']['#open'] = FALSE;
+                // Move the meta group to the bottom of the form.
+                $form['meta']['#weight'] = 21;
+                // Create a new visibility group.
+                $form['visibility'] = [
+                    '#type' => 'details',
+                    '#title' => $this->t('Visibility'),
+                    '#group' => 'advanced',
+                    '#attributes' => [
+                        'class' => [
+                            'node-form-options',
+                        ],
+                    ],
+                    '#attached' => [
+                        'library' => [
+                            'node/drupal.node',
+                        ],
+                    ],
+                    '#weight' => 10,
+                    '#optional' => FALSE,
+                    '#open' => FALSE,
+                ];
+                // Add these fields to the visibility group.
+                $form['field_inline_doc_pages']['#group'] = 'visibility';
+                $form['field_inline_doc_weight']['#group'] = 'visibility';
+                // Breaky breaky.
+                break;
+        }
+    }
+    /**
+     * Implements hook_page_bottom().
+     */
+    #[Hook('page_bottom')]
+    public static function pageBottom(array &$page_bottom)
+    {
+        // Get the current user.
+        $user = \Drupal::currentUser();
+        // Stop if the user hasn't got the permission to view inline documentation.
+        if (!$user->hasPermission('view inline documentation overview')) {
+            return;
+        }
+        $page_whitelist = \Drupal::config('inline_documentation.settings')->get('inline_documentation_page_whitelist');
+        $page_blacklist = \Drupal::config('inline_documentation.settings')->get('inline_documentation_page_blacklist');
+        $show_inline_documentation_overview = FALSE;
+        $pages = inline_documentation_get_current_page();
+        if (!empty($pages)) {
+            foreach ($pages as $page) {
+                if (empty($page_whitelist) || \Drupal::service('path.matcher')->matchPath($page, $page_whitelist)) {
+                    $show_inline_documentation_overview = TRUE;
+                }
+                if (!empty($page_blacklist) && \Drupal::service('path.matcher')->matchPath($page, $page_blacklist)) {
+                    $show_inline_documentation_overview = FALSE;
+                    break;
+                }
+            }
+        }
+        if (!$show_inline_documentation_overview) {
+            return;
+        }
+        // This array will contain the inline documentation nodes in a renderable
+        // array.
+        $renderable_nodes = [
+        ];
+        // Load inline documentation for the current page.
+        $nodes = inline_documentation_load_by_page();
+        // Check if there's documentation.
+        if (!empty($nodes)) {
+            foreach ($nodes as $node) {
+                // Only show unpublished nodes to its owner or anyone with the
+                // administer nodes permission.
+                if (!$node->isPublished() && $node->getOwnerId() != $user->id() && !$user->hasPermission('administer nodes')) {
+                    continue;
+                }
+                // Load the inline_documentation view display.
+                $view_builder = \Drupal::entityTypeManager()->getViewBuilder('node');
+                $renderable_nodes[] = $view_builder->view($node, 'inline_documentation', $node->language()->getId());
+            }
+        }
+        $form = inline_documentation_get_node_form();
+        // Don't show anything if no nodes and no edit form are found.
+        if (empty($renderable_nodes) && empty($form)) {
+            return;
+        }
+        // Add the inline documentation to the page.
+        $page_bottom['inline_documentation'] = [
+            '#theme' => 'inline_documentation_overview',
+            '#nodes' => $renderable_nodes,
+            '#form' => $form,
+            '#attached' => [
+                'library' => 'inline_documentation/inline_documentation',
+            ],
+        ];
+    }
+    /**
+     * Implements hook_entity_type_build().
+     */
+    #[Hook('entity_type_build')]
+    public static function entityTypeBuild(array &$entity_types)
+    {
+        // Add our custom form display mode to the classes so the display can be
+        // called programmatically.
+        $entity_types['node']->setFormClass('inline_documentation', 'Drupal\node\NodeForm');
+    }
+    /**
+     * Implements hook_preprocess_node().
+     */
+    #[Hook('preprocess_node')]
+    public static function preprocessNode(&$variables)
+    {
+        // Check for the inline_documentation node type.
+        if ($variables['node']->getType() == 'inline_documentation' && $variables['view_mode'] == 'inline_documentation') {
+            // Get the value of field_inline_doc_page_element.
+            $field_inline_doc_page_element = $variables['node']->get('field_inline_doc_page_element')->getValue();
+            // Add the value as a data attribute.
+            if (!empty($field_inline_doc_page_element[0]['value'])) {
+                $variables['attributes']['data-field-inline-doc-page-element'] = $field_inline_doc_page_element[0]['value'];
+            }
+            // Get the value of field_inline_doc_color.
+            $field_inline_doc_color = $variables['node']->get('field_inline_doc_color')->getValue();
+            // Add the value as a data attribute.
+            if (!empty($field_inline_doc_color[0]['value'])) {
+                $variables['attributes']['data-field-inline-doc-color'] = $field_inline_doc_color[0]['value'];
+                $variables['attributes']['style'] = 'border-color: ' . $field_inline_doc_color[0]['value'];
+            }
+            // Add extra class if the node is not published.
+            if (!$variables['node']->isPublished()) {
+                $variables['attributes']['class'][] = 'unpublished';
+            }
+        }
+    }
+    /**
+     * Implements hook_entity_presave().
+     *
+     * Force a newline after the field_inline_doc_pages value.
+     */
+    #[Hook('entity_presave')]
+    public static function entityPresave(\Drupal\Core\Entity\EntityInterface $entity)
+    {
+        // Only do this on inline_documentation nodes.
+        if ($entity->bundle() == 'inline_documentation') {
+            // Explode the pages on newlines.
+            $pages = explode("\r\n", $entity->field_inline_doc_pages->value);
+            // Create new string for the sanitized pages.
+            $new_pages = '';
+            // Check if there are pages.
+            if (!empty($pages)) {
+                // Iterate through them.
+                foreach ($pages as $page) {
+                    // Remove surrounding whitespaces.
+                    $page = trim($page);
+                    // Add it to the new var if it's not empty.
+                    if (!empty($page)) {
+                        $new_pages .= $page . "\r\n";
+                    }
+                }
+            }
+            // Save the new value.
+            $entity->field_inline_doc_pages->value = $new_pages;
+        }
+    }
+    /**
+     * Implements hook_theme().
+     */
+    #[Hook('theme')]
+    public static function theme($existing, $type, $theme, $path)
+    {
+        return [
+            'inline_documentation_overview' => [
+                'variables' => [
+                    'nodes' => NULL,
+                    'form' => NULL,
+                ],
+            ],
+            'node__inline_documentation__inline_documentation' => [
+                'template' => 'node--inline-documentation--inline-documentation',
+                'base hook' => 'node',
+            ],
+        ];
+    }
+    /**
+     * Implements hook_node_view_alter().
+     *
+     * Add metadata to the contextual links so the node edit url can be changed in
+     * hook_contextual_view_alter().
+     */
+    #[Hook('node_view_alter')]
+    public static function nodeViewAlter(array &$build, \Drupal\Core\Entity\EntityInterface $entity, \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display)
+    {
+        $bundle = $entity->bundle();
+        // Check if the node is of the type inline_documentation and the view mode
+        // also is inline_documentation. Also check if the node has contextual links.
+        if ($bundle == 'inline_documentation' && $build['#view_mode'] == 'inline_documentation' && isset($build['#contextual_links']['node'])) {
+            // Put the node type and view mode in the contextual metadata.
+            $build['#contextual_links']['node']['metadata']['bundle'] = $bundle;
+            $build['#contextual_links']['node']['metadata']['view_mode'] = $build['#view_mode'];
+            // Get the current url + query as shown in the browser.
+            $edit_url = \Drupal::request()->getRequestUri();
+            // Add a ? or & to the url.
+            if (strstr($edit_url, '?')) {
+                $edit_url .= '&';
+            } else {
+                $edit_url .= '?';
+            }
+            // Add the inline doc to edit to the url.
+            $edit_url .= 'edit-inline-doc=' . $entity->id();
+            // Add the url to the metadata so it can be read by the alter hook.
+            $build['#contextual_links']['node']['metadata']['edit_url'] = $edit_url;
+        }
+    }
+    /**
+     * Implements hook_contextual_links_view_alter().
+     *
+     * Set a new node edit url in the contextual links for inline documentation
+     * nodes in the overview.
+     */
+    #[Hook('contextual_links_view_alter')]
+    public static function contextualLinksViewAlter(&$element, $items)
+    {
+        // Check if the node bundle and view mode are set in the metadata and check
+        // if they contain 'inline_documentation'. Also check if the edit url is set.
+        if (isset($element['#contextual_links']['node']['metadata']['bundle']) && $element['#contextual_links']['node']['metadata']['bundle'] == 'inline_documentation' && isset($element['#contextual_links']['node']['metadata']['view_mode']) && $element['#contextual_links']['node']['metadata']['view_mode'] == 'inline_documentation' && isset($element['#contextual_links']['node']['metadata']['edit_url'])) {
+            // Get the node edit url.
+            $edit_url = $element['#contextual_links']['node']['metadata']['edit_url'];
+            // Overwrite the node edit url with ours.
+            if (!empty($element['#links']['entitynodeedit-form']['url'])) {
+                $element['#links']['entitynodeedit-form']['url'] = \Drupal\Core\Url::fromUserInput($edit_url);
+            }
+        }
+    }
+    /**
+     * Implements hook_help().
+     */
+    #[Hook('help')]
+    public static function help($route_name, \Drupal\Core\Routing\RouteMatchInterface $route_match)
+    {
+        switch ($route_name) {
+            case 'help.page.inline_documentation':
+                $text = file_get_contents(__DIR__ . '/README.md');
+                if (!\Drupal::moduleHandler()->moduleExists('markdown')) {
+                    return '<pre>' . \Drupal\Component\Utility\Html::escape($text) . '</pre>';
+                } else {
+                    // Use the Markdown filter to render the README.
+                    $filter_manager = \Drupal::service('plugin.manager.filter');
+                    $settings = \Drupal::configFactory()->get('markdown.settings')->getRawData();
+                    $config = [
+                        'settings' => $settings,
+                    ];
+                    $filter = $filter_manager->createInstance('markdown', $config);
+                    return $filter->process($text, 'en');
+                }
+        }
+        return NULL;
+    }
+}
