Fortran · 15784 bytes Raw Blame History
1 module references_panel_module
2 use iso_fortran_env, only: int32
3 use terminal_io_module, only: terminal_move_cursor, terminal_write
4 implicit none
5 private
6
7 public :: references_panel_t, reference_location_t
8 public :: init_references_panel, cleanup_references_panel
9 public :: show_references_panel, hide_references_panel, toggle_references_panel
10 public :: is_references_panel_visible, references_panel_handle_key
11 public :: set_references, clear_references
12 public :: get_selected_reference_location
13 public :: render_references_panel
14
15 ! Reference location
16 type :: reference_location_t
17 character(len=:), allocatable :: uri
18 character(len=:), allocatable :: filename ! Extracted from URI
19 integer(int32) :: line
20 integer(int32) :: column
21 integer(int32) :: end_line
22 integer(int32) :: end_column
23 character(len=:), allocatable :: preview_text ! Line content for preview
24 end type reference_location_t
25
26 ! References panel
27 type :: references_panel_t
28 logical :: visible = .false.
29 integer :: width = 50 ! Panel width in columns
30 integer :: selected_index = 1
31 integer :: scroll_offset = 0
32
33 ! References data
34 type(reference_location_t), allocatable :: references(:)
35 integer :: num_references = 0
36 character(len=:), allocatable :: symbol_name
37
38 ! Screen position
39 integer :: screen_width = 80
40 integer :: screen_height = 24
41 end type references_panel_t
42
43 contains
44
45 subroutine init_references_panel(panel)
46 type(references_panel_t), intent(out) :: panel
47
48 panel%visible = .false.
49 panel%width = 50
50 panel%selected_index = 1
51 panel%scroll_offset = 0
52 panel%num_references = 0
53 panel%screen_width = 80
54 panel%screen_height = 24
55 end subroutine init_references_panel
56
57 subroutine cleanup_references_panel(panel)
58 type(references_panel_t), intent(inout) :: panel
59 integer :: i
60
61 if (allocated(panel%references)) then
62 do i = 1, panel%num_references
63 if (allocated(panel%references(i)%uri)) deallocate(panel%references(i)%uri)
64 if (allocated(panel%references(i)%filename)) deallocate(panel%references(i)%filename)
65 if (allocated(panel%references(i)%preview_text)) deallocate(panel%references(i)%preview_text)
66 end do
67 deallocate(panel%references)
68 end if
69
70 if (allocated(panel%symbol_name)) deallocate(panel%symbol_name)
71
72 panel%num_references = 0
73 panel%selected_index = 1
74 panel%scroll_offset = 0
75 end subroutine cleanup_references_panel
76
77 subroutine set_references(panel, references, num_refs, symbol_name)
78 type(references_panel_t), intent(inout) :: panel
79 type(reference_location_t), intent(in) :: references(:)
80 integer, intent(in) :: num_refs
81 character(len=*), intent(in), optional :: symbol_name
82 integer :: i
83
84 ! Clear existing references
85 call cleanup_references_panel(panel)
86
87 ! Allocate and copy new references
88 if (num_refs > 0) then
89 allocate(panel%references(num_refs))
90 panel%num_references = num_refs
91
92 do i = 1, num_refs
93 if (allocated(references(i)%uri)) then
94 allocate(character(len=len(references(i)%uri)) :: panel%references(i)%uri)
95 panel%references(i)%uri = references(i)%uri
96 end if
97
98 if (allocated(references(i)%filename)) then
99 allocate(character(len=len(references(i)%filename)) :: panel%references(i)%filename)
100 panel%references(i)%filename = references(i)%filename
101 end if
102
103 if (allocated(references(i)%preview_text)) then
104 allocate(character(len=len(references(i)%preview_text)) :: panel%references(i)%preview_text)
105 panel%references(i)%preview_text = references(i)%preview_text
106 end if
107
108 panel%references(i)%line = references(i)%line
109 panel%references(i)%column = references(i)%column
110 panel%references(i)%end_line = references(i)%end_line
111 panel%references(i)%end_column = references(i)%end_column
112 end do
113 end if
114
115 ! Set symbol name
116 if (present(symbol_name)) then
117 if (allocated(panel%symbol_name)) deallocate(panel%symbol_name)
118 allocate(character(len=len_trim(symbol_name)) :: panel%symbol_name)
119 panel%symbol_name = trim(symbol_name)
120 end if
121
122 panel%selected_index = 1
123 panel%scroll_offset = 0
124 end subroutine set_references
125
126 subroutine clear_references(panel)
127 type(references_panel_t), intent(inout) :: panel
128 call cleanup_references_panel(panel)
129 end subroutine clear_references
130
131 subroutine show_references_panel(panel, screen_width, screen_height)
132 type(references_panel_t), intent(inout) :: panel
133 integer, intent(in) :: screen_width, screen_height
134
135 panel%screen_width = screen_width
136 panel%screen_height = screen_height
137 panel%visible = .true.
138 end subroutine show_references_panel
139
140 subroutine hide_references_panel(panel)
141 type(references_panel_t), intent(inout) :: panel
142 panel%visible = .false.
143 end subroutine hide_references_panel
144
145 subroutine toggle_references_panel(panel)
146 type(references_panel_t), intent(inout) :: panel
147 panel%visible = .not. panel%visible
148 end subroutine toggle_references_panel
149
150 function is_references_panel_visible(panel) result(visible)
151 type(references_panel_t), intent(in) :: panel
152 logical :: visible
153 visible = panel%visible
154 end function is_references_panel_visible
155
156 subroutine render_references_panel(panel, start_row)
157 type(references_panel_t), intent(in) :: panel
158 integer, intent(in) :: start_row
159 integer :: row, col, start_col
160 integer :: i, visible_index, max_visible
161 character(len=256) :: line
162 character(len=100) :: header, location_str
163 character(len=:), allocatable :: display_text
164
165 if (.not. panel%visible) return
166
167 ! Calculate panel position (right side of screen)
168 start_col = panel%screen_width - panel%width + 1
169 max_visible = panel%screen_height - start_row - 2 ! Leave room for header/footer
170
171 ! Draw panel border and header
172 row = start_row
173
174 ! Header with symbol name
175 call terminal_move_cursor(row, start_col)
176 call terminal_write(char(27) // '[48;5;237m') ! Dark background
177
178 if (allocated(panel%symbol_name)) then
179 write(header, '(A,A,A,I0,A)') " References: ", trim(panel%symbol_name), &
180 " (", panel%num_references, ") "
181 else
182 write(header, '(A,I0,A)') " References (", panel%num_references, ") "
183 end if
184
185 ! Truncate header if too long
186 if (len_trim(header) > panel%width) then
187 header = header(1:panel%width-3) // "..."
188 end if
189
190 ! Center the header
191 col = start_col + (panel%width - len_trim(header)) / 2
192 call terminal_move_cursor(row, col)
193 call terminal_write(char(27) // '[1m' // trim(header) // char(27) // '[0m')
194
195 ! Clear to end of header line
196 call terminal_move_cursor(row, start_col + len_trim(header))
197 call render_empty_line(panel%width - len_trim(header))
198
199 row = row + 1
200
201 ! Draw separator
202 call terminal_move_cursor(row, start_col)
203 call terminal_write(char(27) // '[48;5;237m' // repeat("─", panel%width) // char(27) // '[0m')
204 row = row + 1
205
206 ! Display references
207 if (panel%num_references == 0) then
208 call terminal_move_cursor(row, start_col)
209 call terminal_write(char(27) // '[48;5;235m' // char(27) // '[90m')
210 call terminal_write(" No references found")
211 call terminal_write(char(27) // '[K') ! Clear to end of line
212 call terminal_write(char(27) // '[0m')
213 else
214 ! Display visible references
215 do i = 1, min(max_visible, panel%num_references - panel%scroll_offset)
216 visible_index = panel%scroll_offset + i
217
218 if (visible_index > panel%num_references) exit
219
220 call terminal_move_cursor(row, start_col)
221
222 ! Highlight selected item
223 if (visible_index == panel%selected_index) then
224 call terminal_write(char(27) // '[48;5;240m') ! Highlight background
225 else
226 call terminal_write(char(27) // '[48;5;235m') ! Normal background
227 end if
228
229 ! Format location string
230 if (allocated(panel%references(visible_index)%filename)) then
231 write(location_str, '(A,A,I0,A,I0)') &
232 trim(get_basename(panel%references(visible_index)%filename)), &
233 ":", panel%references(visible_index)%line, &
234 ":", panel%references(visible_index)%column
235 else
236 write(location_str, '(I0,A,I0)') &
237 panel%references(visible_index)%line, &
238 ":", panel%references(visible_index)%column
239 end if
240
241 ! Truncate location if needed
242 if (len_trim(location_str) > 20) then
243 location_str = location_str(1:17) // "..."
244 end if
245
246 ! Format display line
247 write(line, '(A2,A20,A)') " ", adjustl(location_str), " "
248
249 ! Add preview text if available
250 if (allocated(panel%references(visible_index)%preview_text)) then
251 display_text = trim(panel%references(visible_index)%preview_text)
252 if (len(display_text) > panel%width - 25) then
253 display_text = display_text(1:panel%width-28) // "..."
254 end if
255 line = trim(line) // display_text
256 end if
257
258 ! Ensure line fits in panel width
259 if (len_trim(line) > panel%width) then
260 line = line(1:panel%width)
261 end if
262
263 call terminal_write(line(1:panel%width))
264 call terminal_write(char(27) // '[0m')
265
266 row = row + 1
267 end do
268
269 ! Clear remaining lines
270 do i = row, start_row + max_visible + 1
271 if (i > panel%screen_height - 1) exit
272 call terminal_move_cursor(i, start_col)
273 call render_empty_line(panel%width)
274 end do
275
276 ! Show scroll indicator if needed
277 if (panel%num_references > max_visible) then
278 call terminal_move_cursor(start_row + max_visible + 2, start_col)
279 call terminal_write(char(27) // '[48;5;237m' // char(27) // '[90m')
280 write(line, '(A,I0,A,I0,A)') " [", panel%selected_index, "/", panel%num_references, "] "
281 if (panel%scroll_offset > 0) then
282 line = trim(line) // "↑"
283 end if
284 if (panel%scroll_offset + max_visible < panel%num_references) then
285 line = trim(line) // "↓"
286 end if
287 call terminal_write(trim(line))
288 call terminal_write(char(27) // '[0m')
289 end if
290 end if
291 end subroutine render_references_panel
292
293 subroutine render_empty_line(width)
294 integer, intent(in) :: width
295 call terminal_write(char(27) // '[48;5;235m' // repeat(" ", width) // char(27) // '[0m')
296 end subroutine render_empty_line
297
298 function references_panel_handle_key(panel, key) result(handled)
299 type(references_panel_t), intent(inout) :: panel
300 character(len=*), intent(in) :: key
301 logical :: handled
302 integer :: max_visible
303
304 handled = .false.
305 if (.not. panel%visible) return
306
307 max_visible = panel%screen_height - 4
308
309 select case(trim(key))
310 case('j', 'down')
311 ! Move selection down
312 if (panel%selected_index < panel%num_references) then
313 panel%selected_index = panel%selected_index + 1
314
315 ! Adjust scroll if needed
316 if (panel%selected_index > panel%scroll_offset + max_visible) then
317 panel%scroll_offset = panel%selected_index - max_visible
318 end if
319 handled = .true.
320 end if
321
322 case('k', 'up')
323 ! Move selection up
324 if (panel%selected_index > 1) then
325 panel%selected_index = panel%selected_index - 1
326
327 ! Adjust scroll if needed
328 if (panel%selected_index <= panel%scroll_offset) then
329 panel%scroll_offset = max(0, panel%selected_index - 1)
330 end if
331 handled = .true.
332 end if
333
334 case('pagedown')
335 ! Page down
336 panel%selected_index = min(panel%num_references, &
337 panel%selected_index + max_visible)
338 panel%scroll_offset = min(max(0, panel%num_references - max_visible), &
339 panel%scroll_offset + max_visible)
340 handled = .true.
341
342 case('pageup')
343 ! Page up
344 panel%selected_index = max(1, panel%selected_index - max_visible)
345 panel%scroll_offset = max(0, panel%scroll_offset - max_visible)
346 handled = .true.
347
348 case('home')
349 ! Jump to first
350 panel%selected_index = 1
351 panel%scroll_offset = 0
352 handled = .true.
353
354 case('end')
355 ! Jump to last
356 panel%selected_index = panel%num_references
357 panel%scroll_offset = max(0, panel%num_references - max_visible)
358 handled = .true.
359
360 case('enter')
361 ! User wants to jump to this reference
362 handled = .true.
363
364 case('escape', 'shift-f12')
365 ! Hide panel
366 panel%visible = .false.
367 handled = .true.
368 end select
369 end function references_panel_handle_key
370
371 function get_selected_reference_location(panel, uri, line, col) result(has_location)
372 type(references_panel_t), intent(in) :: panel
373 character(len=:), allocatable, intent(out) :: uri
374 integer(int32), intent(out) :: line, col
375 logical :: has_location
376
377 has_location = .false.
378
379 if (panel%selected_index > 0 .and. panel%selected_index <= panel%num_references) then
380 if (allocated(panel%references(panel%selected_index)%uri)) then
381 allocate(character(len=len(panel%references(panel%selected_index)%uri)) :: uri)
382 uri = panel%references(panel%selected_index)%uri
383 line = panel%references(panel%selected_index)%line
384 col = panel%references(panel%selected_index)%column
385 has_location = .true.
386 end if
387 end if
388 end function get_selected_reference_location
389
390 ! Helper function to extract basename from path
391 function get_basename(path) result(basename)
392 character(len=*), intent(in) :: path
393 character(len=:), allocatable :: basename
394 integer :: last_slash
395
396 last_slash = index(path, '/', back=.true.)
397 if (last_slash > 0) then
398 basename = path(last_slash+1:)
399 else
400 basename = path
401 end if
402 end function get_basename
403
404 end module references_panel_module