jamesnvc/lsp_server

Language Server Protocol server for SWI-Prolog

129

stars

511

commits

Prolog

primary language

Aug 27, 2026

updated

README

* Prolog Language server

/Recent development has been sponsored by [[https://logicmoo.org][LogicMoo]] - Thank you!/

Still a work-in-progress -- please open an issue if you have any issues or feature requests!.

Currently supports:
 - diagnostics (singleton variables, syntax errors) via xref
 - find references/definitions
 - documentation of predicates on hover
 - auto-completion of predicates
 - code formatting (✨new!✨)
 - symbol highlighting (✨new!✨)
 - variable renaming (✨new!✨)

The code formatter can also be run stand-alone. After installing the ~lsp_server~ pack, you can run ~swipl formatter <file>~ at the command line.

Only tested with SWI-Prolog, as it heavily uses its introspection facilities to do its stuff.
It should work with any relatively-recent version of SWI-Prolog, but for best results (for "find references" in particular), use a version with ~xref_called/5~ (8.1.5 or newer; past commit [[https://github.com/SWI-Prolog/swipl-devel/commit/303f6430de5c9d7e225d8eb6fb8bb8b59e7c5f8f][303f6430de5c]]).

Installable as a pack with ~swipl pack install lsp_server~ or ~?- pack_install(lsp_server).~ from a repl.

As of version 2.5.0, running the server over a socket is now supported by passing in the commandline arguments ~port <port number>~ (instead of ~stdio~).

If running on Windows directly (i.e. not via WSL) running the server via stdio will NOT work and you will need to run the server over a socket, so please consult that section of the documention for your editor..

* Emacs

**  [[https://github.com/emacs-lsp/lsp-mode][lsp-mode]]:

*** socket server

#+begin_src emacs-lisp
(lsp-register-client
  (make-lsp-client
   :new-connection
   (lsp-tcp-connection (lambda (port) (list "swipl"
                                    "-g" "use_module(library(lsp_server))."
                                    "-g" "lsp_server:main"
                                    "-t" "halt"
                                    "--" "port" port)))
   :major-modes '(prolog-mode)
   :priority 1
   :multi-root t
   :server-id 'prolog-ls))
#+end_src
*** stdio server

#+begin_src emacs-lisp
(lsp-register-client
  (make-lsp-client
   :new-connection
   (lsp-stdio-connection (list "swipl"
                               "-g" "use_module(library(lsp_server))."
                               "-g" "lsp_server:main"
                               "-t" "halt"
                               "--" "stdio"))
   :major-modes '(prolog-mode)
   :priority 1
   :multi-root t
   :server-id 'prolog-ls))
#+end_src

** [[https://github.com/joaotavora/eglot][eglot]]
*** socket server
#+begin_src emacs-lisp
(setopt eglot-server-programs (cons
                                 (cons 'prolog-mode
                                       (list "swipl"
                                             "-O"
                                             "-g" "use_module(library(lsp_server))."
                                             "-g" "lsp_server:main"
                                             "-t" "halt"
                                             "--" "port" :autoport))
                                 eglot-server-programs))
#+end_src

*** stdio server
#+begin_src emacs-lisp
(setopt eglot-server-programs (cons
                                 (cons 'prolog-mode
                                       (list "swipl"
                                             "-O"
                                             "-g" "use_module(library(lsp_server))."
                                             "-g" "lsp_server:main"
                                             "-t" "halt"
                                             "--" "stdio"))
                                 eglot-server-programs))
#+end_src

* Vim/Neovim

* Neovim
** [[https://github.com/neoclide/coc.nvim][CoC]]
*** stdio server
Put the following in ~coc-settings.json~ (which you can access by using the command ~:CocConfig~).

#+begin_src json
{"languageserver": {
  "prolog-lsp": {
    "command": "swipl",
    "args": ["-g", "use_module(library(lsp_server)).",
             "-g", "lsp_server:main",
             "-t", "halt",
             "--", "stdio"
            ],
    "filetypes": ["prolog"]
  }}
}
#+end_src
*** socket server

CoC does not support automatically starting a socket server.
If you are on Windows and using CoC and hence need a socket server, you'll have to manually start the LSP process by running the following command:

#+begin_src sh
swipl -g 'use_module(library(lsp_server))' -g 'lsp_server:main' -t halt -- port 12345
#+end_src

Where "12345" is an arbitrary, free port number.

Then, with that started, add the following to ~coc-settings.json~ (accessed via ~:CocConfig~).

#+begin_src json
{"languageserver": {
  "prolog-lsp": {
    "host": "127.0.0.1",
    "port": 12345,
    "filetypes": ["prolog"]
  }}
}
#+end_src

Ensuring the port entered in the config is the same one used when starting the server process

** Native LSP (for Neovim >= 0.11)

*** stdio server
Put the following in ~$XDG_CONFIG_DIR/nvim/lsp/prolog.lua~:

#+begin_src lua
return {
   cmd = { 'swipl',
           '-g', 'use_module(library(lsp_server))',
           '-g', 'lsp_server:main',
           '-t', 'halt',
           '--', 'stdio' },
   root_markers = { '.git', },
   filetypes = { 'prolog' },
}
#+end_src

And add ~vim.lsp.enable({'prolog'})~ to ~$XDG_CONFIG_DIR/nvim/init.lua~.

*** socket server
Put the following in ~$XDG_CONFIG_DIR/nvim/lsp/prolog.lua~:

#+begin_src lua
local find_port = function()
   local uv = vim.uv
   local tcp = uv.new_tcp()
   tcp:bind("127.0.0.1", 0)
   local port = tcp:getsockname().port
   tcp:close_reset()
   return port
end
return {
   cmd = function(...)
      local server_port = find_port()
      vim.system({'swipl',
                  '-g', 'use_module(library(lsp_server))',
                  '-g', 'lsp_server:main',
                  '-t', 'halt',
                  '--', 'port', server_port},
         {},
         function(...)
            print("LSP PROCESS EXITED", ...)
      end)
      vim.uv.sleep(500)
      return vim.lsp.rpc.connect('127.0.0.1', server_port)(...)
   end,
   root_markers = { '.git', },
   filetypes = { 'prolog' },
}
#+end_src

And add ~vim.lsp.enable({'prolog'})~ to ~$XDG_CONFIG_DIR/nvim/init.lua~.

** Native LSP (for Neovim >= 0.5 < 0.11)

Install the [[https://github.com/neovim/nvim-lspconfig][neovim/nvim-lspconfig]] package

Put the following in ~$XDG_CONFIG_DIR/nvim/lua/lspconfig/prolog_lsp.lua~:

#+begin_src lua
local configs = require 'lspconfig/configs'
local util = require 'lspconfig/util'

configs.prolog_lsp = {
  default_config = {
    cmd = {"swipl",
           "-g", "use_module(library(lsp_server)).",
           "-g", "lsp_server:main",
           "-t", "halt",
           "--", "stdio"};
    filetypes = {"prolog"};
    root_dir = util.root_pattern("pack.pl");
  };
  docs = {
     description = [[
  https://github.com/jamesnvc/prolog_lsp

  Prolog Language Server
  ]];
  }
}
-- vim:et ts=2 sw=2
#+end_src

Then add the following to ~init.vim~:

#+begin_src viml
lua << EOF
require('lspconfig/prolog_lsp')
require('lspconfig').prolog_lsp.setup{}
EOF
#+end_src

* LazyVim

** stdio
Create the following file in ~$XDG_CONFIG_DIR/nvim/lua/plugins/lsp.lua~

#+begin_src lua
return {
  {
    "neovim/nvim-lspconfig",
    opts = {
      servers = {
        prolog = {},
      },
      setup = {
        prolog = function(_, opts)
          local lspconfig = require("lspconfig")
          local configs = require("lspconfig.configs")
          local util = require("lspconfig.util")

          local root_files = { ".git", "pack.pl" }

          if not configs.prolog then
            configs.prolog = {
              default_config = {
                cmd = {
                  "swipl",
                  "-g",
                  "use_module(library(lsp_server)).",
                  "-g",
                  "lsp_server:main",
                  "-t",
                  "halt",
                  "--",
                  "stdio",
                },
                filetypes = { "prolog" },
                single_file_support = true,
                root_dir = util.root_pattern(unpack(root_files)),
                settings = {},
              },
              commands = {},
              docs = {
                description = [[
              Prolog LSP server
              ]],
              },
            }
          end
          lspconfig.prolog.setup(opts)
        end,
      },
    },
  },
}
#+end_src

* VSCode

Choose one from the list below:

  - download the latest ~.vsix~ file from the [[https://github.com/jamesnvc/lsp_server/releases][releases page]]
  - clone this repo and copy/symlink the ~vscode/~ directory to ~~/.vscode/extensions/~
  - clone and build the ~.vsix~ file yourself by the follwing steps:
    1. ~cd /path/to/clone/vscode~
    2. ~npm install~
    3. ~npx vsce package~
    4. add the resulting ~.vsix~ to VSCode by clicking the ~...~ at the top right of the "Extensions" panel then selecting ~Install from VSIX...~


* Helix

Helix already includes configuration for this Prolog LSP server, so it should mostly Just Work.

However, the default configuration gives the '.pl' extension to perl, so to avoid having to manually do ~:set-language prolog~ each time, you can add the following to ~$XDG_CONFIG/helix/languages.toml~ to remove Perl's association with that extension:

#+begin_src toml
[[language]]
name = "perl"
file-types = ["perl"]
#+end_src
* Kate

Open settings, go to ~LSP Client~, select the ~User Server Settings~ tab, and add the following content (or merge with your existing settings if present):

#+begin_src json
{
  "servers": {
    "prolog": {
      "command": ["swipl", "-O",
                  "-g", "use_module(library(lsp_server))",
                  "-g", "lsp_server:main",
                  "-t", "halt",
                  "--", "stdio"],
      "url": "https://github.com/jamesnvc/lsp_server",
      "root": ""
      "highlightingModeRegex": "^Prolog$"
    }
  }
}
#+end_src

Not written in Markdown, so it's shown here as plain text — view it formatted on GitHub.

Contributors

jamesnvc

484 commits

eignnx

27 commits

jamesnvc/lsp_server

Language Server Protocol server for SWI-Prolog

129

stars

511

commits

Prolog

primary language

Aug 27, 2026

updated

README

* Prolog Language server

/Recent development has been sponsored by [[https://logicmoo.org][LogicMoo]] - Thank you!/

Still a work-in-progress -- please open an issue if you have any issues or feature requests!.

Currently supports:
 - diagnostics (singleton variables, syntax errors) via xref
 - find references/definitions
 - documentation of predicates on hover
 - auto-completion of predicates
 - code formatting (✨new!✨)
 - symbol highlighting (✨new!✨)
 - variable renaming (✨new!✨)

The code formatter can also be run stand-alone. After installing the ~lsp_server~ pack, you can run ~swipl formatter <file>~ at the command line.

Only tested with SWI-Prolog, as it heavily uses its introspection facilities to do its stuff.
It should work with any relatively-recent version of SWI-Prolog, but for best results (for "find references" in particular), use a version with ~xref_called/5~ (8.1.5 or newer; past commit [[https://github.com/SWI-Prolog/swipl-devel/commit/303f6430de5c9d7e225d8eb6fb8bb8b59e7c5f8f][303f6430de5c]]).

Installable as a pack with ~swipl pack install lsp_server~ or ~?- pack_install(lsp_server).~ from a repl.

As of version 2.5.0, running the server over a socket is now supported by passing in the commandline arguments ~port <port number>~ (instead of ~stdio~).

If running on Windows directly (i.e. not via WSL) running the server via stdio will NOT work and you will need to run the server over a socket, so please consult that section of the documention for your editor..

* Emacs

**  [[https://github.com/emacs-lsp/lsp-mode][lsp-mode]]:

*** socket server

#+begin_src emacs-lisp
(lsp-register-client
  (make-lsp-client
   :new-connection
   (lsp-tcp-connection (lambda (port) (list "swipl"
                                    "-g" "use_module(library(lsp_server))."
                                    "-g" "lsp_server:main"
                                    "-t" "halt"
                                    "--" "port" port)))
   :major-modes '(prolog-mode)
   :priority 1
   :multi-root t
   :server-id 'prolog-ls))
#+end_src
*** stdio server

#+begin_src emacs-lisp
(lsp-register-client
  (make-lsp-client
   :new-connection
   (lsp-stdio-connection (list "swipl"
                               "-g" "use_module(library(lsp_server))."
                               "-g" "lsp_server:main"
                               "-t" "halt"
                               "--" "stdio"))
   :major-modes '(prolog-mode)
   :priority 1
   :multi-root t
   :server-id 'prolog-ls))
#+end_src

** [[https://github.com/joaotavora/eglot][eglot]]
*** socket server
#+begin_src emacs-lisp
(setopt eglot-server-programs (cons
                                 (cons 'prolog-mode
                                       (list "swipl"
                                             "-O"
                                             "-g" "use_module(library(lsp_server))."
                                             "-g" "lsp_server:main"
                                             "-t" "halt"
                                             "--" "port" :autoport))
                                 eglot-server-programs))
#+end_src

*** stdio server
#+begin_src emacs-lisp
(setopt eglot-server-programs (cons
                                 (cons 'prolog-mode
                                       (list "swipl"
                                             "-O"
                                             "-g" "use_module(library(lsp_server))."
                                             "-g" "lsp_server:main"
                                             "-t" "halt"
                                             "--" "stdio"))
                                 eglot-server-programs))
#+end_src

* Vim/Neovim

* Neovim
** [[https://github.com/neoclide/coc.nvim][CoC]]
*** stdio server
Put the following in ~coc-settings.json~ (which you can access by using the command ~:CocConfig~).

#+begin_src json
{"languageserver": {
  "prolog-lsp": {
    "command": "swipl",
    "args": ["-g", "use_module(library(lsp_server)).",
             "-g", "lsp_server:main",
             "-t", "halt",
             "--", "stdio"
            ],
    "filetypes": ["prolog"]
  }}
}
#+end_src
*** socket server

CoC does not support automatically starting a socket server.
If you are on Windows and using CoC and hence need a socket server, you'll have to manually start the LSP process by running the following command:

#+begin_src sh
swipl -g 'use_module(library(lsp_server))' -g 'lsp_server:main' -t halt -- port 12345
#+end_src

Where "12345" is an arbitrary, free port number.

Then, with that started, add the following to ~coc-settings.json~ (accessed via ~:CocConfig~).

#+begin_src json
{"languageserver": {
  "prolog-lsp": {
    "host": "127.0.0.1",
    "port": 12345,
    "filetypes": ["prolog"]
  }}
}
#+end_src

Ensuring the port entered in the config is the same one used when starting the server process

** Native LSP (for Neovim >= 0.11)

*** stdio server
Put the following in ~$XDG_CONFIG_DIR/nvim/lsp/prolog.lua~:

#+begin_src lua
return {
   cmd = { 'swipl',
           '-g', 'use_module(library(lsp_server))',
           '-g', 'lsp_server:main',
           '-t', 'halt',
           '--', 'stdio' },
   root_markers = { '.git', },
   filetypes = { 'prolog' },
}
#+end_src

And add ~vim.lsp.enable({'prolog'})~ to ~$XDG_CONFIG_DIR/nvim/init.lua~.

*** socket server
Put the following in ~$XDG_CONFIG_DIR/nvim/lsp/prolog.lua~:

#+begin_src lua
local find_port = function()
   local uv = vim.uv
   local tcp = uv.new_tcp()
   tcp:bind("127.0.0.1", 0)
   local port = tcp:getsockname().port
   tcp:close_reset()
   return port
end
return {
   cmd = function(...)
      local server_port = find_port()
      vim.system({'swipl',
                  '-g', 'use_module(library(lsp_server))',
                  '-g', 'lsp_server:main',
                  '-t', 'halt',
                  '--', 'port', server_port},
         {},
         function(...)
            print("LSP PROCESS EXITED", ...)
      end)
      vim.uv.sleep(500)
      return vim.lsp.rpc.connect('127.0.0.1', server_port)(...)
   end,
   root_markers = { '.git', },
   filetypes = { 'prolog' },
}
#+end_src

And add ~vim.lsp.enable({'prolog'})~ to ~$XDG_CONFIG_DIR/nvim/init.lua~.

** Native LSP (for Neovim >= 0.5 < 0.11)

Install the [[https://github.com/neovim/nvim-lspconfig][neovim/nvim-lspconfig]] package

Put the following in ~$XDG_CONFIG_DIR/nvim/lua/lspconfig/prolog_lsp.lua~:

#+begin_src lua
local configs = require 'lspconfig/configs'
local util = require 'lspconfig/util'

configs.prolog_lsp = {
  default_config = {
    cmd = {"swipl",
           "-g", "use_module(library(lsp_server)).",
           "-g", "lsp_server:main",
           "-t", "halt",
           "--", "stdio"};
    filetypes = {"prolog"};
    root_dir = util.root_pattern("pack.pl");
  };
  docs = {
     description = [[
  https://github.com/jamesnvc/prolog_lsp

  Prolog Language Server
  ]];
  }
}
-- vim:et ts=2 sw=2
#+end_src

Then add the following to ~init.vim~:

#+begin_src viml
lua << EOF
require('lspconfig/prolog_lsp')
require('lspconfig').prolog_lsp.setup{}
EOF
#+end_src

* LazyVim

** stdio
Create the following file in ~$XDG_CONFIG_DIR/nvim/lua/plugins/lsp.lua~

#+begin_src lua
return {
  {
    "neovim/nvim-lspconfig",
    opts = {
      servers = {
        prolog = {},
      },
      setup = {
        prolog = function(_, opts)
          local lspconfig = require("lspconfig")
          local configs = require("lspconfig.configs")
          local util = require("lspconfig.util")

          local root_files = { ".git", "pack.pl" }

          if not configs.prolog then
            configs.prolog = {
              default_config = {
                cmd = {
                  "swipl",
                  "-g",
                  "use_module(library(lsp_server)).",
                  "-g",
                  "lsp_server:main",
                  "-t",
                  "halt",
                  "--",
                  "stdio",
                },
                filetypes = { "prolog" },
                single_file_support = true,
                root_dir = util.root_pattern(unpack(root_files)),
                settings = {},
              },
              commands = {},
              docs = {
                description = [[
              Prolog LSP server
              ]],
              },
            }
          end
          lspconfig.prolog.setup(opts)
        end,
      },
    },
  },
}
#+end_src

* VSCode

Choose one from the list below:

  - download the latest ~.vsix~ file from the [[https://github.com/jamesnvc/lsp_server/releases][releases page]]
  - clone this repo and copy/symlink the ~vscode/~ directory to ~~/.vscode/extensions/~
  - clone and build the ~.vsix~ file yourself by the follwing steps:
    1. ~cd /path/to/clone/vscode~
    2. ~npm install~
    3. ~npx vsce package~
    4. add the resulting ~.vsix~ to VSCode by clicking the ~...~ at the top right of the "Extensions" panel then selecting ~Install from VSIX...~


* Helix

Helix already includes configuration for this Prolog LSP server, so it should mostly Just Work.

However, the default configuration gives the '.pl' extension to perl, so to avoid having to manually do ~:set-language prolog~ each time, you can add the following to ~$XDG_CONFIG/helix/languages.toml~ to remove Perl's association with that extension:

#+begin_src toml
[[language]]
name = "perl"
file-types = ["perl"]
#+end_src
* Kate

Open settings, go to ~LSP Client~, select the ~User Server Settings~ tab, and add the following content (or merge with your existing settings if present):

#+begin_src json
{
  "servers": {
    "prolog": {
      "command": ["swipl", "-O",
                  "-g", "use_module(library(lsp_server))",
                  "-g", "lsp_server:main",
                  "-t", "halt",
                  "--", "stdio"],
      "url": "https://github.com/jamesnvc/lsp_server",
      "root": ""
      "highlightingModeRegex": "^Prolog$"
    }
  }
}
#+end_src

Not written in Markdown, so it's shown here as plain text — view it formatted on GitHub.

Contributors

jamesnvc

484 commits

eignnx

27 commits

Languages

Prolog

99.0%