Fortran · 20183 bytes Raw Blame History
1 module help_display_module
2 use iso_fortran_env, only: input_unit
3 use terminal_io_module, only: terminal_clear_screen, terminal_hide_cursor, terminal_show_cursor, &
4 terminal_move_cursor, terminal_write
5 use input_handler_module, only: get_key_input
6 use editor_state_module
7 implicit none
8 private
9
10 public :: show_help, show_tags_modal, display_tags_header, show_fuss_hints
11
12 contains
13
14 subroutine show_help(editor)
15 type(editor_state_t), intent(in) :: editor
16 character(len=100), allocatable :: help_lines(:)
17 integer :: n_lines, viewport_start, viewport_size
18 integer :: row, max_rows
19 character(len=32) :: key_input
20 integer :: status
21 logical :: done
22
23 max_rows = editor%screen_rows
24
25 ! Build help content array
26 call build_help_content(help_lines, n_lines)
27
28 ! Initialize viewport
29 viewport_start = 1
30 viewport_size = max_rows - 4 ! Leave room for header and footer
31
32 done = .false.
33 do while (.not. done)
34 ! Clear screen and hide cursor
35 call terminal_clear_screen()
36 call terminal_hide_cursor()
37
38 ! Header
39 call terminal_move_cursor(1, 1)
40 call terminal_write("FACSIMILE HELP - Navigate: ↑↓/jk/PgUp/PgDn, Quit: q/ESC")
41 call terminal_move_cursor(2, 1)
42 call terminal_write(repeat("=", 70))
43
44 ! Display visible portion of help
45 call display_help_viewport(help_lines, n_lines, viewport_start, viewport_size, 3)
46
47 ! Footer with scroll indicator
48 row = max_rows - 1
49 call terminal_move_cursor(row, 1)
50 call terminal_write(repeat("=", 70))
51 row = max_rows
52 call terminal_move_cursor(row, 1)
53 if (n_lines > viewport_size) then
54 write(key_input, '(a,i0,a,i0,a,i0,a,i0,a)') "Lines ", viewport_start, "-", &
55 min(viewport_start + viewport_size - 1, n_lines), " of ", n_lines, &
56 " (", int(real(viewport_start) * 100.0 / real(max(1, n_lines - viewport_size + 1))), "%)"
57 call terminal_write(trim(key_input))
58 else
59 call terminal_write("All content visible")
60 end if
61
62 ! Show cursor
63 call terminal_move_cursor(max_rows, 1)
64 call terminal_show_cursor()
65
66 ! Handle navigation
67 call get_key_input(key_input, status)
68 if (status == 0) then
69 select case(trim(key_input))
70 case('q', 'Q', 'esc', 'ctrl-/', 'ctrl-?')
71 done = .true.
72 case('up', 'k')
73 if (viewport_start > 1) viewport_start = viewport_start - 1
74 case('down', 'j')
75 if (viewport_start + viewport_size - 1 < n_lines) viewport_start = viewport_start + 1
76 case('pageup')
77 viewport_start = max(1, viewport_start - viewport_size)
78 case('pagedown')
79 if (viewport_start + viewport_size - 1 < n_lines) then
80 viewport_start = min(n_lines - viewport_size + 1, viewport_start + viewport_size)
81 end if
82 case('home')
83 viewport_start = 1
84 case('end')
85 if (n_lines > viewport_size) then
86 viewport_start = n_lines - viewport_size + 1
87 end if
88 end select
89 end if
90 end do
91
92 ! Cleanup
93 if (allocated(help_lines)) deallocate(help_lines)
94
95 ! Redraw will happen after returning
96 end subroutine show_help
97
98 subroutine build_help_content(lines, n_lines)
99 character(len=100), allocatable, intent(out) :: lines(:)
100 integer, intent(out) :: n_lines
101 integer :: i
102
103 ! Count total lines needed (sections + items + spacing)
104 n_lines = 0
105 n_lines = n_lines + 11 + 2 ! NAVIGATION
106 n_lines = n_lines + 6 + 2 ! SELECTION
107 n_lines = n_lines + 11 + 2 ! EDITING
108 n_lines = n_lines + 4 + 2 ! CLIPBOARD
109 n_lines = n_lines + 3 + 2 ! LINES
110 n_lines = n_lines + 9 + 2 ! SEARCH & REPLACE
111 n_lines = n_lines + 5 + 2 ! MULTIPLE CURSORS (increased from 4)
112 n_lines = n_lines + 6 + 2 ! SPECIAL
113 n_lines = n_lines + 7 + 2 ! TABS
114 n_lines = n_lines + 7 + 2 ! PANES
115 n_lines = n_lines + 10 + 2 ! GIT
116 n_lines = n_lines + 5 + 2 ! FILE
117 n_lines = n_lines + 7 + 2 ! LSP (added code actions)
118
119 allocate(lines(n_lines))
120 i = 1
121
122 ! NAVIGATION
123 lines(i) = "NAVIGATION"; i = i + 1
124 lines(i) = " arrows move cursor"; i = i + 1
125 lines(i) = " ctrl-a/home smart home (toggle)"; i = i + 1
126 lines(i) = " ctrl-e/end end of line"; i = i + 1
127 lines(i) = " ctrl-home/end file start/end"; i = i + 1
128 lines(i) = " alt-left/right word jump"; i = i + 1
129 lines(i) = " alt-[/alt-] jump to matching bracket"; i = i + 1
130 lines(i) = " pageup/down page scroll"; i = i + 1
131 lines(i) = " ctrl-g go to line:column"; i = i + 1
132 lines(i) = " click position cursor"; i = i + 1
133 lines(i) = " alt-click add/remove cursor"; i = i + 1
134 lines(i) = ""; i = i + 1
135
136 ! SELECTION
137 lines(i) = "SELECTION"; i = i + 1
138 lines(i) = " shift-arrows character selection"; i = i + 1
139 lines(i) = " shift-alt-l/r word selection"; i = i + 1
140 lines(i) = " shift-ctrl-a/e select to line start/end"; i = i + 1
141 lines(i) = " shift-home/end select to line boundaries"; i = i + 1
142 lines(i) = " shift-pageup/down page selection"; i = i + 1
143 lines(i) = " esc clear selection"; i = i + 1
144 lines(i) = ""; i = i + 1
145
146 ! EDITING
147 lines(i) = "EDITING"; i = i + 1
148 lines(i) = " backspace/ctrl-h delete backward"; i = i + 1
149 lines(i) = " delete delete forward"; i = i + 1
150 lines(i) = " tab insert 4 spaces/indent"; i = i + 1
151 lines(i) = " shift-tab dedent selection/line"; i = i + 1
152 lines(i) = " ctrl-k kill line forward"; i = i + 1
153 lines(i) = " ctrl-u kill line backward"; i = i + 1
154 lines(i) = " ctrl-y yank from stack"; i = i + 1
155 lines(i) = " alt-bksp delete word backward"; i = i + 1
156 lines(i) = " alt-d delete word forward"; i = i + 1
157 lines(i) = " ctrl-j join lines"; i = i + 1
158 lines(i) = " ctrl-t transpose characters"; i = i + 1
159 lines(i) = ""; i = i + 1
160
161 ! CLIPBOARD
162 lines(i) = "CLIPBOARD (uses system clipboard)"; i = i + 1
163 lines(i) = " ctrl-x cut line/selection"; i = i + 1
164 lines(i) = " ctrl-c copy line/selection"; i = i + 1
165 lines(i) = " ctrl-v paste"; i = i + 1
166 lines(i) = ""; i = i + 1
167
168 ! LINES
169 lines(i) = "LINES"; i = i + 1
170 lines(i) = " alt-up/down move line"; i = i + 1
171 lines(i) = " alt-shift-up/down duplicate line"; i = i + 1
172 lines(i) = ""; i = i + 1
173
174 ! SEARCH & REPLACE
175 lines(i) = "SEARCH & REPLACE"; i = i + 1
176 lines(i) = " ctrl-f search forward"; i = i + 1
177 lines(i) = " ctrl-r find and replace"; i = i + 1
178 lines(i) = " n next match (after ctrl-f)"; i = i + 1
179 lines(i) = " N previous match (after ctrl-f)"; i = i + 1
180 lines(i) = " ctrl-d select word & find next match"; i = i + 1
181 lines(i) = " esc exit match mode / clear selections"; i = i + 1
182 lines(i) = " alt-c (in search) toggle case sensitive"; i = i + 1
183 lines(i) = " alt-w (in search) toggle whole word match"; i = i + 1
184 lines(i) = ""; i = i + 1
185
186 ! MULTIPLE CURSORS
187 lines(i) = "MULTIPLE CURSORS"; i = i + 1
188 lines(i) = " ctrl-d select word & add cursor at next match"; i = i + 1
189 lines(i) = " alt-click add/remove cursor at position"; i = i + 1
190 lines(i) = " ctrl-alt-up/down add cursor on line above/below"; i = i + 1
191 lines(i) = " esc reduce to single cursor"; i = i + 1
192 lines(i) = ""; i = i + 1
193
194 ! SPECIAL
195 lines(i) = "SPECIAL"; i = i + 1
196 lines(i) = " alt-' cycle quotes"; i = i + 1
197 lines(i) = " alt-shift-' remove brackets/quotes"; i = i + 1
198 lines(i) = " ctrl-z undo"; i = i + 1
199 lines(i) = " ctrl-]/ctrl-shift-z redo"; i = i + 1
200 lines(i) = " ctrl-l clear/redraw screen"; i = i + 1
201 lines(i) = ""; i = i + 1
202
203 ! TABS
204 lines(i) = "TABS"; i = i + 1
205 lines(i) = " ctrl-t new empty tab"; i = i + 1
206 lines(i) = " ctrl-w close current tab"; i = i + 1
207 lines(i) = " ctrl-1 to ctrl-9 jump to tab 1-9 (or alt-1 to alt-9)"; i = i + 1
208 lines(i) = " ctrl-0 jump to tab 10 (or alt-0)"; i = i + 1
209 lines(i) = " ctrl-pageup previous tab (or ctrl-alt-left)"; i = i + 1
210 lines(i) = " ctrl-pagedown next tab (or ctrl-alt-right)"; i = i + 1
211 lines(i) = ""; i = i + 1
212
213 ! PANES
214 lines(i) = "PANES"; i = i + 1
215 lines(i) = " alt-v split pane vertically"; i = i + 1
216 lines(i) = " alt-s split pane horizontally"; i = i + 1
217 lines(i) = " alt-q close current pane only"; i = i + 1
218 lines(i) = " ctrl-w close pane (then tab if last)"; i = i + 1
219 lines(i) = " alt-h/j/k/l navigate left/down/up/right (Vim style)"; i = i + 1
220 lines(i) = " ctrl-shift-arrows navigate between panes (alternative)"; i = i + 1
221 lines(i) = ""; i = i + 1
222
223 ! GIT (in fuss mode)
224 lines(i) = "GIT (in fuss mode - ctrl-b)"; i = i + 1
225 lines(i) = " a stage file/add"; i = i + 1
226 lines(i) = " u unstage file"; i = i + 1
227 lines(i) = " m commit with message"; i = i + 1
228 lines(i) = " p push to remote"; i = i + 1
229 lines(i) = " f fetch from remote"; i = i + 1
230 lines(i) = " l pull from remote"; i = i + 1
231 lines(i) = " t create tag"; i = i + 1
232 lines(i) = " d diff file in new tab"; i = i + 1
233 lines(i) = " enter open file in editor"; i = i + 1
234 lines(i) = ""; i = i + 1
235
236 ! FILE
237 lines(i) = "FILE"; i = i + 1
238 lines(i) = " ctrl-b toggle file browser (fuss mode)"; i = i + 1
239 lines(i) = " ctrl-s save"; i = i + 1
240 lines(i) = " ctrl-q quit"; i = i + 1
241 lines(i) = " ctrl-/ or ctrl-? show this help"; i = i + 1
242 lines(i) = ""; i = i + 1
243
244 ! LSP (Language Server Protocol)
245 lines(i) = "LSP (Language Server Protocol)"; i = i + 1
246 lines(i) = " ctrl-space code completion"; i = i + 1
247 lines(i) = " F12/alt-g go to definition"; i = i + 1
248 lines(i) = " shift-F12/alt-r find all references"; i = i + 1
249 lines(i) = " alt-, (alt-comma) jump back (navigation history)"; i = i + 1
250 lines(i) = " F2 rename symbol"; i = i + 1
251 lines(i) = " F10/alt-. code actions (quick fixes)"; i = i + 1
252 lines(i) = " F4/alt-o document symbols (outline)"; i = i + 1
253 lines(i) = " F6/alt-p workspace symbols (search project)"; i = i + 1
254 lines(i) = " F8/alt-e toggle diagnostics panel (errors)"; i = i + 1
255 lines(i) = " ctrl-p command palette"; i = i + 1
256 lines(i) = ""; i = i + 1
257
258 n_lines = i - 1
259 end subroutine build_help_content
260
261 subroutine display_help_viewport(lines, n_lines, start_line, viewport_size, start_row)
262 character(len=*), intent(in) :: lines(:)
263 integer, intent(in) :: n_lines, start_line, viewport_size, start_row
264 integer :: i, row, end_line
265
266 row = start_row
267 end_line = min(start_line + viewport_size - 1, n_lines)
268
269 do i = start_line, end_line
270 call terminal_move_cursor(row, 1)
271 call terminal_write(lines(i))
272 row = row + 1
273 end do
274 end subroutine display_help_viewport
275
276 subroutine show_tags_modal(editor, tags, n_tags)
277 type(editor_state_t), intent(in) :: editor
278 character(len=256), intent(in) :: tags(:)
279 integer, intent(in) :: n_tags
280 integer :: row, i, max_display
281 character(len=32) :: key_input
282 integer :: status
283
284 ! Clear screen and hide cursor
285 call terminal_clear_screen()
286 call terminal_hide_cursor()
287
288 ! Title
289 row = 1
290 call terminal_move_cursor(row, 1)
291 call terminal_write("EXISTING GIT TAGS - Press any key to continue")
292 row = row + 1
293 call terminal_move_cursor(row, 1)
294 call terminal_write(repeat("=", 60))
295 row = row + 2
296
297 ! Display tags
298 if (n_tags == 0) then
299 call terminal_move_cursor(row, 1)
300 call terminal_write(" (no tags found)")
301 row = row + 2
302 else
303 ! Show up to screen_rows - 6 tags (leave room for header/footer)
304 max_display = min(n_tags, editor%screen_rows - 6)
305 do i = 1, max_display
306 call terminal_move_cursor(row, 3)
307 call terminal_write(trim(tags(i)))
308 row = row + 1
309 end do
310
311 if (n_tags > max_display) then
312 row = row + 1
313 call terminal_move_cursor(row, 3)
314 call terminal_write("... and " // trim(int_to_str(n_tags - max_display)) // " more")
315 row = row + 1
316 end if
317 row = row + 1
318 end if
319
320 ! Footer
321 if (row < editor%screen_rows - 1) then
322 row = editor%screen_rows - 1
323 call terminal_move_cursor(row, 1)
324 call terminal_write(repeat("=", 60))
325 end if
326
327 ! Show cursor at bottom
328 call terminal_move_cursor(editor%screen_rows, 1)
329 call terminal_show_cursor()
330
331 ! Wait for any key press
332 do
333 call get_key_input(key_input, status)
334 if (status == 0) exit ! Got a valid key, exit loop
335 end do
336 end subroutine show_tags_modal
337
338 ! Helper function to convert integer to string
339 function int_to_str(val) result(str)
340 integer, intent(in) :: val
341 character(len=20) :: str
342 write(str, '(I0)') val
343 end function int_to_str
344
345 ! Display fuss mode hints (compact help for ctrl-b mode)
346 subroutine show_fuss_hints(editor)
347 type(editor_state_t), intent(in) :: editor
348 integer :: row
349 character(len=32) :: key_input
350 integer :: status
351
352 ! Clear screen and hide cursor
353 call terminal_clear_screen()
354 call terminal_hide_cursor()
355
356 ! Title
357 row = 1
358 call terminal_move_cursor(row, 1)
359 call terminal_write("FUSS MODE HINTS - Press any key to close")
360 row = row + 1
361 call terminal_move_cursor(row, 1)
362 call terminal_write(repeat("=", 60))
363 row = row + 2
364
365 ! Navigation
366 call terminal_move_cursor(row, 1)
367 call terminal_write("NAVIGATION")
368 row = row + 1
369 call terminal_move_cursor(row, 3)
370 call terminal_write("j/k move to previous/next sibling")
371 row = row + 1
372 call terminal_move_cursor(row, 3)
373 call terminal_write("→/← expand/collapse or enter/exit directory")
374 row = row + 1
375 call terminal_move_cursor(row, 3)
376 call terminal_write("o/enter open file in editor")
377 row = row + 1
378 call terminal_move_cursor(row, 3)
379 call terminal_write("space toggle directory expand/collapse")
380 row = row + 2
381
382 ! Git Operations
383 call terminal_move_cursor(row, 1)
384 call terminal_write("GIT OPERATIONS")
385 row = row + 1
386 call terminal_move_cursor(row, 3)
387 call terminal_write("a stage file")
388 row = row + 1
389 call terminal_move_cursor(row, 3)
390 call terminal_write("u unstage file")
391 row = row + 1
392 call terminal_move_cursor(row, 3)
393 call terminal_write("d diff file in new tab")
394 row = row + 1
395 call terminal_move_cursor(row, 3)
396 call terminal_write("m commit with message")
397 row = row + 1
398 call terminal_move_cursor(row, 3)
399 call terminal_write("p push to remote")
400 row = row + 1
401 call terminal_move_cursor(row, 3)
402 call terminal_write("f fetch from remote")
403 row = row + 1
404 call terminal_move_cursor(row, 3)
405 call terminal_write("l pull from remote")
406 row = row + 1
407 call terminal_move_cursor(row, 3)
408 call terminal_write("t create and push tag")
409 row = row + 2
410
411 ! Exit
412 call terminal_move_cursor(row, 1)
413 call terminal_write("EXIT")
414 row = row + 1
415 call terminal_move_cursor(row, 3)
416 call terminal_write("esc/ctrl-b close fuss mode")
417 row = row + 1
418
419 ! Footer
420 if (row < editor%screen_rows - 1) then
421 row = editor%screen_rows - 1
422 call terminal_move_cursor(row, 1)
423 call terminal_write(repeat("=", 60))
424 end if
425
426 ! Show cursor at bottom
427 call terminal_move_cursor(editor%screen_rows, 1)
428 call terminal_show_cursor()
429
430 ! Wait for any key press
431 do
432 call get_key_input(key_input, status)
433 if (status == 0) exit ! Got a valid key, exit loop
434 end do
435 end subroutine show_fuss_hints
436
437 ! Display tags header without waiting for input (for split view with prompt)
438 subroutine display_tags_header(editor, tags, n_tags)
439 type(editor_state_t), intent(in) :: editor
440 character(len=256), intent(in) :: tags(:)
441 integer, intent(in) :: n_tags
442 integer :: row, i, max_display
443
444 ! Clear screen
445 call terminal_clear_screen()
446 call terminal_hide_cursor()
447
448 ! Title
449 row = 1
450 call terminal_move_cursor(row, 1)
451 call terminal_write("EXISTING GIT TAGS")
452 row = row + 1
453 call terminal_move_cursor(row, 1)
454 call terminal_write(repeat("=", 50))
455 row = row + 1
456
457 ! Display tags (reserve bottom 2 lines for prompt)
458 if (n_tags == 0) then
459 call terminal_move_cursor(row, 1)
460 call terminal_write(" (no tags found)")
461 else
462 ! Show up to screen_rows - 4 tags (leave room for header + prompt area)
463 max_display = min(n_tags, editor%screen_rows - 4)
464 do i = 1, max_display
465 call terminal_move_cursor(row, 3)
466 call terminal_write(trim(tags(i)))
467 row = row + 1
468 end do
469
470 if (n_tags > max_display) then
471 call terminal_move_cursor(row, 3)
472 call terminal_write("... and " // trim(int_to_str(n_tags - max_display)) // " more")
473 end if
474 end if
475
476 ! Separator line before prompt area
477 call terminal_move_cursor(editor%screen_rows - 1, 1)
478 call terminal_write(repeat("-", 50))
479 end subroutine display_tags_header
480
481 end module help_display_module