Skip to content

konradmalik/flint-ls

Repository files navigation

flint-ls

Actions Status

General purpose Language Server that can spawn formatters and linters.

linting

Description

This is a fork of efm-langserver that will maintain and develop separately. It is a cleaned up and simplified version of the original. It supports a subset of original configuration - 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)
  • all formatters must support stdin, non-stdin formatters won't work. Option formatStdin was removed.
  • fixed behavior of LintIgnoreExitCode - when true, output is parsed for errors even if exit code is 0. Previously each lint command that resulted in exit code 0 was considered a problem, but exit code 0 is ok in situations when there's no lint issues.
  • 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
  • added Lsp Progress notifications
  • removed RootMarkers from root settings. They can only be provided per language now. The use of this was questionable.

Sections

Installation

go install github.com/konradmalik/flint-ls@latest

or via nix. Flake provided in this repo.

nix build

Usage

Usage of flint-ls:
  -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.

flint-ls 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": {
        "languages": {
            "lua": {
                "formatCommand": "lua-format -i"
            }
        }
    }
}

Full config

type Config struct {
	Languages      *map[string][]Language `json:"languages,omitempty"`
	LintDebounce   time.Duration          `json:"lintDebounce,omitempty"`
	FormatDebounce time.Duration          `json:"formatDebounce,omitempty"`
}

type Language struct {
	Env           []string `json:"env,omitempty"`
	RootMarkers   []string `json:"rootMarkers,omitempty"`
	RequireMarker bool     `json:"requireMarker,omitempty"`
	// prefix for lint message
	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"`
}

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

All formatters must support stdin. When a formatter uses non-stdin in replaces file contents on disk which leads to confusing and unpredictable results.

Client Setup

Configuration for neovim builtin LSP with nvim-lspconfig

Neovim's built-in LSP client sends DidChangeConfiguration.

require "lspconfig".flint_ls.setup {
    init_options = {documentFormatting = true},
    settings = {
        languages = {
            lua = {
                {formatCommand = "lua-format -i"}
            }
        }
    }
}

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"}
}

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

Configuration for coc.nvim

coc-settings.json

  // languageserver
  "languageserver": {
    "flint-ls": {
      "command": "flint-ls",
      "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/flint-ls",
    "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.flint-ls]
command = "flint-ls"

[[language]]
name = "typescript"
language-servers = [
  { name = "flint-ls", 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": {
	    "flint-ls": {
	      "enabled": true,
	      "command": ["flint-ls"],
	      "selector": "source.c | source.php | source.python" // see https://www.sublimetext.com/docs/3/selectors.html
	    }
  	}
}

Configuration for vim-lsp

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

vim-lsp-settings provide installer for flint-ls.

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 . ("flint-ls"))))

License

MIT

Authors

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

About

A language server for formatting and linting

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Contributors