aboutsummaryrefslogtreecommitdiff
path: root/src/fpm_backend_console.f90
blob: 44220376ee7244b8354ff6d323e10cbadca1587b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
!># Build Backend Console
!> This module provides a lightweight implementation for printing to the console
!> and updating previously-printed console lines. It used by `[[fpm_backend_output]]`
!> for pretty-printing build status and progress.
!>
!> @note The implementation for updating previous lines relies on no other output
!> going to `stdout`/`stderr` except through the `console_t` object provided.
!>
!> @note All write statements to `stdout` are enclosed within OpenMP `critical` regions
!>
module fpm_backend_console
use iso_fortran_env, only: stdout=>output_unit
implicit none

private
public :: console_t

character(len=*), parameter :: ESC = char(27)

!> Console object
type console_t
    !> Number of lines printed
    integer :: n_line = 1
    !> 'Plain' output (no escape codes)
    logical :: plain_mode = .false.
    !> Escape code for erasing current line
    character(:), allocatable :: LINE_RESET
    !> Escape code for moving up one line
    character(:), allocatable :: LINE_UP
    !> Escape code for moving down one line
    character(:), allocatable :: LINE_DOWN
contains
    !> Initialise the console object
    procedure :: init => console_init
    !> Write a single line to the console
    procedure :: write_line => console_write_line
    !> Update a previously-written console line
    procedure :: update_line => console_update_line
end type console_t

contains

!> Initialise the console object
subroutine console_init(console,plain_mode)
    !> Console object to initialise
    class(console_t), intent(out), target :: console
    !> 'Plain' output (no escape codes)
    logical, intent(in), optional :: plain_mode

    if (present(plain_mode)) then
        console%plain_mode = plain_mode
    end if

    if (console%plain_mode) then
        console%LINE_RESET = ""
        console%LINE_UP = ""
        console%LINE_DOWN = ""
    else
        console%LINE_RESET = ESC//"[2K"//ESC//"[1G"
        console%LINE_UP = ESC//"[1A"
        console%LINE_DOWN = ESC//"[1B"
    end if

end subroutine console_init

!> Write a single line to the standard output
subroutine console_write_line(console,str,line,advance)
    !> Console object
    class(console_t), intent(inout), target :: console
    !> String to write
    character(*), intent(in) :: str
    !> Integer needed to later update console line
    integer, intent(out), optional :: line
    !> Advancing output (print newline?)
    logical, intent(in), optional :: advance

    character(3) :: adv

    adv = "yes"
    if (present(advance)) then
        if (.not.advance) then
            adv = "no"
        end if
    end if

    !$omp critical

    if (present(line)) then
        line = console%n_line
    end if
    
    write(stdout,'(A)',advance=trim(adv)) console%LINE_RESET//str

    if (adv=="yes") then
        console%n_line = console%n_line + 1
    end if

    !$omp end critical

end subroutine console_write_line

!> Overwrite a previously-written line in standard output
subroutine console_update_line(console,line_no,str)
    !> Console object
    class(console_t), intent(in) :: console
    !> Integer output from `[[console_write_line]]`
    integer, intent(in) :: line_no
    !> New string to overwrite line
    character(*), intent(in) :: str

    integer :: n

    !$omp critical

    n = console%n_line - line_no !+ 1 !+ 1

    ! Step back to line
    write(stdout,'(A)',advance="no") repeat(console%LINE_UP,n)//console%LINE_RESET

    write(stdout,*) str

    ! Step forward to end
    write(stdout,'(A)',advance="no") repeat(console%LINE_DOWN,n)//console%LINE_RESET

    !$omp end critical

end subroutine console_update_line

end module fpm_backend_console