Fortran · 15066 bytes Raw Blame History
1 module lsp_server_installer_panel_module
2 use terminal_io_module
3 use server_detection_module, only: detected_server_t, detect_all_servers, check_server_installed
4 use server_installer_module, only: run_install_command, install_result_t
5 implicit none
6 private
7
8 public :: lsp_server_installer_panel_t
9 public :: init_lsp_server_installer_panel, cleanup_lsp_server_installer_panel
10 public :: show_lsp_server_installer_panel, hide_lsp_server_installer_panel
11 public :: is_lsp_server_installer_panel_visible
12 public :: lsp_server_installer_panel_handle_key
13 public :: render_lsp_server_installer_panel
14 public :: refresh_server_status
15
16 integer, parameter :: PANEL_WIDTH = 62
17 integer, parameter :: MAX_VISIBLE = 8
18
19 type :: lsp_server_installer_panel_t
20 logical :: visible = .false.
21 integer :: selected_index = 1
22 integer :: scroll_offset = 0
23 type(detected_server_t), allocatable :: servers(:)
24 integer :: num_servers = 0
25 logical :: confirm_mode = .false.
26 integer :: confirm_server_index = 0
27 logical :: installing = .false.
28 character(len=256) :: status_message = ''
29 end type lsp_server_installer_panel_t
30
31 contains
32
33 subroutine init_lsp_server_installer_panel(panel)
34 type(lsp_server_installer_panel_t), intent(out) :: panel
35
36 panel%visible = .false.
37 panel%selected_index = 1
38 panel%scroll_offset = 0
39 panel%num_servers = 0
40 panel%confirm_mode = .false.
41 panel%confirm_server_index = 0
42 panel%installing = .false.
43 panel%status_message = ''
44 end subroutine init_lsp_server_installer_panel
45
46 subroutine cleanup_lsp_server_installer_panel(panel)
47 type(lsp_server_installer_panel_t), intent(inout) :: panel
48
49 if (allocated(panel%servers)) deallocate(panel%servers)
50 panel%num_servers = 0
51 end subroutine cleanup_lsp_server_installer_panel
52
53 subroutine show_lsp_server_installer_panel(panel)
54 type(lsp_server_installer_panel_t), intent(inout) :: panel
55
56 panel%visible = .true.
57 panel%selected_index = 1
58 panel%scroll_offset = 0
59 panel%confirm_mode = .false.
60 panel%status_message = ''
61
62 ! Detect servers if not already done
63 if (panel%num_servers == 0) then
64 call refresh_server_status(panel)
65 end if
66 end subroutine show_lsp_server_installer_panel
67
68 subroutine hide_lsp_server_installer_panel(panel)
69 type(lsp_server_installer_panel_t), intent(inout) :: panel
70 panel%visible = .false.
71 panel%confirm_mode = .false.
72 end subroutine hide_lsp_server_installer_panel
73
74 function is_lsp_server_installer_panel_visible(panel) result(visible)
75 type(lsp_server_installer_panel_t), intent(in) :: panel
76 logical :: visible
77 visible = panel%visible
78 end function is_lsp_server_installer_panel_visible
79
80 subroutine refresh_server_status(panel)
81 type(lsp_server_installer_panel_t), intent(inout) :: panel
82
83 if (allocated(panel%servers)) deallocate(panel%servers)
84 call detect_all_servers(panel%servers, panel%num_servers)
85 panel%status_message = 'Server status refreshed'
86 end subroutine refresh_server_status
87
88 function lsp_server_installer_panel_handle_key(panel, key) result(handled)
89 type(lsp_server_installer_panel_t), intent(inout) :: panel
90 character(len=*), intent(in) :: key
91 logical :: handled
92 type(install_result_t) :: result
93
94 handled = .true.
95
96 ! Handle confirm mode separately
97 if (panel%confirm_mode) then
98 select case(trim(key))
99 case('y', 'Y')
100 ! Execute installation
101 panel%installing = .true.
102 panel%status_message = 'Installing ' // trim(panel%servers(panel%confirm_server_index)%name) // '...'
103
104 result = run_install_command(trim(panel%servers(panel%confirm_server_index)%install_cmd))
105
106 panel%installing = .false.
107 if (result%success) then
108 panel%status_message = 'Successfully installed ' // trim(panel%servers(panel%confirm_server_index)%name)
109 ! Refresh to update status
110 call refresh_server_status(panel)
111 else
112 panel%status_message = 'Installation failed. Try manually: ' // &
113 trim(panel%servers(panel%confirm_server_index)%install_cmd)
114 end if
115 panel%confirm_mode = .false.
116
117 case('n', 'N', 'esc', 'escape')
118 panel%confirm_mode = .false.
119 panel%status_message = ''
120
121 case default
122 ! Ignore other keys in confirm mode
123 end select
124 return
125 end if
126
127 ! Normal mode key handling
128 select case(trim(key))
129 case('j', 'down')
130 if (panel%selected_index < panel%num_servers) then
131 panel%selected_index = panel%selected_index + 1
132 ! Scroll if needed
133 if (panel%selected_index > panel%scroll_offset + MAX_VISIBLE) then
134 panel%scroll_offset = panel%selected_index - MAX_VISIBLE
135 end if
136 end if
137
138 case('k', 'up')
139 if (panel%selected_index > 1) then
140 panel%selected_index = panel%selected_index - 1
141 ! Scroll if needed
142 if (panel%selected_index <= panel%scroll_offset) then
143 panel%scroll_offset = panel%selected_index - 1
144 end if
145 end if
146
147 case('enter')
148 ! Only allow install for non-installed servers
149 if (panel%num_servers > 0 .and. panel%selected_index <= panel%num_servers) then
150 if (.not. panel%servers(panel%selected_index)%is_installed) then
151 panel%confirm_mode = .true.
152 panel%confirm_server_index = panel%selected_index
153 else
154 panel%status_message = trim(panel%servers(panel%selected_index)%name) // ' is already installed'
155 end if
156 end if
157
158 case('r', 'R')
159 ! Refresh server status
160 call refresh_server_status(panel)
161
162 case('esc', 'escape', 'q')
163 call hide_lsp_server_installer_panel(panel)
164
165 case default
166 handled = .false.
167 end select
168 end function lsp_server_installer_panel_handle_key
169
170 subroutine render_lsp_server_installer_panel(panel, screen_cols)
171 type(lsp_server_installer_panel_t), intent(in) :: panel
172 integer, intent(in) :: screen_cols
173 integer :: start_col, start_row, row, i, visible_end
174 integer :: content_width, visible_len, status_len, padding
175 character(len=:), allocatable :: border_top, border_mid, border_bottom
176 character(len=128) :: visible_text, status_text
177 character(len=*), parameter :: ESC = char(27)
178 character(len=*), parameter :: GREEN = ESC // '[32m'
179 character(len=*), parameter :: RED = ESC // '[31m'
180 character(len=*), parameter :: CYAN = ESC // '[36m'
181 character(len=*), parameter :: YELLOW = ESC // '[33m'
182 character(len=*), parameter :: DIM = ESC // '[90m'
183 character(len=*), parameter :: INVERSE = ESC // '[7m'
184 character(len=*), parameter :: RESET = ESC // '[0m'
185
186 if (.not. panel%visible) return
187
188 ! Calculate centering
189 content_width = min(PANEL_WIDTH, screen_cols - 4)
190 start_col = max(1, (screen_cols - content_width) / 2)
191 start_row = 2
192
193 ! Build borders
194 border_top = '┌' // repeat('─', content_width - 2) // '┐'
195 border_mid = '├' // repeat('─', content_width - 2) // '┤'
196 border_bottom = '└' // repeat('─', content_width - 2) // '┘'
197
198 ! Render confirm dialog if in confirm mode
199 if (panel%confirm_mode) then
200 call render_confirm_dialog(panel, screen_cols)
201 return
202 end if
203
204 ! Draw top border
205 call terminal_move_cursor(start_row, start_col)
206 call terminal_write(border_top)
207
208 ! Draw header
209 row = start_row + 1
210 call terminal_move_cursor(row, start_col)
211 call terminal_write('│' // CYAN // ' Language Server Manager' // RESET)
212 call terminal_write(repeat(' ', content_width - 31) // DIM // 'Alt+M' // RESET // ' │')
213
214 ! Draw separator
215 row = row + 1
216 call terminal_move_cursor(row, start_col)
217 call terminal_write(border_mid)
218
219 ! Draw server list
220 visible_end = min(panel%scroll_offset + MAX_VISIBLE, panel%num_servers)
221 do i = panel%scroll_offset + 1, visible_end
222 row = row + 1
223 call terminal_move_cursor(row, start_col)
224 call terminal_write('│')
225
226 ! Build line content (visible text only for width calculation)
227 ! Format: " ✓ servername (Language) <spaces> status"
228 visible_text = ' ✓ ' // trim(panel%servers(i)%name) // ' (' // &
229 trim(panel%servers(i)%language) // ')'
230
231 ! Calculate visible length (icon + space + name + space + language + parens)
232 visible_len = len_trim(visible_text)
233
234 ! Add status text length
235 if (panel%servers(i)%is_installed) then
236 status_text = 'installed'
237 status_len = 9
238 else
239 status_text = 'Enter to install'
240 status_len = 16
241 end if
242
243 ! Calculate padding needed (content_width - 2 for borders, minus visible text, minus status)
244 padding = max(1, content_width - 2 - visible_len - status_len)
245
246 ! Highlight selected row
247 if (i == panel%selected_index) then
248 call terminal_write(INVERSE)
249 end if
250
251 ! Write status icon with color
252 if (panel%servers(i)%is_installed) then
253 call terminal_write(' ' // GREEN // '✓' // RESET // ' ')
254 else
255 call terminal_write(' ' // RED // '✗' // RESET // ' ')
256 end if
257
258 ! Write server name and language
259 call terminal_write(trim(panel%servers(i)%name) // ' (' // &
260 trim(panel%servers(i)%language) // ')')
261
262 ! Write padding
263 call terminal_write(repeat(' ', padding))
264
265 ! Write status text with color
266 if (panel%servers(i)%is_installed) then
267 call terminal_write(DIM // trim(status_text) // RESET)
268 else
269 call terminal_write(YELLOW // trim(status_text) // RESET)
270 end if
271
272 ! Reset highlighting
273 if (i == panel%selected_index) then
274 call terminal_write(RESET)
275 end if
276
277 call terminal_write('│')
278 end do
279
280 ! Fill remaining rows if needed
281 do i = visible_end + 1, panel%scroll_offset + MAX_VISIBLE
282 row = row + 1
283 call terminal_move_cursor(row, start_col)
284 call terminal_write('│' // repeat(' ', content_width - 2) // '│')
285 end do
286
287 ! Draw separator before footer
288 row = row + 1
289 call terminal_move_cursor(row, start_col)
290 call terminal_write(border_mid)
291
292 ! Draw status message or help
293 row = row + 1
294 call terminal_move_cursor(row, start_col)
295 if (len_trim(panel%status_message) > 0) then
296 call terminal_write('│ ' // YELLOW // trim(panel%status_message) // RESET)
297 call terminal_write(repeat(' ', content_width - len_trim(panel%status_message) - 4) // ' │')
298 else
299 call terminal_write('│' // DIM // ' ↑↓ Navigate Enter Install r Refresh Esc Close' // RESET)
300 call terminal_write(repeat(' ', content_width - 52) // '│')
301 end if
302
303 ! Draw bottom border
304 row = row + 1
305 call terminal_move_cursor(row, start_col)
306 call terminal_write(border_bottom)
307
308 ! Hide cursor while panel is shown
309 call terminal_hide_cursor()
310 end subroutine render_lsp_server_installer_panel
311
312 subroutine render_confirm_dialog(panel, screen_cols)
313 type(lsp_server_installer_panel_t), intent(in) :: panel
314 integer, intent(in) :: screen_cols
315 integer :: start_col, start_row, row, content_width
316 character(len=:), allocatable :: border_top, border_bottom
317 character(len=256) :: server_name, install_cmd
318 character(len=*), parameter :: ESC = char(27)
319 character(len=*), parameter :: CYAN = ESC // '[36m'
320 character(len=*), parameter :: YELLOW = ESC // '[33m'
321 character(len=*), parameter :: GREEN = ESC // '[32m'
322 character(len=*), parameter :: RED = ESC // '[31m'
323 character(len=*), parameter :: RESET = ESC // '[0m'
324
325 content_width = min(PANEL_WIDTH, screen_cols - 4)
326 start_col = max(1, (screen_cols - content_width) / 2)
327 start_row = 5
328
329 border_top = '┌' // repeat('─', content_width - 2) // '┐'
330 border_bottom = '└' // repeat('─', content_width - 2) // '┘'
331
332 server_name = panel%servers(panel%confirm_server_index)%name
333 install_cmd = panel%servers(panel%confirm_server_index)%install_cmd
334
335 ! Top border
336 call terminal_move_cursor(start_row, start_col)
337 call terminal_write(border_top)
338
339 ! Title
340 row = start_row + 1
341 call terminal_move_cursor(row, start_col)
342 call terminal_write('│' // CYAN // ' Install ' // trim(server_name) // '?' // RESET)
343 call terminal_write(repeat(' ', content_width - 12 - len_trim(server_name)) // '│')
344
345 ! Blank line
346 row = row + 1
347 call terminal_move_cursor(row, start_col)
348 call terminal_write('│' // repeat(' ', content_width - 2) // '│')
349
350 ! Command
351 row = row + 1
352 call terminal_move_cursor(row, start_col)
353 call terminal_write('│ Command: ' // YELLOW // trim(install_cmd) // RESET)
354 call terminal_write(repeat(' ', content_width - 12 - len_trim(install_cmd)) // '│')
355
356 ! Blank line
357 row = row + 1
358 call terminal_move_cursor(row, start_col)
359 call terminal_write('│' // repeat(' ', content_width - 2) // '│')
360
361 ! Yes/No buttons
362 row = row + 1
363 call terminal_move_cursor(row, start_col)
364 call terminal_write('│' // repeat(' ', (content_width - 20) / 2))
365 call terminal_write('[' // GREEN // 'Y' // RESET // ']es ')
366 call terminal_write('[' // RED // 'N' // RESET // ']o')
367 call terminal_write(repeat(' ', (content_width - 20) / 2) // '│')
368
369 ! Bottom border
370 row = row + 1
371 call terminal_move_cursor(row, start_col)
372 call terminal_write(border_bottom)
373
374 call terminal_hide_cursor()
375 end subroutine render_confirm_dialog
376
377 end module lsp_server_installer_panel_module
378