Skip to content

konradmalik/efm-langserver

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

efm-langserver

Actions Status

General purpose Language Server that can spawn formatters and linters.

linting

Description

This is a fork efm-langserver that will maintain and develop separately. It is a cleaned up and simplified version of the original. It supports the original configuration but only for formatting and linting. No code actions, completions, hover etc.

Notable changes from the original:

  • no config.yaml, settings need to be passed via DidChangeConfiguration
  • only linting and formatting (for now)
  • better diffs handling for formatting (no more "format twice to remove an extra newline")
    • external, maintained diff library used for that
  • support for errorformat's end line and end column
  • added tests (always in progress)
  • refactored, cleaned and more maintainable code (always in progress)
  • fixed and applied sane defaults for options like LintAfterOpen, LintOnSave etc.
  • removed explicit support for LintWorkspace (linters that lint the whole workspace and do not need filename)
    • it may be implemented back in the future if needed, but I've no usage of such linters, and a quick search through creativenull/efmls-configs-nvim showed no usage of this property
    • tracked in #11

Sections

Installation

go install github.com/konradmalik/efm-langserver@latest

or via nix. Flake provided in this repo.

nix build

Usage

Usage of efm-langserver:
  -h    Show help
  -logfile string
        File to save logs into. If provided stderr won't be used anymore.
  -loglevel int
        Set the log level. Max is 5, min is 0. (default 1)
  -q    Run quiet
  -v    Print the version

Configuration

Configuration can be done through a DidChangeConfiguration notification from the client. DidChangeConfiguration can be called any time and will overwrite only provided properties (note though that per language configuration will be overwritten as a whole array).

DidChangeConfiguration cannot set LogFile.

efm-langserver does not include formatters/linters for any language. You must install these manually, e.g.

InitializeParams

Because the configuration can be updated on the fly, capabilities might change throughout the lifetime of the server. To enable support for capabilities that will be available later, set them in the InitializeParams

Example

{
    "initializationOptions": {
        "documentFormatting": true,
        "documentRangeFormatting": true
    }
}

Example for DidChangeConfiguration notification

{
    "settings": {
        "rootMarkers": [".git/"],
        "languages": {
            "lua": {
                "formatCommand": "lua-format -i",
                "formatStdin": true
            }
        }
    }
}

Full config

type Config struct {
	Version        int                    `json:"version,omitempty"`
	LogLevel       int                    `json:"logLevel,omitempty"`
	Languages      *map[string][]Language `json:"languages,omitempty"`
	RootMarkers    *[]string              `json:"rootMarkers,omitempty"`
	LintDebounce   time.Duration          `json:"lintDebounce,omitempty"`
	FormatDebounce time.Duration          `json:"formatDebounce,omitempty"`
}

type Language struct {
	Prefix             string             `json:"prefix,omitempty"`
	LintFormats        []string           `json:"lintFormats,omitempty"`
	LintStdin          bool               `json:"lintStdin,omitempty"`
	// warning: this will be subtracted from the line reported by the linter
	LintOffset         int                `json:"lintOffset,omitempty"`
 	// warning: this will be added to the column reported by the linter
	LintOffsetColumns  int                `json:"lintOffsetColumns,omitempty"`
	LintCommand        string             `json:"lintCommand,omitempty"`
	LintIgnoreExitCode bool               `json:"lintIgnoreExitCode,omitempty"`
	LintCategoryMap    map[string]string  `json:"lintCategoryMap,omitempty"`
	LintSource         string             `json:"lintSource,omitempty"`
	LintSeverity       DiagnosticSeverity `json:"lintSeverity,omitempty"`
	// defaults to true if not provided as a sanity default
	LintAfterOpen *bool `json:"lintAfterOpen,omitempty"`
	// defaults to true if not provided as a sanity default
	LintOnChange *bool `json:"lintOnChange,omitempty"`
	// defaults to true if not provided as a sanity default
	LintOnSave     *bool    `json:"lintOnSave,omitempty"`
	FormatCommand  string   `json:"formatCommand,omitempty"`
	FormatCanRange bool     `json:"formatCanRange,omitempty"`
	FormatStdin    bool     `json:"formatStdin,omitempty"`
	Env            []string `json:"env,omitempty"`
	RootMarkers    []string `json:"rootMarkers,omitempty"`
	RequireMarker  bool     `json:"requireMarker,omitempty"`
}

Also note that there's a wildcard for language name =. So if you want to define some config entry for all languages, you can use = as a key.

Formatting

Note that while it's always possible to use multiple formatters, only formatters with formatStdin will work in such cases. If a formatter formats file on disk, multiple formatters will simply always use the original text. When formatStdin is true, then formatters can use previous formatter output as input which does what we want.

The first formatter can always be non-stdin.

Client Setup

Configuration for neovim builtin LSP with nvim-lspconfig

Neovim's built-in LSP client sends DidChangeConfiguration.

init.lua example (settings follows schema.md):

require "lspconfig".efm.setup {
    init_options = {documentFormatting = true},
    settings = {
        rootMarkers = {".git/"},
        languages = {
            lua = {
                {formatCommand = "lua-format -i", formatStdin = true}
            }
        }
    }
}

You can get premade tool definitions from creativenull/efmls-configs-nvim:

lua = {
  require('efmls-configs.linters.luacheck'),
  require('efmls-configs.formatters.stylua'),
}

If you define your own, make sure to define as a table of tables:

lua = {
    {formatCommand = "lua-format -i", formatStdin = true}
}

-- and for multiple formatters, add to the table
lua = {
    {formatCommand = "lua-format -i", formatStdin = true},
    {formatCommand = "lua-pretty -i"}
}

Configuration for coc.nvim

coc-settings.json

  // languageserver
  "languageserver": {
    "efm": {
      "command": "efm-langserver",
      "args": [],
      "filetypes": ["vim", "eruby", "markdown", "yaml"]
    }
  },

Configuration for VSCode

Generic LSP Client for VSCode

Example settings.json (change to fit your local installs):

{
    "glspc.languageId": "lua",
    "glspc.serverCommand": "/Users/me/.local/share/nvim/mason/bin/efm-langserver",
    "glspc.pathPrepend": "/Users/me/.local/share/rtx/installs/python/3.11.4/bin:/Users/me/.local/share/rtx/installs/node/20.3.1/bin"
}

Configuration for Helix

~/.config/helix/languages.toml

[language-server.efm]
command = "efm-langserver"

[[language]]
name = "typescript"
language-servers = [
  { name = "efm", only-features = [ "diagnostics", "format" ] },
  { name = "typescript-language-server", except-features = [ "format" ] }
]

Configuration for SublimeText LSP

Open Preferences: LSP Settings command from the Command Palette (Ctrl+Shift+P)

{
	"clients": {
	    "efm-langserver": {
	      "enabled": true,
	      "command": ["efm-langserver"],
	      "selector": "source.c | source.php | source.python" // see https://www.sublimetext.com/docs/3/selectors.html
	    }
  	}
}

Configuration for vim-lsp

augroup LspEFM
  au!
  autocmd User lsp_setup call lsp#register_server({
      \ 'name': 'efm-langserver',
      \ 'cmd': {server_info->['efm-langserver']},
      \ 'allowlist': ['vim', 'eruby', 'markdown', 'yaml'],
      \ })
augroup END

vim-lsp-settings provide installer for efm-langserver.

Configuration for Eglot (Emacs)

Add to eglot-server-programs with major mode you want.

(with-eval-after-load 'eglot
  (add-to-list 'eglot-server-programs
    `(markdown-mode . ("efm-langserver"))))

License

MIT

Authors

  • Yasuhiro Matsumoto (a.k.a. mattn) before 2025-04-29 (original efm-langserver author)
  • Konrad Malik after 2025-04-29 (author and maintainer of this fork)

About

General purpose Language Server

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 44