Skip to content

A dynamic, project-aware LSP dispatcher for Eglot, designed to support multiple language server configurations for the same major mode — including simultaneous LSPs via lspx.

License

Notifications You must be signed in to change notification settings

Askath/eglot-dispatcher

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 

Repository files navigation

eglot-dispatcher

A dynamic, project-aware LSP dispatcher for Eglot, designed to support multiple language server configurations for the same major mode — including simultaneous LSPs via LSPX.


🧠 Motivation

Eglot is a lightweight and elegant client for Language Server Protocol (LSP), but it assumes a one-to-one mapping between a major mode and a single language server. In practice, however, real-world projects often require more flexibility:

  • Different LSPs for the same language (e.g., TypeScript projects that use Angular, React, or no framework at all)
  • Running multiple LSPs simultaneously for the same buffer (e.g., a language server plus a linter)
  • Project-specific configurations that vary based on files like angular.json, package.json, or vite.config.ts

eglot-dispatcher solves this by letting you define a declarative list of strategies that determine which LSP(s) to launch based on file type and project context — all without modifying eglot itself.


✨ Features

  • 🔄 Dynamically chooses the correct LSP for a file based on project structure
  • 🧠 Supports multiple major modes per strategy
  • 🔁 Automatically runs multiple LSPs per buffer via lspx
  • 🔍 Supports :initializationOptions, :command, and other extended configuration options
  • 💡 Clean, declarative interface designed for scalability and reuse

📦 Installation

Clone the repo into your load-path:

git clone https://github.com/Askath/eglot-dispatcher ~/emacs.d/lisp/eglot-dispatcher

Then in your Emacs configuration:

(add-to-list 'load-path "~/emacs.d/lisp/eglot-dispatcher")
(require 'eglot-dispatcher)

🛠️ Usage

Hook it into relevant modes

(add-hook 'typescript-ts-mode-hook #'eglot-dispatcher-dispatch)
(add-hook 'html-ts-mode-hook #'eglot-dispatcher-dispatch)

Define your LSP strategies

(setq eglot-strategies-list
	  '((:name "Angular + ESLint"
		 :modes (typescript-ts-mode html-ts-mode)
		 :project-root "angular.json"
		 :command (lambda (root)
					(list
					 `("npx" "ngserver"
					   "--stdio"
					   "--tsProbeLocations" ,(expand-file-name "node_modules" root)
					   "--ngProbeLocations" ,(expand-file-name "node_modules" root))
					 '("vscode-eslint-language-server" "--stdio"))))

		(:name "React"
		 :modes (typescript-ts-mode)
		 :project-root "package.json"
		 :predicate (lambda (root)
					  (file-exists-p (expand-file-name "node_modules/react" root)))
		 :command (lambda (_root)
					'("typescript-language-server" "--stdio")))

		(:name "Default TypeScript"
		 :modes (typescript-ts-mode)
		 :command (lambda (_root)
					'("typescript-language-server" "--stdio")))))

📋 Strategy Fields

Field Description
:name Strategy name (used for logging/debugging)
:modes List of major modes this strategy applies to
:project-root A filename to search upward for to detect the project root
:predicate (optional) A function (fn root) that returns non-nil if the strategy should be used
:command A function (fn root) that returns:
  • A single command list (e.g. ("tsserver" "--stdio"))
  • A plist with :command, :initializationOptions, etc.
  • A list of command lists → triggers lspx

🔁 Multiplexing with lspx

If your command function returns multiple command lists, eglot-dispatcher automatically invokes lspx to run them in parallel:

:command (lambda (_root)
		   (list
			'("angular-language-server" "--stdio")
			'("eslint-language-server" "--stdio")))

This becomes:

("lspx" "--lsp" "angular-language-server --stdio"
		"--lsp" "eslint-language-server --stdio")

⚙️ Initialization Options Example

You can specify Eglot-compatible initialization options like so:

:command (lambda (_root)
		   '(:command ("typescript-language-server" "--stdio")
			 :initializationOptions (:tsserver (:logVerbosity "verbose"))))

✅ Use Cases

  • Use angular-language-server + eslint-language-server in Angular projects
  • Use typescript-language-server in React projects with framework-specific flags
  • Fall back to the default TypeScript server in generic projects
  • Extend to Vue, Python, Go, or any other Eglot-supported language

📄 License

MIT


🤝 Contributing

Feedback and contributions are very welcome. Please feel free to:

  • Report issues
  • Suggest new strategies or use cases
  • Contribute improvements or enhancements

About

A dynamic, project-aware LSP dispatcher for Eglot, designed to support multiple language server configurations for the same major mode — including simultaneous LSPs via lspx.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published