Подтвердить что ты не робот

Создание оглавления от Markdown в php

Я хотел бы создать оглавление из Markdown.
Например, в stackedit.io https://stackedit.io/editor#table-of-contents при вставке:

[TOC]

Есть ли способ генерировать это из уценки?

например. если у вас есть:

## header 1
## header 2

ToC должен быть:

<ol>
   <li><a href="#header1">Header 1</a></li>
   <li><a href="#header2">Header 2</a></li>
</ol>

Должен ли я создать собственный анализатор разметки, чтобы получить ToC?

4b9b3361

Ответ 1

Ниже приведена функция, выполняющая основное задание: он возвращает список найденных заголовков JSON, каждый со своим уровнем и текстом.
Этот элемент JSON может также использоваться для генерации необходимой структуры HTML или всего остального.

Схематически он работает следующим образом:

  • Получить файл разметки в виде строки и нормализовать разрывы строк только на \n (это важно для шага № 3 ниже)
  • Примените простое регулярное выражение /^(?:=|-|#).*$/m к PREG_OFFSET_CAPTURE: так соответствует всем строкам, которые:
    • являются "underliners" из <h1> (когда "=" ) или <h2> (когда "-" ) заголовки
    • являются названиями сами по себе (начиная с "#" )
  • Итерировать согласованные строки:
    • для "underliners", посмотрите исходный файл для предыдущей строки, расположенный в виде строки между текущим смещением строки и предыдущей строкой break;, затем получите уровень от типа underliner и текст из предыдущей строки
    • иначе просто получить уровень и текст из текущей строки

Вот функция:

function markdown_toc($file_path) {
  $file = file_get_contents($file_path);

  // ensure using only "\n" as line-break
  $source = str_replace(["\r\n", "\r"], "\n", $file);

  // look for markdown TOC items
  preg_match_all(
    '/^(?:=|-|#).*$/m',
    $source,
    $matches,
    PREG_PATTERN_ORDER | PREG_OFFSET_CAPTURE
  );

  // preprocess: iterate matched lines to create an array of items
  // where each item is an array(level, text)
  $file_size = strlen($source);
  foreach ($matches[0] as $item) {
    $found_mark = substr($item[0], 0, 1);
    if ($found_mark == '#') {
      // text is the found item
      $item_text = $item[0];
      $item_level = strrpos($item_text, '#') + 1;
      $item_text = substr($item_text, $item_level);
    } else {
      // text is the previous line (empty if <hr>)
      $item_offset = $item[1];
      $prev_line_offset = strrpos($source, "\n", -($file_size - $item_offset + 2));
      $item_text =
        substr($source, $prev_line_offset, $item_offset - $prev_line_offset - 1);
      $item_text = trim($item_text);
      $item_level = $found_mark == '=' ? 1 : 2;
    }
    if (!trim($item_text) OR strpos($item_text, '|') !== FALSE) {
      // item is an horizontal separator or a table header, don't mind
      continue;
    }
    $raw_toc[] = ['level' => $item_level, 'text' => trim($item_text)];
  }

  // create a JSON list (the easiest way to generate HTML structure is using JS)
  return json_encode($raw_toc);
}

Вот результат, который он возвращает с домашней страницы указанной вами ссылки:

[
  {"level":1,"text":"Welcome to StackEdit!"},
  {"level":2,"text":"Documents"},
  {"level":4,"text":"<\/i> Create a document"},
  {"level":4,"text":"<\/i> Switch to another document"},
  {"level":4,"text":"<\/i> Rename a document"},
  {"level":4,"text":"<\/i> Delete a document"},
  {"level":4,"text":"<\/i> Export a document"},
  {"level":2,"text":"Synchronization"},
  {"level":4,"text":"<\/i> Open a document"},
  {"level":4,"text":"<\/i> Save a document"},
  {"level":4,"text":"<\/i> Synchronize a document"},
  {"level":4,"text":"<\/i> Manage document synchronization"},
  {"level":2,"text":"Publication"},
  {"level":4,"text":"<\/i> Publish a document"},
  {"level":2,"text":"- Markdown, to publish the Markdown text on a website that can interpret it (**GitHub** for instance),"},
  {"level":2,"text":"- HTML, to publish the document converted into HTML (on a blog for example),"},
  {"level":4,"text":"<\/i> Update a publication"},
  {"level":4,"text":"<\/i> Manage document publication"},
  {"level":2,"text":"Markdown Extra"},
  {"level":3,"text":"Tables"},
  {"level":3,"text":"Definition Lists"},
  {"level":3,"text":"Fenced code blocks"},
  {"level":3,"text":"Footnotes"},
  {"level":3,"text":"SmartyPants"},
  {"level":3,"text":"Table of contents"},
  {"level":3,"text":"MathJax"},
  {"level":3,"text":"UML diagrams"},
  {"level":3,"text":"Support StackEdit"}
]

Ответ 2

Добавление оглавления не является частью стандартного синтаксиса разметки или доступно (пока) во многих более сильных анализаторах уценки.

Однако я добавил автоматическое создание оглавления в моем синтаксисе разметки: https://github.com/PeterWaher/IoTGateway/tree/master/Content/Waher.Content.Markdown

Он работает через подключаемый интерфейс для включения мультимедиа, используя тот же синтаксис, что и при вставке изображений. Мультимедийный модуль выбирается на основе оценки, рассчитанной по предоставленному URL. Это позволяет включать видео, аудио, клипы YouTube и т.д. Он также позволяет вставлять оглавление. Вы просто пишете ![Table of Contents](ToC).