fdoc

filefdoc_docs.f90

Contents

fdoc_docsprivate

Rendering the documentation of every entity from Markdown to HTML.

Source

module fdoc_docs
!! Rendering the documentation of every entity from Markdown to HTML.
use fdoc_util
use fdoc_model
use fdoc_settings, only: settings_t, map_entry_t
use fdoc_project, only: project_t
use fdoc_markdown, only: markdown_to_html, markdown_options_t, dedent
use fdoc_urls, only: link_context_t, resolve_link
use fdoc_progress, only: progress_start, progress_tick, progress_finish
implicit none
private

public :: base_options, render_documentation, render_text, collect_abbreviations

contains

!> The Markdown options of a project.
subroutine base_options(s, root, options)
    type(settings_t), intent(in) :: s                   !! the settings
    character(len=*), intent(in) :: root                !! from the pages to the root of the site
    type(markdown_options_t), intent(out) :: options    !! the options
    integer :: k
    character(len=:), allocatable :: url

    url = root
    options%aliases = [s%alias, s%external, map_entry_t('url', url), map_entry_t('media', url//'/media'), &
                       map_entry_t('page', url//'/page')]
    options%base_path = s%md_base_dir
    options%resolver => resolve_link
    do k = 1, size(s%md_extensions)
        select case (s%md_extensions(k)%s)
        case ('markdown.extensions.toc', 'toc')
            options%toc = .true.
        case ('markdown.extensions.smarty', 'smarty')
            options%smarty = .true.
        end select
    end do
end subroutine base_options

!> Make the abbreviations defined in any documentation apply everywhere, as they do in FORD.
subroutine collect_abbreviations(p, options)
    type(project_t), intent(in) :: p                    !! the project
    type(markdown_options_t), intent(inout) :: options  !! the options
    type(map_entry_t), allocatable :: found(:)
    integer :: k, j, at

    allocate (found(0))
    do k = 1, p%list%n
        if (.not. allocated(p%list%box(k)%e%doc)) cycle
        associate (doc => p%list%box(k)%e%doc)
            do j = 1, size(doc)
                if (.not. starts_with(strip(doc(j)%s), '*[')) cycle
                at = index(doc(j)%s, ']:')
                if (at == 0) cycle
                found = [found, map_entry_t(doc(j)%s(index(doc(j)%s, '*[') + 2:at - 1), strip(doc(j)%s(at + 2:)))]
            end do
        end associate
    end do
    if (size(found) > 0) options%abbreviations = found
end subroutine collect_abbreviations

!> Render `text`, as if it belonged to entity `context`, for a page `prefix` away from the root.
subroutine render_text(p, base, context, prefix, text, html)
    type(project_t), intent(inout), target :: p         !! the project
    type(markdown_options_t), intent(in) :: base        !! the Markdown options
    integer, intent(in) :: context                      !! the entity, or 0
    character(len=*), intent(in) :: prefix              !! from the page to the root of the site
    character(len=*), intent(in) :: text                !! the Markdown
    character(len=:), allocatable, intent(out) :: html  !! the HTML
    type(markdown_options_t) :: options
    type(link_context_t), target :: link

    options = base
    link%list => p%list
    link%entity = context
    link%prefix = prefix
    options%resolver_data => link
    call markdown_to_html(text, options, html)
end subroutine render_text

!> The first paragraph of `html`, or `''`.
pure subroutine first_paragraph(html, out)
    character(len=*), intent(in) :: html                !! the HTML
    character(len=:), allocatable, intent(out) :: out   !! the paragraph
    integer :: a, b

    out = ''
    a = index(html, '<p>')
    if (a == 0) a = index(html, '<p ')
    if (a == 0) return
    b = index(html(a:), '</p>')
    if (b > 0) out = html(a:a + b + 2)
end subroutine first_paragraph

!> Render the documentation and summary of each entity.
subroutine render_documentation(p, s, options)
    type(project_t), intent(inout), target :: p         !! the project
    type(settings_t), intent(in) :: s                   !! the settings
    type(markdown_options_t), intent(in) :: options     !! the Markdown options
    integer :: k

    call progress_start('Converting Markdown', p%list%n)
    !$omp parallel do schedule(dynamic, 16) num_threads(max(1, s%parallel)) default(shared)
    do k = 1, p%list%n
        call render_entity(p, options, k)
        call progress_tick()
    end do
    !$omp end parallel do
    call progress_finish()
end subroutine render_documentation

!> Render the documentation and summary of entity `id`.
subroutine render_entity(p, options, id)
    type(project_t), intent(inout), target :: p         !! the project
    type(markdown_options_t), intent(in) :: options     !! the Markdown options
    integer, intent(in) :: id                           !! the entity
    type(string_t), allocatable :: lines(:)
    integer :: k

    if (.not. allocated(p%list%box(id)%e%doc)) return
    if (p%list%box(id)%e%kind == E_NONE) return
    associate (e => p%list%box(id)%e)
        lines = e%doc
        call dedent(lines)
        call render_text(p, options, id, '../', join(lines, achar(10)), e%html)
        do k = 1, size(e%meta)
            if (e%meta(k)%key /= 'summary') cycle
            call render_text(p, options, id, '../', join(split_words(join(e%meta(k)%values, ' ')), ' '), e%summary)
        end do
        if (.not. allocated(e%summary)) call first_paragraph(e%html, e%summary)
    end associate
end subroutine render_entity

end module fdoc_docs

Graphs

Files this one depends on

Files that depend on this one