| 1 | module server_detection_module |
| 2 | implicit none |
| 3 | private |
| 4 | |
| 5 | public :: detected_server_t |
| 6 | public :: detect_all_servers, check_server_installed |
| 7 | public :: get_known_servers_count |
| 8 | |
| 9 | integer, parameter :: MAX_SERVERS = 30 |
| 10 | |
| 11 | type :: detected_server_t |
| 12 | character(len=64) :: name = '' ! "pyright", "clangd", etc. |
| 13 | character(len=32) :: language = '' ! "python", "c", etc. |
| 14 | character(len=256) :: install_cmd = '' ! "pip install pyright" |
| 15 | character(len=256) :: description = '' ! "Python type checker and language server" |
| 16 | character(len=128) :: check_cmd = '' ! Command to check if installed |
| 17 | logical :: is_installed = .false. ! Result of detection |
| 18 | character(len=64) :: version = '' ! Detected version (if installed) |
| 19 | end type detected_server_t |
| 20 | |
| 21 | contains |
| 22 | |
| 23 | function get_known_servers_count() result(count) |
| 24 | integer :: count |
| 25 | count = 20 ! Number of servers we know about |
| 26 | end function get_known_servers_count |
| 27 | |
| 28 | subroutine detect_all_servers(servers, num_servers) |
| 29 | type(detected_server_t), intent(out), allocatable :: servers(:) |
| 30 | integer, intent(out) :: num_servers |
| 31 | integer :: i |
| 32 | |
| 33 | num_servers = get_known_servers_count() |
| 34 | allocate(servers(num_servers)) |
| 35 | |
| 36 | ! Initialize all known servers |
| 37 | call init_known_servers(servers) |
| 38 | |
| 39 | ! Check which are installed |
| 40 | do i = 1, num_servers |
| 41 | servers(i)%is_installed = check_server_installed(servers(i)%check_cmd) |
| 42 | end do |
| 43 | end subroutine detect_all_servers |
| 44 | |
| 45 | subroutine init_known_servers(servers) |
| 46 | type(detected_server_t), intent(inout) :: servers(:) |
| 47 | integer :: i |
| 48 | |
| 49 | i = 1 |
| 50 | |
| 51 | ! Python - Pyright |
| 52 | servers(i)%name = 'pyright' |
| 53 | servers(i)%language = 'Python' |
| 54 | servers(i)%install_cmd = 'pipx install pyright' |
| 55 | servers(i)%description = 'Python type checker and language server' |
| 56 | servers(i)%check_cmd = 'pyright-langserver' |
| 57 | i = i + 1 |
| 58 | |
| 59 | ! Python - Ruff |
| 60 | servers(i)%name = 'ruff' |
| 61 | servers(i)%language = 'Python' |
| 62 | servers(i)%install_cmd = 'pipx install ruff' |
| 63 | servers(i)%description = 'Fast Python linter with auto-fix' |
| 64 | servers(i)%check_cmd = 'ruff' |
| 65 | i = i + 1 |
| 66 | |
| 67 | ! Python - python-lsp-server |
| 68 | servers(i)%name = 'python-lsp-server' |
| 69 | servers(i)%language = 'Python' |
| 70 | servers(i)%install_cmd = 'pipx install python-lsp-server' |
| 71 | servers(i)%description = 'Python LSP (pylsp) with plugins support' |
| 72 | servers(i)%check_cmd = 'pylsp' |
| 73 | i = i + 1 |
| 74 | |
| 75 | ! C/C++ - clangd |
| 76 | servers(i)%name = 'clangd' |
| 77 | servers(i)%language = 'C/C++' |
| 78 | servers(i)%install_cmd = 'brew install llvm' |
| 79 | servers(i)%description = 'C/C++ language server from LLVM' |
| 80 | servers(i)%check_cmd = 'clangd' |
| 81 | i = i + 1 |
| 82 | |
| 83 | ! Rust - rust-analyzer |
| 84 | servers(i)%name = 'rust-analyzer' |
| 85 | servers(i)%language = 'Rust' |
| 86 | servers(i)%install_cmd = 'rustup component add rust-analyzer' |
| 87 | servers(i)%description = 'Rust language server' |
| 88 | servers(i)%check_cmd = 'rust-analyzer' |
| 89 | i = i + 1 |
| 90 | |
| 91 | ! Go - gopls |
| 92 | servers(i)%name = 'gopls' |
| 93 | servers(i)%language = 'Go' |
| 94 | servers(i)%install_cmd = 'go install golang.org/x/tools/gopls@latest' |
| 95 | servers(i)%description = 'Go language server' |
| 96 | servers(i)%check_cmd = 'gopls' |
| 97 | i = i + 1 |
| 98 | |
| 99 | ! TypeScript/JavaScript - typescript-language-server |
| 100 | servers(i)%name = 'typescript-language-server' |
| 101 | servers(i)%language = 'JS/TS' |
| 102 | servers(i)%install_cmd = 'npm install -g typescript-language-server typescript' |
| 103 | servers(i)%description = 'JavaScript and TypeScript language server' |
| 104 | servers(i)%check_cmd = 'typescript-language-server' |
| 105 | i = i + 1 |
| 106 | |
| 107 | ! JavaScript/TypeScript - vtsls (faster alternative) |
| 108 | servers(i)%name = 'vtsls' |
| 109 | servers(i)%language = 'JS/TS' |
| 110 | servers(i)%install_cmd = 'npm install -g @vtsls/language-server' |
| 111 | servers(i)%description = 'Fast TypeScript language server' |
| 112 | servers(i)%check_cmd = 'vtsls' |
| 113 | i = i + 1 |
| 114 | |
| 115 | ! Lua - lua-language-server |
| 116 | servers(i)%name = 'lua-language-server' |
| 117 | servers(i)%language = 'Lua' |
| 118 | servers(i)%install_cmd = 'brew install lua-language-server' |
| 119 | servers(i)%description = 'Lua language server by sumneko' |
| 120 | servers(i)%check_cmd = 'lua-language-server' |
| 121 | i = i + 1 |
| 122 | |
| 123 | ! Ruby - solargraph |
| 124 | servers(i)%name = 'solargraph' |
| 125 | servers(i)%language = 'Ruby' |
| 126 | servers(i)%install_cmd = 'gem install solargraph' |
| 127 | servers(i)%description = 'Ruby language server' |
| 128 | servers(i)%check_cmd = 'solargraph' |
| 129 | i = i + 1 |
| 130 | |
| 131 | ! Java - jdtls |
| 132 | servers(i)%name = 'jdtls' |
| 133 | servers(i)%language = 'Java' |
| 134 | servers(i)%install_cmd = 'brew install jdtls' |
| 135 | servers(i)%description = 'Eclipse JDT Language Server' |
| 136 | servers(i)%check_cmd = 'jdtls' |
| 137 | i = i + 1 |
| 138 | |
| 139 | ! HTML/CSS/JSON - vscode-langservers-extracted |
| 140 | servers(i)%name = 'vscode-langservers' |
| 141 | servers(i)%language = 'HTML/CSS/JSON' |
| 142 | servers(i)%install_cmd = 'npm i -g vscode-langservers-extracted' |
| 143 | servers(i)%description = 'HTML, CSS, JSON language servers' |
| 144 | servers(i)%check_cmd = 'vscode-html-language-server' |
| 145 | i = i + 1 |
| 146 | |
| 147 | ! Bash - bash-language-server |
| 148 | servers(i)%name = 'bash-language-server' |
| 149 | servers(i)%language = 'Bash' |
| 150 | servers(i)%install_cmd = 'npm i -g bash-language-server' |
| 151 | servers(i)%description = 'Bash/Shell language server' |
| 152 | servers(i)%check_cmd = 'bash-language-server' |
| 153 | i = i + 1 |
| 154 | |
| 155 | ! YAML - yaml-language-server |
| 156 | servers(i)%name = 'yaml-language-server' |
| 157 | servers(i)%language = 'YAML' |
| 158 | servers(i)%install_cmd = 'npm i -g yaml-language-server' |
| 159 | servers(i)%description = 'YAML language server' |
| 160 | servers(i)%check_cmd = 'yaml-language-server' |
| 161 | i = i + 1 |
| 162 | |
| 163 | ! Docker - dockerfile-language-server |
| 164 | servers(i)%name = 'dockerfile-langserver' |
| 165 | servers(i)%language = 'Docker' |
| 166 | servers(i)%install_cmd = 'npm i -g dockerfile-language-server-nodejs' |
| 167 | servers(i)%description = 'Dockerfile language server' |
| 168 | servers(i)%check_cmd = 'docker-langserver' |
| 169 | i = i + 1 |
| 170 | |
| 171 | ! Zig - zls |
| 172 | servers(i)%name = 'zls' |
| 173 | servers(i)%language = 'Zig' |
| 174 | servers(i)%install_cmd = 'brew install zls' |
| 175 | servers(i)%description = 'Zig language server' |
| 176 | servers(i)%check_cmd = 'zls' |
| 177 | i = i + 1 |
| 178 | |
| 179 | ! Swift - sourcekit-lsp |
| 180 | servers(i)%name = 'sourcekit-lsp' |
| 181 | servers(i)%language = 'Swift' |
| 182 | servers(i)%install_cmd = '# Included with Xcode' |
| 183 | servers(i)%description = 'Swift/Obj-C language server' |
| 184 | servers(i)%check_cmd = 'sourcekit-lsp' |
| 185 | i = i + 1 |
| 186 | |
| 187 | ! Haskell - haskell-language-server |
| 188 | servers(i)%name = 'haskell-language-server' |
| 189 | servers(i)%language = 'Haskell' |
| 190 | servers(i)%install_cmd = 'ghcup install hls' |
| 191 | servers(i)%description = 'Haskell language server' |
| 192 | servers(i)%check_cmd = 'haskell-language-server-wrapper' |
| 193 | i = i + 1 |
| 194 | |
| 195 | ! Terraform - terraform-ls |
| 196 | servers(i)%name = 'terraform-ls' |
| 197 | servers(i)%language = 'Terraform' |
| 198 | servers(i)%install_cmd = 'brew install hashicorp/tap/terraform-ls' |
| 199 | servers(i)%description = 'Terraform language server' |
| 200 | servers(i)%check_cmd = 'terraform-ls' |
| 201 | i = i + 1 |
| 202 | |
| 203 | ! Fortran - fortls |
| 204 | servers(i)%name = 'fortls' |
| 205 | servers(i)%language = 'Fortran' |
| 206 | servers(i)%install_cmd = 'pipx install fortls' |
| 207 | servers(i)%description = 'Fortran language server' |
| 208 | servers(i)%check_cmd = 'fortls' |
| 209 | end subroutine init_known_servers |
| 210 | |
| 211 | function check_server_installed(cmd_name) result(installed) |
| 212 | character(len=*), intent(in) :: cmd_name |
| 213 | logical :: installed |
| 214 | character(len=512) :: check_command |
| 215 | integer :: exit_status |
| 216 | |
| 217 | installed = .false. |
| 218 | if (len_trim(cmd_name) == 0) return |
| 219 | |
| 220 | ! Use 'which' on Unix to check if command exists |
| 221 | ! Redirect output to /dev/null to suppress it |
| 222 | check_command = 'which ' // trim(cmd_name) // ' > /dev/null 2>&1' |
| 223 | |
| 224 | call execute_command_line(trim(check_command), wait=.true., exitstat=exit_status) |
| 225 | |
| 226 | installed = (exit_status == 0) |
| 227 | end function check_server_installed |
| 228 | |
| 229 | end module server_detection_module |
| 230 |