@@ -14,6 +14,8 @@ module lsp_server_manager_module |
| 14 | 14 | public :: send_request, send_notification |
| 15 | 15 | public :: process_server_messages |
| 16 | 16 | public :: register_callback |
| 17 | + public :: get_language_for_file, start_lsp_for_file |
| 18 | + public :: notify_file_opened, notify_file_changed, notify_file_closed |
| 17 | 19 | |
| 18 | 20 | ! Language server configuration |
| 19 | 21 | type :: server_config_t |
@@ -622,4 +624,122 @@ contains |
| 622 | 624 | manager%num_callbacks = manager%num_callbacks - 1 |
| 623 | 625 | end subroutine remove_callback |
| 624 | 626 | |
| 627 | + ! Helper to get language from filename |
| 628 | + function get_language_for_file(filename) result(language) |
| 629 | + character(len=*), intent(in) :: filename |
| 630 | + character(len=:), allocatable :: language |
| 631 | + integer :: dot_pos |
| 632 | + |
| 633 | + ! Find last dot in filename |
| 634 | + dot_pos = index(filename, '.', back=.true.) |
| 635 | + if (dot_pos == 0) then |
| 636 | + language = "" |
| 637 | + return |
| 638 | + end if |
| 639 | + |
| 640 | + ! Match extension to language |
| 641 | + select case(filename(dot_pos:)) |
| 642 | + case('.py') |
| 643 | + language = "python" |
| 644 | + case('.rs') |
| 645 | + language = "rust" |
| 646 | + case('.c', '.h') |
| 647 | + language = "c" |
| 648 | + case('.cpp', '.cc', '.cxx', '.hpp', '.hxx', '.C', '.H') |
| 649 | + language = "cpp" |
| 650 | + case('.go') |
| 651 | + language = "go" |
| 652 | + case('.ts', '.tsx') |
| 653 | + language = "typescript" |
| 654 | + case('.js', '.jsx') |
| 655 | + language = "javascript" |
| 656 | + case('.f90', '.f95', '.f03', '.f08', '.F90', '.F95', '.F03', '.F08') |
| 657 | + language = "fortran" |
| 658 | + case('.java') |
| 659 | + language = "java" |
| 660 | + case('.rb') |
| 661 | + language = "ruby" |
| 662 | + case('.lua') |
| 663 | + language = "lua" |
| 664 | + case default |
| 665 | + language = "" |
| 666 | + end select |
| 667 | + end function get_language_for_file |
| 668 | + |
| 669 | + ! Start LSP server for a file if needed |
| 670 | + function start_lsp_for_file(manager, filename) result(server_index) |
| 671 | + type(lsp_manager_t), intent(inout) :: manager |
| 672 | + character(len=*), intent(in) :: filename |
| 673 | + integer :: server_index |
| 674 | + character(len=:), allocatable :: language |
| 675 | + character(len=256) :: workspace_path |
| 676 | + integer :: slash_pos |
| 677 | + |
| 678 | + server_index = 0 |
| 679 | + |
| 680 | + ! Get language from file extension |
| 681 | + language = get_language_for_file(filename) |
| 682 | + if (language == "") return |
| 683 | + |
| 684 | + ! Extract workspace path from filename (directory containing file) |
| 685 | + slash_pos = index(filename, '/', back=.true.) |
| 686 | + if (slash_pos > 0) then |
| 687 | + workspace_path = filename(1:slash_pos-1) |
| 688 | + else |
| 689 | + workspace_path = "." |
| 690 | + end if |
| 691 | + |
| 692 | + ! Get or start server for this language |
| 693 | + server_index = get_or_start_server(manager, language, trim(workspace_path)) |
| 694 | + end function start_lsp_for_file |
| 695 | + |
| 696 | + ! Send textDocument/didOpen notification |
| 697 | + subroutine notify_file_opened(manager, server_index, filename, content) |
| 698 | + use lsp_protocol_module, only: create_did_open_notification |
| 699 | + type(lsp_manager_t), intent(inout) :: manager |
| 700 | + integer, intent(in) :: server_index |
| 701 | + character(len=*), intent(in) :: filename |
| 702 | + character(len=*), intent(in) :: content |
| 703 | + type(lsp_message_t) :: msg |
| 704 | + character(len=:), allocatable :: language |
| 705 | + |
| 706 | + if (server_index < 1 .or. server_index > manager%num_servers) return |
| 707 | + if (.not. manager%servers(server_index)%initialized) return |
| 708 | + |
| 709 | + language = get_language_for_file(filename) |
| 710 | + msg = create_did_open_notification(filename, language, 1, content) |
| 711 | + call send_notification(manager%servers(server_index), msg) |
| 712 | + end subroutine notify_file_opened |
| 713 | + |
| 714 | + ! Send textDocument/didChange notification |
| 715 | + subroutine notify_file_changed(manager, server_index, filename, content) |
| 716 | + use lsp_protocol_module, only: create_did_change_notification |
| 717 | + type(lsp_manager_t), intent(inout) :: manager |
| 718 | + integer, intent(in) :: server_index |
| 719 | + character(len=*), intent(in) :: filename |
| 720 | + character(len=*), intent(in) :: content |
| 721 | + type(lsp_message_t) :: msg |
| 722 | + |
| 723 | + if (server_index < 1 .or. server_index > manager%num_servers) return |
| 724 | + if (.not. manager%servers(server_index)%initialized) return |
| 725 | + |
| 726 | + msg = create_did_change_notification(filename, 2, content) |
| 727 | + call send_notification(manager%servers(server_index), msg) |
| 728 | + end subroutine notify_file_changed |
| 729 | + |
| 730 | + ! Send textDocument/didClose notification |
| 731 | + subroutine notify_file_closed(manager, server_index, filename) |
| 732 | + use lsp_protocol_module, only: create_did_close_notification |
| 733 | + type(lsp_manager_t), intent(inout) :: manager |
| 734 | + integer, intent(in) :: server_index |
| 735 | + character(len=*), intent(in) :: filename |
| 736 | + type(lsp_message_t) :: msg |
| 737 | + |
| 738 | + if (server_index < 1 .or. server_index > manager%num_servers) return |
| 739 | + if (.not. manager%servers(server_index)%initialized) return |
| 740 | + |
| 741 | + msg = create_did_close_notification(filename) |
| 742 | + call send_notification(manager%servers(server_index), msg) |
| 743 | + end subroutine notify_file_closed |
| 744 | + |
| 625 | 745 | end module lsp_server_manager_module |