| 1 | module document_sync_module |
| 2 | use iso_fortran_env, only: int32, int64, real64 |
| 3 | use lsp_server_manager_module, only: notify_file_changed |
| 4 | implicit none |
| 5 | private |
| 6 | |
| 7 | public :: document_sync_t |
| 8 | public :: init_document_sync, cleanup_document_sync |
| 9 | public :: notify_document_change, flush_pending_changes |
| 10 | public :: set_sync_delay |
| 11 | |
| 12 | ! Document synchronization manager with debouncing |
| 13 | type :: document_sync_t |
| 14 | integer :: version = 0 ! Current document version |
| 15 | character(len=:), allocatable :: uri ! File URI |
| 16 | character(len=:), allocatable :: pending_content ! Pending content to send |
| 17 | real(real64) :: last_change_time = 0.0 ! Time of last change (for debouncing) |
| 18 | real(real64) :: sync_delay = 0.5 ! Delay before sending (in seconds) |
| 19 | logical :: has_pending_changes = .false. ! Flag for pending changes |
| 20 | integer :: server_index = 0 ! LSP server handling this document |
| 21 | end type document_sync_t |
| 22 | |
| 23 | contains |
| 24 | |
| 25 | subroutine init_document_sync(sync, uri, server_index) |
| 26 | type(document_sync_t), intent(out) :: sync |
| 27 | character(len=*), intent(in) :: uri |
| 28 | integer, intent(in) :: server_index |
| 29 | |
| 30 | sync%version = 0 |
| 31 | sync%uri = uri |
| 32 | sync%server_index = server_index |
| 33 | sync%sync_delay = 0.5 ! 500ms default |
| 34 | sync%has_pending_changes = .false. |
| 35 | sync%last_change_time = 0.0 |
| 36 | end subroutine init_document_sync |
| 37 | |
| 38 | subroutine cleanup_document_sync(sync) |
| 39 | type(document_sync_t), intent(inout) :: sync |
| 40 | |
| 41 | if (allocated(sync%uri)) deallocate(sync%uri) |
| 42 | if (allocated(sync%pending_content)) deallocate(sync%pending_content) |
| 43 | sync%has_pending_changes = .false. |
| 44 | end subroutine cleanup_document_sync |
| 45 | |
| 46 | subroutine set_sync_delay(sync, delay_seconds) |
| 47 | type(document_sync_t), intent(inout) :: sync |
| 48 | real(real64), intent(in) :: delay_seconds |
| 49 | |
| 50 | sync%sync_delay = max(0.1_real64, min(2.0_real64, delay_seconds)) ! Clamp between 100ms and 2s |
| 51 | end subroutine set_sync_delay |
| 52 | |
| 53 | ! Notify that the document has changed (with debouncing) |
| 54 | subroutine notify_document_change(sync, content) |
| 55 | type(document_sync_t), intent(inout) :: sync |
| 56 | character(len=*), intent(in) :: content |
| 57 | |
| 58 | ! Update pending content |
| 59 | if (allocated(sync%pending_content)) deallocate(sync%pending_content) |
| 60 | sync%pending_content = content |
| 61 | |
| 62 | ! Mark that we have pending changes |
| 63 | sync%has_pending_changes = .true. |
| 64 | |
| 65 | ! Update timestamp |
| 66 | sync%last_change_time = get_current_time() |
| 67 | end subroutine notify_document_change |
| 68 | |
| 69 | ! Check if enough time has passed and flush pending changes |
| 70 | subroutine flush_pending_changes(sync, manager, force) |
| 71 | use lsp_server_manager_module, only: lsp_manager_t, notify_file_changed |
| 72 | type(document_sync_t), intent(inout) :: sync |
| 73 | type(lsp_manager_t), intent(inout) :: manager |
| 74 | logical, intent(in), optional :: force |
| 75 | real(real64) :: current_time, elapsed |
| 76 | logical :: should_flush |
| 77 | |
| 78 | if (.not. sync%has_pending_changes) return |
| 79 | |
| 80 | should_flush = .false. |
| 81 | if (present(force)) then |
| 82 | should_flush = force |
| 83 | else |
| 84 | ! Check if enough time has elapsed |
| 85 | current_time = get_current_time() |
| 86 | elapsed = current_time - sync%last_change_time |
| 87 | should_flush = (elapsed >= sync%sync_delay) |
| 88 | end if |
| 89 | |
| 90 | if (should_flush .and. allocated(sync%pending_content)) then |
| 91 | ! Increment version |
| 92 | sync%version = sync%version + 1 |
| 93 | |
| 94 | ! Send the notification |
| 95 | call notify_file_changed(manager, sync%server_index, & |
| 96 | sync%uri, sync%pending_content) |
| 97 | |
| 98 | ! Clear pending changes |
| 99 | sync%has_pending_changes = .false. |
| 100 | deallocate(sync%pending_content) |
| 101 | end if |
| 102 | end subroutine flush_pending_changes |
| 103 | |
| 104 | ! Helper function to get current time in seconds |
| 105 | function get_current_time() result(time) |
| 106 | real(real64) :: time |
| 107 | integer(int64) :: count, count_rate |
| 108 | |
| 109 | call system_clock(count, count_rate) |
| 110 | time = real(count, real64) / real(count_rate, real64) |
| 111 | end function get_current_time |
| 112 | |
| 113 | end module document_sync_module |