aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWilliam Boman <william@redwill.se>2022-07-21 14:15:11 +0200
committerWilliam Boman <william@redwill.se>2022-07-22 03:03:23 +0200
commit1d459b6d19118b9944d5313e4439cb361d607366 (patch)
treed48a07eaa5fddebc75ade8bb1d3861992e0c11a3
downloadmason-lspconfig-1d459b6d19118b9944d5313e4439cb361d607366.tar
mason-lspconfig-1d459b6d19118b9944d5313e4439cb361d607366.tar.gz
mason-lspconfig-1d459b6d19118b9944d5313e4439cb361d607366.tar.bz2
mason-lspconfig-1d459b6d19118b9944d5313e4439cb361d607366.tar.lz
mason-lspconfig-1d459b6d19118b9944d5313e4439cb361d607366.tar.xz
mason-lspconfig-1d459b6d19118b9944d5313e4439cb361d607366.tar.zst
mason-lspconfig-1d459b6d19118b9944d5313e4439cb361d607366.zip
init
-rw-r--r--.editorconfig10
-rw-r--r--.github/FUNDING.yml13
-rw-r--r--.github/ISSUE_TEMPLATE/config.yml4
-rw-r--r--.github/ISSUE_TEMPLATE/feature_request.yaml32
-rw-r--r--.github/ISSUE_TEMPLATE/general_issue.yaml75
-rw-r--r--.github/workflows/autogenerate.yml42
-rw-r--r--.github/workflows/check-generated-code-state.yml29
-rw-r--r--.github/workflows/stylua.yml20
-rw-r--r--.github/workflows/tests.yml19
-rw-r--r--.gitignore2
-rw-r--r--LICENSE174
-rw-r--r--Makefile28
-rw-r--r--README.md103
-rw-r--r--doc/mason-lspconfig.txt206
-rw-r--r--lua/mason-lspconfig/api/command.lua150
-rw-r--r--lua/mason-lspconfig/init.lua174
-rw-r--r--lua/mason-lspconfig/mappings/filetype.lua162
-rw-r--r--lua/mason-lspconfig/mappings/server.lua127
-rw-r--r--lua/mason-lspconfig/server_configurations/angularls/init.lua39
-rw-r--r--lua/mason-lspconfig/server_configurations/apex_ls/init.lua8
-rw-r--r--lua/mason-lspconfig/server_configurations/bicep/init.lua5
-rw-r--r--lua/mason-lspconfig/server_configurations/bsl_ls/init.lua5
-rw-r--r--lua/mason-lspconfig/server_configurations/elixirls/init.lua5
-rw-r--r--lua/mason-lspconfig/server_configurations/esbonio/init.lua5
-rw-r--r--lua/mason-lspconfig/server_configurations/groovyls/init.lua3
-rw-r--r--lua/mason-lspconfig/server_configurations/julials/README.md9
-rw-r--r--lua/mason-lspconfig/server_configurations/julials/init.lua50
-rw-r--r--lua/mason-lspconfig/server_configurations/kotlin_language_server/init.lua5
-rw-r--r--lua/mason-lspconfig/server_configurations/ltex/init.lua5
-rw-r--r--lua/mason-lspconfig/server_configurations/omnisharp/README.md16
-rw-r--r--lua/mason-lspconfig/server_configurations/omnisharp/init.lua5
-rw-r--r--lua/mason-lspconfig/server_configurations/perlnavigator/init.lua5
-rw-r--r--lua/mason-lspconfig/server_configurations/powershell_es/init.lua6
-rw-r--r--lua/mason-lspconfig/server_configurations/psalm/init.lua5
-rw-r--r--lua/mason-lspconfig/server_configurations/pylsp/README.md16
-rw-r--r--lua/mason-lspconfig/server_configurations/pylsp/init.lua51
-rw-r--r--lua/mason-lspconfig/server_configurations/r_language_server/init.lua5
-rw-r--r--lua/mason-lspconfig/server_configurations/rescriptls/init.lua5
-rw-r--r--lua/mason-lspconfig/server_configurations/solang/init.lua14
-rw-r--r--lua/mason-lspconfig/server_configurations/tflint/README.md14
-rw-r--r--lua/mason-lspconfig/server_configurations/visualforce_ls/init.lua5
-rw-r--r--lua/mason-lspconfig/server_configurations/volar/init.lua27
-rw-r--r--lua/mason-lspconfig/settings.lua27
-rw-r--r--lua/mason-lspconfig/win-exepath-compat.lua82
-rw-r--r--scripts/lua/mason-scripts/mason-lspconfig/generate.lua54
-rw-r--r--scripts/lua/mason-scripts/utils.lua21
-rwxr-xr-xscripts/nvim.sh14
-rw-r--r--stylua.toml2
-rw-r--r--tests/helpers/lua/dummy2_package.lua14
-rw-r--r--tests/helpers/lua/dummy_package.lua14
-rw-r--r--tests/helpers/lua/luassertx.lua70
-rw-r--r--tests/helpers/lua/test_helpers.lua49
-rw-r--r--tests/mason-lspconfig/api/command_spec.lua71
-rw-r--r--tests/mason-lspconfig/setup_spec.lua141
-rw-r--r--tests/minimal_init.vim39
55 files changed, 2281 insertions, 0 deletions
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..8fe3efd
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,10 @@
+root = true
+
+[*]
+indent_style = space
+indent_size = 4
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace=true
+max_line_length = 120
+charset = utf-8
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 0000000..2924f6e
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,13 @@
+# These are supported funding model platforms
+
+github: [williamboman] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
+patreon: # Replace with a single Patreon username
+open_collective: # Replace with a single Open Collective username
+ko_fi: # Replace with a single Ko-fi username
+tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
+community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
+liberapay: # Replace with a single Liberapay username
+issuehunt: # Replace with a single IssueHunt username
+otechie: # Replace with a single Otechie username
+lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
+custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 0000000..70db94b
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,4 @@
+contact_links:
+ - name: Ask a question about mason.nvim or get support
+ url: https://github.com/williamboman/mason.nvim/discussions/new?category=q-a
+ about: Ask a question or request support for using mason.nvim
diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml
new file mode 100644
index 0000000..de4fe3a
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.yaml
@@ -0,0 +1,32 @@
+name: Feature request
+description: Suggest an idea for this project
+labels:
+ - enhancement
+
+body:
+ - type: markdown
+ attributes:
+ value: |
+ 👋! This is not an issue template for questions! If you have questions, please refer to https://github.com/williamboman/mason.nvim/discussions/categories/q-a :)
+
+ Before filing an issue, make sure that you meet the minimum requirements mentioned in the README.
+
+ - type: textarea
+ attributes:
+ label: Is your feature request related to a problem? Please describe.
+ validations:
+ required: true
+
+ - type: textarea
+ attributes:
+ label: Describe the solution you'd like
+ validations:
+ required: true
+
+ - type: textarea
+ attributes:
+ label: Describe potential alternatives you've considered
+
+ - type: textarea
+ attributes:
+ label: Additional context
diff --git a/.github/ISSUE_TEMPLATE/general_issue.yaml b/.github/ISSUE_TEMPLATE/general_issue.yaml
new file mode 100644
index 0000000..c1d122b
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/general_issue.yaml
@@ -0,0 +1,75 @@
+name: Issue
+description: Report an issue with mason-lspconfig.nvim
+
+body:
+ - type: markdown
+ attributes:
+ value: |
+ 👋! This is not an issue template for questions! If you have questions, please refer to https://github.com/williamboman/mason.nvim/discussions/categories/q-a :)
+
+ Before filing an issue, make sure that you meet the minimum requirements mentioned in the README.
+
+ - type: textarea
+ attributes:
+ label: Problem description
+ description: A clear and concise description of what the issue is and why you think it's an issue with mason.nvim.
+ validations:
+ required: true
+
+ - type: textarea
+ attributes:
+ label: "Neovim version (>= 0.7)"
+ description: "Output of `nvim --version`"
+ placeholder: |
+ NVIM v0.7.0-dev
+ Build type: Release
+ LuaJIT 2.1.0-beta3
+ validations:
+ required: true
+
+ - type: input
+ attributes:
+ label: "Operating system/version"
+ description: "On Linux and Mac systems: `$ uname -a`"
+ validations:
+ required: true
+
+ - type: checkboxes
+ attributes:
+ label: I've recently downloaded the latest plugin version of mason.nvim & mason-lspconfig.nvim
+ options:
+ - label: "Yes"
+
+ - type: input
+ attributes:
+ label: Affected packages
+ description: If this issue is specific to one or more packages, list them here. If not, write 'all'.
+ validations:
+ required: true
+
+ - type: textarea
+ attributes:
+ label: Actual behavior
+ description: A short description of what's happening.
+ validations:
+ required: true
+
+ - type: textarea
+ attributes:
+ label: Expected behavior
+ description: A short description of the behavior you expected.
+ validations:
+ required: true
+
+ - type: textarea
+ attributes:
+ label: Healthcheck output
+ placeholder: ":checkhealth mason"
+ render: shell
+ validations:
+ required: true
+
+ - type: textarea
+ attributes:
+ label: Screenshots
+ description: If applicable, add screenshots to help explain your problem
diff --git a/.github/workflows/autogenerate.yml b/.github/workflows/autogenerate.yml
new file mode 100644
index 0000000..b148513
--- /dev/null
+++ b/.github/workflows/autogenerate.yml
@@ -0,0 +1,42 @@
+name: Autogenerate code
+
+on:
+ workflow_dispatch:
+ schedule:
+ - cron: "0 10 * * *"
+
+jobs:
+ autogenerate:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - uses: rhysd/action-setup-vim@v1
+ with:
+ neovim: true
+ version: v0.7.0
+
+ - name: make generate
+ run: make generate
+
+ - name: Create Pull Request
+ id: cpr
+ uses: peter-evans/create-pull-request@v4
+ with:
+ token: ${{ secrets.PAT }}
+ author: "William Botman <william+bot@redwill.se>"
+ committer: "William Botman <william+bot@redwill.se>"
+ add-paths: lua/mason-lspconfig
+ commit-message: "chore: update generated code"
+ branch: chore/generate
+ branch-suffix: short-commit-hash
+ delete-branch: true
+ labels: automerge
+ title: "chore: update generated code"
+
+ - name: Enable Pull Request Automerge
+ if: steps.cpr.outputs.pull-request-operation == 'created'
+ uses: peter-evans/enable-pull-request-automerge@v2
+ with:
+ token: ${{ secrets.PAT }}
+ pull-request-number: ${{ steps.cpr.outputs.pull-request-number }}
+ merge-method: squash
diff --git a/.github/workflows/check-generated-code-state.yml b/.github/workflows/check-generated-code-state.yml
new file mode 100644
index 0000000..99abfe2
--- /dev/null
+++ b/.github/workflows/check-generated-code-state.yml
@@ -0,0 +1,29 @@
+name: Check generated code state
+
+on:
+ push:
+ branches:
+ - "main"
+ pull_request:
+
+jobs:
+ check-generated-code-state:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - uses: rhysd/action-setup-vim@v1
+ with:
+ neovim: true
+ version: v0.7.0
+
+ - name: make generate
+ run: make generate
+
+ - name: Ensure there are no diffs
+ run: |
+ git update-index -q --refresh
+ git diff
+ git diff-index --exit-code --quiet HEAD -- || {
+ echo '::error::Generated code is not up to date, run "make generate".';
+ exit 1;
+ }
diff --git a/.github/workflows/stylua.yml b/.github/workflows/stylua.yml
new file mode 100644
index 0000000..e28130b
--- /dev/null
+++ b/.github/workflows/stylua.yml
@@ -0,0 +1,20 @@
+name: Stylua check
+
+on:
+ push:
+ branches:
+ - "main"
+ pull_request:
+
+jobs:
+ stylua:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - name: Run Stylua check
+ uses: JohnnyMorganz/stylua-action@1.0.0
+ with:
+ # token is needed because the action allegedly downloads binary from github releases
+ token: ${{ secrets.GITHUB_TOKEN }}
+ # CLI arguments
+ args: --check .
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
new file mode 100644
index 0000000..68c2e21
--- /dev/null
+++ b/.github/workflows/tests.yml
@@ -0,0 +1,19 @@
+name: Tests
+
+on:
+ push:
+ branches:
+ - "main"
+ pull_request:
+
+jobs:
+ tests:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - uses: rhysd/action-setup-vim@v1
+ with:
+ neovim: true
+ version: v0.7.0
+ - name: Run tests
+ run: make test
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..68422d0
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+/dependencies
+/tests/fixtures/mason
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..dd5b3a5
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,174 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..5c4a527
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,28 @@
+INSTALL_ROOT_DIR:=$(shell pwd)/tests/fixtures/mason
+NVIM_HEADLESS:=nvim --headless --noplugin -u tests/minimal_init.vim
+
+dependencies:
+ git clone --depth 1 https://github.com/williamboman/mason.nvim dependencies/pack/vendor/start/mason.nvim
+ git clone --depth 1 https://github.com/nvim-lua/plenary.nvim dependencies/pack/vendor/start/plenary.nvim
+ git clone --depth 1 https://github.com/neovim/nvim-lspconfig dependencies/pack/vendor/start/nvim-lspconfig
+
+.PHONY: clean_dependencies
+clean_dependencies:
+ rm -rf dependencies
+
+.PHONY: clean_fixtures
+clean_fixtures:
+ rm -rf "${INSTALL_ROOT_DIR}"
+
+.PHONY: clean
+clean: clean_fixtures clean_dependencies
+
+.PHONY: test
+test: clean_fixtures dependencies
+ INSTALL_ROOT_DIR=${INSTALL_ROOT_DIR} $(NVIM_HEADLESS) -c "call RunTests()"
+
+.PHONY: generate
+generate: dependencies
+ ./scripts/nvim.sh scripts/lua/mason-scripts/mason-lspconfig/generate.lua
+
+# vim:noexpandtab
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..6ed8395
--- /dev/null
+++ b/README.md
@@ -0,0 +1,103 @@
+[![GitHub CI](https://github.com/williamboman/mason-lspconfig.nvim/workflows/Tests/badge.svg)](https://github.com/williamboman/mason-lspconfig.nvim/actions?query=workflow%3ATests+branch%3Amain+event%3Apush)
+![Platforms](https://img.shields.io/badge/platform-linux%20macOS%20windows-blue)
+![Repository size](https://img.shields.io/github/repo-size/williamboman/mason-lspconfig.nvim)
+[![Sponsors](https://img.shields.io/github/sponsors/williamboman?style=flat-square)](https://github.com/sponsors/williamboman)
+
+# mason-lspconfig.nvim
+
+<p align="center">
+ <code>mason-lspconfig</code> bridges <a
+ href="https://github.com/williamboman/mason.nvim"><code>mason.nvim</code></a> with the <a
+ href="https://github.com/neovim/nvim-lspconfig"><code>lspconfig</code></a> plugin - making it easier to use the both
+ plugins together.
+</p>
+
+# Table of Contents
+
+- [Introduction](#introduction)
+- [Requirements](#requirements)
+- [Installation](#installation)
+- [Setup](#setup)
+- [Commands](#commands)
+- [Configuration](#configuration)
+
+# Introduction
+
+`mason-lspconfig.nvim` registers a setup hook with `lspconfig` that ensures servers installed with `mason.nvim` are set
+up with the necessary extra configuration. It also provides extra convenience commands such as `:LspInstall`. Lastly, it
+also provides convenience APIs that allow you to use mason.nvim as the main control for dynamically setting up installed
+servers on an ad-hoc basis.
+
+It is recommended to use this extension if you use `mason.nvim` and `lspconfig`.
+
+# Requirements
+
+- neovim `>= 0.7.0`
+- `mason.nvim`
+- `lspconfig`
+
+# Installation
+
+## [Packer](https://github.com/wbthomason/packer.nvim)
+
+```lua
+use {
+ { "williamboman/mason.nvim", branch = "alpha" },
+ "williamboman/mason-lspconfig.nvim",
+ "neovim/nvim-lspconfig",
+}
+```
+
+## vim-plug
+
+```vim
+Plug "williamboman/mason.nvim", { 'branch': 'alpha' }
+Plug "williamboman/mason-lspconfig.nvim"
+Plug "neovim/nvim-lspconfig"
+```
+
+# Setup
+
+```lua
+require("mason").setup()
+require("mason-lspconfig").setup()
+```
+
+Refer to the [Configuration](#configuration) section for information about which settings are available.
+
+# Commands
+
+- `:LspInstall [<server>...]` - installs the provided servers
+- `:LspUninstall <server> ...` - uninstalls the provided servers
+
+# Configuration
+
+You may optionally configure certain behavior of `mason-lspconfig.nvim` when calling the `.setup()` function. Refer to
+the [default configuration](#default-configuration) for a list of all available settings.
+
+Example:
+
+```lua
+require("mason-lspconfig").setup({
+ ensure_installed = { "sumneko_lua", "rust_analyzer" }
+})
+```
+
+## Default configuration
+
+```lua
+local DEFAULT_SETTINGS = {
+ -- A list of servers to automatically install if they're not already installed. Example: { "rust_analyzer@nightly", "sumneko_lua" }
+ -- This setting has no relation with the `automatic_installation` setting.
+ ensure_installed = {},
+
+ -- Whether servers that are set up (via lspconfig) should be automatically installed if they're not already installed.
+ -- This setting has no relation with the `ensure_installed` setting.
+ -- Can either be:
+ -- - false: Servers are not automatically installed.
+ -- - true: All servers set up via lspconfig are automatically installed.
+ -- - { exclude: string[] }: All servers set up via lspconfig, except the ones provided in the list, are automatically installed.
+ -- Example: automatic_installation = { exclude = { "rust_analyzer", "solargraph" } }
+ automatic_installation = false,
+}
+```
diff --git a/doc/mason-lspconfig.txt b/doc/mason-lspconfig.txt
new file mode 100644
index 0000000..20392b3
--- /dev/null
+++ b/doc/mason-lspconfig.txt
@@ -0,0 +1,206 @@
+*mason-lspconfig*
+
+Minimum version of neovim: 0.7.0
+
+Author: William Boman
+
+==============================================================================
+INTRODUCTION *mason-lspconfig-introduction*
+
+`mason-lspconfig` bridges `mason.nvim` with the `lspconfig`[1] plugin - making
+it easier to use the both plugins together.
+
+It is recommended to use this extension if you use `lspconfig`.
+
+`mason-lspconfig.nvim` registers a setup hook with `lspconfig` that ensures
+servers installed with `mason.nvim` are set up with the necessary extra
+configuration. It also provides extra convenience commands such as
+`:LspInstall`. Lastly, it also provides convenience APIs that allow you to use
+mason.nvim as the main control for dynamically setting up installed servers on
+an ad-hoc basis.
+
+[1] `lspconfig`: https://github.com/neovim/nvim-lspconfig
+
+==============================================================================
+REQUIREMENTS *mason-lspconfig-requirements*
+
+`mason-lspconfig` requires `mason.nvim` & `lspconfig` to be installed. Note
+that `lspconfig` needs to be available when setting up `mason-lspconfig` - so
+if you lazy load either plugin make sure `lspconfig` is not loaded after
+`mason-lspconfig`.
+
+Also, make sure not to set up any servers via `lspconfig` _before_ calling
+`mason-lspconfig`'s setup function.
+
+==============================================================================
+QUICK START *mason-lspconfig-quickstart*
+
+The only thing needed to enable the `mason-lspconfig` plugin is to make sure
+to call the `setup()` function:
+
+ require("mason").setup()
+ require("mason-lspconfig").setup()
+
+==============================================================================
+COMMANDS *mason-lspconfig-commands*
+
+ *:LspInstall*
+:LspInstall [<server>...]
+
+Installs the provided servers. This command only accepts servers that have a
+corresponding server configuration in `lspconfig`.
+
+You may also provide a language, like `:LspInstall typescript`. This will
+prompt you with a selection of all available servers for that given language.
+
+When the command is ran without any arguments, the currently active buffer's
+'filetype' will be used to identify relevant servers, and you will be prompted
+with a selection of all suggested servers.
+
+ *:LspUninstall*
+:LspUninstall <server> ...
+
+Uninstalls the provided servers.
+
+==============================================================================
+SETTINGS *mason-lspconfig-settings*
+
+You can configure certain behavior of `mason-lspconfig` when calling the
+`.setup()` function.
+
+Refer to |mason-lspconfig-default-settings| for all available settings.
+
+Example:
+
+ require("mason-lspconfig").setup({
+ ensure_installed = { "rust_analyzer", "tsserver" }
+ })
+
+ *mason-lspconfig-default-settings*
+
+ local DEFAULT_SETTINGS = {
+ -- A list of servers to automatically install if they're not already installed. Example: { "rust-analyzer@nightly", "sumneko_lua" }
+ -- This setting has no relation with the `automatic_installation` setting.
+ ensure_installed = {},
+
+ -- Whether servers that are set up (via lspconfig) should be automatically installed if they're not already installed.
+ -- This setting has no relation with the `ensure_installed` setting.
+ -- Can either be:
+ -- - false: Servers are not automatically installed.
+ -- - true: All servers set up via lspconfig are automatically installed.
+ -- - { exclude: string[] }: All servers set up via lspconfig, except the ones provided in the list, are automatically installed.
+ -- Example: automatic_installation = { exclude = { "rust_analyzer", "solargraph" } }
+ automatic_installation = false,
+ }
+
+==============================================================================
+DYNAMIC SERVER SETUP *mason-lspconfig-dynamic-server-setup*
+
+`mason-lspconfig` provides extra opt-in functionality that allow you to set up
+LSP servers installed with `mason.nvim` without having to manually edit your
+Neovim configuration for every single server you want to use. This is also
+convenient if you want to use `mason.nvim` as the main control mechanism for
+which servers to set up. It also makes it possible to use newly installed
+servers without having to restart Neovim! Example:
+
+ require("mason").setup()
+ require("mason-lspconfig").setup()
+ require("mason-lspconfig").setup_handlers {
+ -- The first entry (without a key) will be the default handler
+ -- and will be called for each installed server that doesn't have
+ -- a dedicated handler.
+ function (server_name) -- default handler (optional)
+ require("lspconfig")[server_name].setup {}
+ end,
+ -- Next, you can provide targeted overrides for specific servers.
+ -- For example, a handler override for the `rust_analyzer`:
+ ["rust_analyzer"] = function ()
+ require("rust-tools").setup {}
+ end
+ }
+
+Refer to |mason-lspconfig.setup_handlers()| for more information.
+
+==============================================================================
+Lua module: mason-lspconfig
+
+ *mason-lspconfig.setup()*
+setup({config})
+ Sets up mason with the provided {config} (see |mason-lspconfig-settings|).
+
+ *mason-lspconfig.setup_handlers()*
+setup_handlers({handlers})
+ Advanced feature ~
+ This is an advanced, opt-in, feature that require some careful reading
+ of the documentation.
+
+ The recommended method to set up servers with lspconfig is to do so by
+ following their guides, see |lspconfig-quickstart|.
+
+ Registers the provided {handlers}, to be called by mason when an installed
+ server supported by lspconfig is ready to be setup.
+
+ {handlers} is a table where the keys are the name of an lspconfig server,
+ and the values are the function to be called when that server is ready to
+ be set up (i.e. is installed).
+
+ You may also pass a default handler that will be called when no dedicated
+ handler is provided. This is done by providing a function without a key
+ (see example below).
+
+ Note: ~
+ The server names provided as keys are the lspconfig server names, not
+ mason's package names, so for example instead of "lua-language-server"
+ it's "sumneko_lua".
+
+ Example: ~
+
+ require("mason-lspconfig").setup_handlers({
+ -- The first entry (without a key) will be the default handler
+ -- and will be called for each installed server that doesn't have
+ -- a dedicated handler.
+ function (server_name) -- default handler (optional)
+ require("lspconfig")[server_name].setup {}
+ end,
+ -- Next, you can provide targeted overrides for specific servers.
+ ["rust_analyzer"] = function ()
+ require("rust-tools").setup {}
+ end,
+ ["sumneko_lua"] = function ()
+ lspconfig.sumneko_lua.setup {
+ settings = {
+ Lua = {
+ diagnostics = {
+ globals = { "vim" }
+ }
+ }
+ }
+ }
+ end,
+ })
+
+ See also: ~
+ You may achieve similar behaviour by manually looping through the
+ installed servers (see |mason-lspconfig.get_installed_servers()|) and
+ setting each one up.
+
+ *mason-lspconfig.get_installed_servers()*
+get_installed_servers()
+ Returns the installed LSP servers supported by lspconfig.
+
+ Note: ~
+ The returned strings are the lspconfig server names, not the mason
+ package names. For example, "sumneko_lua" is returned instead of
+ "lua-language-server". This is useful if you want to loop through the
+ table and use its values to directly interact with lspconfig (for
+ example setting up all installed servers).
+
+ Returns: ~
+ string[]
+
+ See also: ~
+ |mason-registry.get_installed_packages()|
+ |mason-registry.get_installed_package_names()|
+
+
+ vim:tw=78:ft=help:norl:expandtab:sw=4
diff --git a/lua/mason-lspconfig/api/command.lua b/lua/mason-lspconfig/api/command.lua
new file mode 100644
index 0000000..136cd35
--- /dev/null
+++ b/lua/mason-lspconfig/api/command.lua
@@ -0,0 +1,150 @@
+local a = require "mason-core.async"
+local Optional = require "mason-core.optional"
+local notify = require "mason-core.notify"
+local _ = require "mason-core.functional"
+
+---@async
+---@param user_args string[]: The arguments, as provided by the user.
+local function parse_packages_from_user_args(user_args)
+ local Package = require "mason-core.package"
+ local server_mapping = require "mason-lspconfig.mappings.server"
+ local language_mapping = require "mason.mappings.language"
+
+ return _.filter_map(function(server_specifier)
+ local server_name, version = Package.Parse(server_specifier)
+ -- 1. first see if the provided arg is an actual lspconfig server name
+ return Optional
+ .of_nilable(server_mapping.lspconfig_to_package[server_name])
+ -- 2. if not, check if it's a language specifier (e.g., "typescript" or "java")
+ :or_(function()
+ return Optional.of_nilable(language_mapping[server_name]):map(function(package_names)
+ local lsp_package_names = _.filter(function(package_name)
+ return server_mapping.package_to_lspconfig[package_name] ~= nil
+ end, package_names)
+
+ if #lsp_package_names == 0 then
+ return nil
+ end
+
+ return a.promisify(vim.ui.select)(lsp_package_names, {
+ prompt = ("Please select which server you want to install for language %q:"):format(
+ server_name
+ ),
+ format_item = function(item)
+ return server_mapping.package_to_lspconfig[item]
+ end,
+ })
+ end)
+ end)
+ :map(function(package_name)
+ return { package = package_name, version = version }
+ end)
+ :if_not_present(function()
+ notify(("Could not find LSP server %q."):format(server_name), vim.log.levels.ERROR)
+ end)
+ end, user_args)
+end
+
+---@async
+local function parse_packages_from_heuristics()
+ local server_mapping = require "mason-lspconfig.mappings.server"
+
+ -- Prompt user which server they want to install (based on the current filetype)
+ local current_ft = vim.api.nvim_buf_get_option(vim.api.nvim_get_current_buf(), "filetype")
+ local filetype_mapping = require "mason-lspconfig.mappings.filetype"
+ return Optional.of_nilable(filetype_mapping[current_ft])
+ :map(function(server_names)
+ return a.promisify(vim.ui.select)(server_names, {
+ prompt = ("Please select which server you want to install for filetype %q:"):format(current_ft),
+ })
+ end)
+ :map(function(server_name)
+ local package_name = server_mapping.lspconfig_to_package[server_name]
+ return { { package = package_name, version = nil } }
+ end)
+ :or_else_get(function()
+ notify(("No LSP servers found for filetype %q."):format(current_ft), vim.log.levels.ERROR)
+ return {}
+ end)
+end
+
+local parse_packages_to_install = _.cond {
+ { _.compose(_.gt(0), _.length), parse_packages_from_user_args },
+ { _.compose(_.equals(0), _.length), parse_packages_from_heuristics },
+ { _.T, _.always {} },
+}
+
+local LspInstall = a.scope(function(servers)
+ local packages_to_install = parse_packages_to_install(servers)
+ if #packages_to_install > 0 then
+ local registry = require "mason-registry"
+ _.each(function(target)
+ local pkg = registry.get_package(target.package)
+ pkg:install { version = target.version }
+ end, packages_to_install)
+ local ui = require "mason.ui"
+ ui.open()
+ ui.set_view "LSP"
+ vim.schedule(function()
+ ui.set_sticky_cursor "installing-section"
+ end)
+ end
+end)
+
+vim.api.nvim_create_user_command("LspInstall", function(opts)
+ LspInstall(opts.fargs)
+end, {
+ desc = "Install one or more LSP servers.",
+ nargs = "*",
+ complete = "custom,v:lua.mason_lspconfig_completion.available_server_completion",
+})
+
+local function LspUninstall(servers)
+ require("mason.ui").open()
+ require("mason.ui").set_view "LSP"
+ local registry = require "mason-registry"
+ local server_mapping = require "mason-lspconfig.mappings.server"
+ for _, server_specifier in ipairs(servers) do
+ local package_name = server_mapping.lspconfig_to_package[server_specifier]
+ local pkg = registry.get_package(package_name)
+ pkg:uninstall()
+ end
+end
+
+vim.api.nvim_create_user_command("LspUninstall", function(opts)
+ LspUninstall(opts.fargs)
+end, {
+ desc = "Uninstall one or more LSP servers.",
+ nargs = "+",
+ complete = "custom,v:lua.mason_lspconfig_completion.installed_server_completion",
+})
+
+_G.mason_lspconfig_completion = {
+ available_server_completion = function()
+ local registry = require "mason-registry"
+ local server_mapping = require "mason-lspconfig.mappings.server"
+ local language_mapping = require "mason.mappings.language"
+
+ local package_names = _.filter_map(function(pkg_name)
+ return Optional.of_nilable(server_mapping.package_to_lspconfig[pkg_name])
+ end, registry.get_all_package_names())
+ local completion =
+ _.compose(_.sort_by(_.identity), _.uniq_by(_.identity), _.concat(_.keys(language_mapping)))(package_names)
+ return table.concat(completion, "\n")
+ end,
+ installed_server_completion = function()
+ local registry = require "mason-registry"
+ local server_mapping = require "mason-lspconfig.mappings.server"
+
+ local server_names = _.filter_map(function(pkg_name)
+ return Optional.of_nilable(server_mapping.package_to_lspconfig[pkg_name])
+ end, registry.get_installed_package_names())
+ table.sort(server_names)
+ return table.concat(server_names, "\n")
+ end,
+}
+
+return {
+ LspInstall = LspInstall,
+ LspUninstall = LspUninstall,
+}
diff --git a/lua/mason-lspconfig/init.lua b/lua/mason-lspconfig/init.lua
new file mode 100644
index 0000000..33309c3
--- /dev/null
+++ b/lua/mason-lspconfig/init.lua
@@ -0,0 +1,174 @@
+local log = require "mason-core.log"
+local Package = require "mason-core.package"
+local Optional = require "mason-core.optional"
+local _ = require "mason-core.functional"
+local settings = require "mason-lspconfig.settings"
+local server_mapping = require "mason-lspconfig.mappings.server"
+local path = require "mason-core.path"
+local registry = require "mason-registry"
+local notify = require "mason-core.notify"
+local platform = require "mason-core.platform"
+
+local M = {}
+
+---@param lspconfig_server_name string
+local function resolve_package(lspconfig_server_name)
+ return Optional.of_nilable(server_mapping.lspconfig_to_package[lspconfig_server_name]):map(function(package_name)
+ local ok, pkg = pcall(registry.get_package, package_name)
+ if ok then
+ return pkg
+ end
+ end)
+end
+
+---@param lspconfig_server_name string
+local function resolve_server_config_factory(lspconfig_server_name)
+ local ok, server_config = pcall(require, ("mason-lspconfig.server_configurations.%s"):format(lspconfig_server_name))
+ if ok then
+ return Optional.of(server_config)
+ end
+ return Optional.empty()
+end
+
+---@param t1 table
+---@param t2 table
+local function merge_in_place(t1, t2)
+ for k, v in pairs(t2) do
+ if type(v) == "table" then
+ if type(t1[k]) == "table" and not vim.tbl_islist(t1[k]) then
+ merge_in_place(t1[k], v)
+ else
+ t1[k] = v
+ end
+ else
+ t1[k] = v
+ end
+ end
+ return t1
+end
+
+local memoized_set = _.memoize(_.set_of)
+
+---@param server_name string
+local function should_auto_install(server_name)
+ if settings.current.automatic_installation == true then
+ return true
+ end
+ if type(settings.current.automatic_installation) == "table" then
+ return not memoized_set(settings.current.automatic_installation.exclude)[server_name]
+ end
+ return false
+end
+
+local function setup_lspconfig_hook()
+ local util = require "lspconfig.util"
+ local win_exepath_compat = platform.is.win and require "mason-lspconfig.win-exepath-compat"
+
+ util.on_setup = util.add_hook_before(util.on_setup, function(config)
+ local pkg_name = server_mapping.lspconfig_to_package[config.name]
+ if not pkg_name then
+ return
+ end
+
+ if registry.is_installed(pkg_name) then
+ resolve_server_config_factory(config.name):if_present(function(config_factory)
+ merge_in_place(config, config_factory({ install_dir = path.package_prefix(pkg_name) }, config))
+ if win_exepath_compat and win_exepath_compat[config.name] and config.cmd and config.cmd[1] then
+ local exepath = vim.fn.exepath(config.cmd[1])
+ if exepath ~= "" then
+ config.cmd[1] = exepath
+ else
+ log.error("Failed to expand cmd path", config.name, config.cmd)
+ end
+ end
+ end)
+ elseif should_auto_install(config.name) then
+ local pkg = registry.get_package(pkg_name)
+ pkg:install():once("closed", function()
+ if pkg:is_installed() then
+ -- reload config
+ require("lspconfig")[config.name].setup(config)
+ end
+ end)
+ end
+ end)
+end
+
+local function ensure_installed()
+ for _, server_identifier in ipairs(settings.current.ensure_installed) do
+ local server_name, version = Package.Parse(server_identifier)
+ resolve_package(server_name):if_present(
+ ---@param pkg Package
+ function(pkg)
+ if not pkg:is_installed() then
+ pkg:install {
+ version = version,
+ }
+ end
+ end
+ )
+ end
+end
+
+---@param config MasonLspconfigSettings | nil
+function M.setup(config)
+ if config then
+ settings.set(config)
+ end
+
+ setup_lspconfig_hook()
+ ensure_installed()
+
+ require "mason-lspconfig.api.command"
+end
+
+---See `:h mason-lspconfig.setup_handlers()`
+---@param handlers table<string, fun(server_name: string)>
+function M.setup_handlers(handlers)
+ local default_handler = Optional.of_nilable(handlers[1])
+
+ _.each(function(handler)
+ if type(handler) == "string" and not server_mapping.lspconfig_to_package[handler] then
+ notify(
+ ("mason-lspconfig.setup_handlers: Received handler for unknown lspconfig server name: %s."):format(
+ handler
+ ),
+ vim.log.levels.WARN
+ )
+ end
+ end, _.keys(handlers))
+
+ ---@param pkg_name string
+ local function get_server_name(pkg_name)
+ return Optional.of_nilable(server_mapping.package_to_lspconfig[pkg_name])
+ end
+
+ local function call_handler(server_name)
+ log.fmt_trace("Checking handler for %s", server_name)
+ Optional.of_nilable(handlers[server_name]):or_(_.always(default_handler)):if_present(function(handler)
+ log.fmt_trace("Calling handler for %s", server_name)
+ local ok, err = pcall(handler, server_name)
+ if not ok then
+ vim.notify(err, vim.log.levels.ERROR)
+ end
+ end)
+ end
+
+ local installed_servers = _.filter_map(get_server_name, registry.get_installed_package_names())
+ _.each(call_handler, installed_servers)
+ registry:on(
+ "package:install:success",
+ vim.schedule_wrap(function(pkg)
+ get_server_name(pkg.name):if_present(call_handler)
+ end)
+ )
+end
+
+---@return string[]
+function M.get_installed_servers()
+ return _.filter_map(function(pkg_name)
+ return Optional.of_nilable(server_mapping.package_to_lspconfig[pkg_name])
+ end, registry.get_installed_package_names())
+end
+
+return M
diff --git a/lua/mason-lspconfig/mappings/filetype.lua b/lua/mason-lspconfig/mappings/filetype.lua
new file mode 100644
index 0000000..fc84277
--- /dev/null
+++ b/lua/mason-lspconfig/mappings/filetype.lua
@@ -0,0 +1,162 @@
+-- THIS FILE IS GENERATED. DO NOT EDIT MANUALLY.
+-- stylua: ignore start
+return {
+ OpenFOAM = { "foam_ls" },
+ apexcode = { "apex_ls" },
+ arduino = { "arduino_language_server" },
+ asm = { "asm_lsp" },
+ aspnetcorerazor = { "tailwindcss" },
+ astro = { "astro", "tailwindcss" },
+ ["astro-markdown"] = { "tailwindcss" },
+ awk = { "awk_ls" },
+ bean = { "beancount" },
+ beancount = { "beancount" },
+ bib = { "ltex", "texlab" },
+ bicep = { "bicep" },
+ blade = { "tailwindcss" },
+ bsl = { "bsl_ls" },
+ c = { "clangd" },
+ clar = { "clarity_lsp" },
+ clarity = { "clarity_lsp" },
+ clojure = { "clojure_lsp" },
+ cmake = { "cmake" },
+ cpp = { "clangd" },
+ crystal = { "crystalline" },
+ cs = { "csharp_ls", "omnisharp" },
+ css = { "cssls", "emmet_ls", "stylelint_lsp", "tailwindcss" },
+ cucumber = { "cucumber_language_server" },
+ cuda = { "clangd" },
+ d = { "serve_d" },
+ dhall = { "dhall_lsp_server" },
+ ["django-html"] = { "tailwindcss" },
+ dockerfile = { "dockerls" },
+ dot = { "dotls" },
+ dune = { "ocamllsp" },
+ edge = { "tailwindcss" },
+ edn = { "clojure_lsp" },
+ eelixir = { "elixirls", "tailwindcss" },
+ ejs = { "tailwindcss" },
+ elixir = { "elixirls" },
+ elm = { "elmls" },
+ erb = { "tailwindcss" },
+ erlang = { "erlangls" },
+ eruby = { "tailwindcss" },
+ flux = { "flux_lsp" },
+ foam = { "foam_ls" },
+ fortran = { "fortls" },
+ fsharp = { "fsautocomplete" },
+ genie = { "vala_ls" },
+ gitcommit = { "ltex" },
+ go = { "golangci_lint_ls", "gopls" },
+ gohtml = { "tailwindcss" },
+ gomod = { "golangci_lint_ls", "gopls" },
+ gotmpl = { "gopls" },
+ graphql = { "graphql" },
+ groovy = { "groovyls" },
+ haml = { "tailwindcss" },
+ handlebars = { "ember", "tailwindcss" },
+ haskell = { "hls" },
+ haxe = { "haxe_language_server" },
+ hbs = { "tailwindcss" },
+ heex = { "elixirls", "tailwindcss" },
+ hoon = { "hoon_ls" },
+ html = { "angularls", "emmet_ls", "html", "tailwindcss" },
+ ["html-eex"] = { "tailwindcss" },
+ htmldjango = { "tailwindcss" },
+ jade = { "tailwindcss" },
+ java = { "jdtls" },
+ javascript = { "cssmodules_ls", "denols", "ember", "eslint", "quick_lint_js", "rome", "stylelint_lsp", "tailwindcss", "tsserver" },
+ ["javascript.jsx"] = { "denols", "eslint", "tsserver" },
+ javascriptreact = { "cssmodules_ls", "denols", "eslint", "graphql", "rome", "stylelint_lsp", "tailwindcss", "tsserver" },
+ json = { "jsonls", "rome" },
+ jsonc = { "jsonls" },
+ jsonnet = { "jsonnet_ls" },
+ julia = { "julials" },
+ kotlin = { "kotlin_language_server" },
+ leaf = { "tailwindcss" },
+ less = { "cssls", "stylelint_lsp", "tailwindcss" },
+ lhaskell = { "hls" },
+ libsonnet = { "jsonnet_ls" },
+ liquid = { "tailwindcss", "theme_check" },
+ llw = { "lelwel_ls" },
+ lua = { "sumneko_lua" },
+ markdown = { "grammarly", "ltex", "marksman", "prosemd_lsp", "remark_ls", "tailwindcss", "zk" },
+ mdx = { "tailwindcss" },
+ ["metamath-zero"] = { "mm0_ls" },
+ mustache = { "tailwindcss" },
+ mysql = { "sqlls", "sqls" },
+ ncl = { "nickel_ls" },
+ nickel = { "nickel_ls" },
+ nim = { "nimls" },
+ nix = { "rnix" },
+ njk = { "tailwindcss" },
+ nunjucks = { "tailwindcss" },
+ objc = { "clangd" },
+ objcpp = { "clangd" },
+ ocaml = { "ocamllsp" },
+ ["ocaml.interface"] = { "ocamllsp" },
+ ["ocaml.menhir"] = { "ocamllsp" },
+ ["ocaml.ocamllex"] = { "ocamllsp" },
+ opencl = { "opencl_ls" },
+ org = { "ltex" },
+ os = { "bsl_ls" },
+ perl = { "perlnavigator" },
+ php = { "intelephense", "phpactor", "psalm", "tailwindcss" },
+ plaintex = { "ltex" },
+ postcss = { "tailwindcss" },
+ prisma = { "prismals" },
+ ps1 = { "powershell_es" },
+ puppet = { "puppet" },
+ purescript = { "purescriptls" },
+ python = { "jedi_language_server", "pylsp", "pyright", "sourcery" },
+ ql = { "codeqlls" },
+ r = { "r_language_server" },
+ razor = { "tailwindcss" },
+ reason = { "ocamllsp", "reason_ls", "tailwindcss" },
+ rescript = { "rescriptls", "tailwindcss" },
+ rmd = { "r_language_server" },
+ rnoweb = { "ltex" },
+ robot = { "robotframework_ls" },
+ rst = { "esbonio", "ltex" },
+ ruby = { "solargraph", "sorbet" },
+ rust = { "rust_analyzer" },
+ sass = { "tailwindcss" },
+ scss = { "cssls", "stylelint_lsp", "tailwindcss" },
+ sh = { "bashls" },
+ slim = { "tailwindcss" },
+ slint = { "slint_lsp" },
+ sls = { "salt_ls" },
+ solidity = { "solang", "solc" },
+ sql = { "sqlls", "sqls" },
+ stylus = { "tailwindcss" },
+ sugarss = { "stylelint_lsp", "tailwindcss" },
+ svelte = { "svelte", "tailwindcss" },
+ svg = { "lemminx" },
+ systemverilog = { "svlangserver", "svls", "verible" },
+ teal = { "teal_ls" },
+ terraform = { "terraformls", "tflint" },
+ tex = { "ltex", "texlab" },
+ toml = { "taplo" },
+ twig = { "tailwindcss" },
+ typescript = { "angularls", "cssmodules_ls", "denols", "ember", "eslint", "rome", "stylelint_lsp", "tailwindcss", "tsserver" },
+ ["typescript.tsx"] = { "angularls", "denols", "eslint", "rome", "tsserver" },
+ typescriptreact = { "angularls", "cssmodules_ls", "denols", "eslint", "graphql", "rome", "stylelint_lsp", "tailwindcss", "tsserver" },
+ vala = { "vala_ls" },
+ vb = { "omnisharp" },
+ verilog = { "svlangserver", "svls", "verible" },
+ vim = { "vimls" },
+ visualforce = { "visualforce_ls" },
+ vlang = { "vls" },
+ vmasm = { "asm_lsp" },
+ vue = { "eslint", "stylelint_lsp", "tailwindcss", "volar", "vuels" },
+ wxss = { "stylelint_lsp" },
+ xml = { "lemminx" },
+ xsd = { "lemminx" },
+ xsl = { "lemminx" },
+ xslt = { "lemminx" },
+ yaml = { "yamlls" },
+ ["yaml.ansible"] = { "ansiblels" },
+ ["yaml.docker-compose"] = { "yamlls" },
+ zig = { "zls" },
+ zir = { "zls" }
+} \ No newline at end of file
diff --git a/lua/mason-lspconfig/mappings/server.lua b/lua/mason-lspconfig/mappings/server.lua
new file mode 100644
index 0000000..939042c
--- /dev/null
+++ b/lua/mason-lspconfig/mappings/server.lua
@@ -0,0 +1,127 @@
+local _ = require "mason-core.functional"
+
+local M = {}
+
+---Maps lspconfig server config name to its corresponding package name.
+M.lspconfig_to_package = {
+ ["angularls"] = "angular-language-server",
+ ["ansiblels"] = "ansible-language-server",
+ ["apex_ls"] = "apex-language-server",
+ ["arduino_language_server"] = "arduino-language-server",
+ ["asm_lsp"] = "asm-lsp",
+ ["astro"] = "astro-language-server",
+ ["awk_ls"] = "awk-language-server",
+ ["bashls"] = "bash-language-server",
+ ["beancount"] = "beancount-language-server",
+ ["bicep"] = "bicep-lsp",
+ ["bsl_ls"] = "bsl-language-server",
+ ["clangd"] = "clangd",
+ ["clarity_lsp"] = "clarity-lsp",
+ ["clojure_lsp"] = "clojure-lsp",
+ ["cmake"] = "cmake-language-server",
+ ["codeqlls"] = "codeql",
+ ["crystalline"] = "crystalline",
+ ["csharp_ls"] = "csharp-language-server",
+ ["cssls"] = "css-lsp",
+ ["cssmodules_ls"] = "cucumber-language-server",
+ ["cucumber_language_server"] = "cucumber-language-server",
+ ["denols"] = "deno",
+ ["dhall_lsp_server"] = "dhall-lsp",
+ ["diagnosticls"] = "diagnostic-languageserver",
+ ["dockerls"] = "dockerfile-language-server",
+ ["dotls"] = "dot-language-server",
+ ["efm"] = "efm",
+ ["elixirls"] = "elixir-ls",
+ ["elmls"] = "elm-language-server",
+ ["ember"] = "ember-language-server",
+ ["emmet_ls"] = "emmet-ls",
+ ["erlangls"] = "erlang-ls",
+ ["esbonio"] = "esbonio",
+ ["eslint"] = "eslint-lsp",
+ ["flux_lsp"] = "flux-lsp",
+ ["foam_ls"] = "foam-language-server",
+ ["fortls"] = "fortls",
+ ["fsautocomplete"] = "fsautocomplete",
+ ["golangci_lint_ls"] = "golangci-lint-langserver",
+ ["gopls"] = "gopls",
+ ["grammarly"] = "grammarly-languageserver",
+ ["graphql"] = "graphql-language-service-cli",
+ ["groovyls"] = "groovy-language-server",
+ ["haxe_language_server"] = "haxe-language-server",
+ ["hls"] = "haskell-language-server",
+ ["hoon_ls"] = "hoon-language-server",
+ ["html"] = "html-lsp",
+ ["intelephense"] = "intelephense",
+ ["jdtls"] = "jdtls",
+ ["jedi_language_server"] = "jedi-language-server",
+ ["jsonls"] = "json-lsp",
+ ["jsonnet_ls"] = "jsonnet-language-server",
+ ["julials"] = "julia-lsp",
+ ["kotlin_language_server"] = "kotlin-language-server",
+ ["lelwel_ls"] = "lelwel",
+ ["lemminx"] = "lemminx",
+ ["ltex"] = "ltex-ls",
+ ["marksman"] = "marksman",
+ ["mm0_ls"] = "metamath-zero-lsp",
+ ["nickel_ls"] = "nickel-lang-lsp",
+ ["nimls"] = "nimlsp",
+ ["ocamllsp"] = "ocaml-lsp",
+ ["omnisharp"] = "omnisharp",
+ ["opencl_ls"] = "opencl-language-server",
+ ["perlnavigator"] = "perlnavigator",
+ ["phpactor"] = "phpactor",
+ ["powershell_es"] = "powershell-editor-services",
+ ["prismals"] = "prisma-language-server",
+ ["prosemd_lsp"] = "prosemd-lsp",
+ ["psalm"] = "psalm",
+ ["puppet"] = "puppet-editor-services",
+ ["purescriptls"] = "purescript-language-server",
+ ["pylsp"] = "python-lsp-server",
+ ["pyright"] = "pyright",
+ ["quick_lint_js"] = "quick-lint-js",
+ ["r_language_server"] = "r-languageserver",
+ ["reason_ls"] = "reason-language-server",
+ ["remark_ls"] = "remark-language-server",
+ ["rescriptls"] = "rescript-lsp",
+ ["rnix"] = "rnix-lsp",
+ ["robotframework_ls"] = "robotframework-lsp",
+ ["rome"] = "rome",
+ ["rust_analyzer"] = "rust-analyzer",
+ ["salt_ls"] = "salt-lsp",
+ ["serve_d"] = "serve-d",
+ ["slint_lsp"] = "slint-lsp",
+ ["solang"] = "solang",
+ ["solargraph"] = "solargraph",
+ ["solc"] = "solidity",
+ ["sorbet"] = "sorbet",
+ ["sourcery"] = "sourcery",
+ ["sqlls"] = "sqlls",
+ ["sqls"] = "sqls",
+ ["stylelint_lsp"] = "stylelint-lsp",
+ ["sumneko_lua"] = "lua-language-server",
+ ["svelte"] = "svelte-language-server",
+ ["svlangserver"] = "svlangserver",
+ ["svls"] = "svls",
+ ["tailwindcss"] = "tailwindcss-language-server",
+ ["taplo"] = "taplo",
+ ["teal_ls"] = "teal-language-server",
+ ["terraformls"] = "terraform-ls",
+ ["texlab"] = "texlab",
+ ["tflint"] = "tflint",
+ ["theme_check"] = "shopify-theme-check",
+ ["tsserver"] = "typescript-language-server",
+ ["vala_ls"] = "vala-language-server",
+ ["verible"] = "verible",
+ ["vimls"] = "vim-language-server",
+ ["visualforce_ls"] = "visualforce-language-server",
+ ["vls"] = "vls",
+ ["volar"] = "vue-language-server",
+ ["vuels"] = "vetur-vls",
+ ["yamlls"] = "yaml-language-server",
+ ["zk"] = "zk",
+ ["zls"] = "zls",
+}
+
+M.package_to_lspconfig = _.invert(M.lspconfig_to_package)
+
+return M
diff --git a/lua/mason-lspconfig/server_configurations/angularls/init.lua b/lua/mason-lspconfig/server_configurations/angularls/init.lua
new file mode 100644
index 0000000..813e525
--- /dev/null
+++ b/lua/mason-lspconfig/server_configurations/angularls/init.lua
@@ -0,0 +1,39 @@
+local platform = require "mason-core.platform"
+local _ = require "mason-core.functional"
+local path = require "mason-core.path"
+
+---@param install_dir string
+return function(install_dir)
+ local append_node_modules = _.map(function(dir)
+ return path.concat { dir, "node_modules" }
+ end)
+
+ local function get_cmd(workspace_dir)
+ local cmd = {
+ "ngserver",
+ "--stdio",
+ "--tsProbeLocations",
+ table.concat(append_node_modules { install_dir, workspace_dir }, ","),
+ "--ngProbeLocations",
+ table.concat(
+ append_node_modules {
+ path.concat { install_dir, "node_modules", "@angular", "language-server" },
+ workspace_dir,
+ },
+ ","
+ ),
+ }
+ if platform.is_win then
+ cmd[1] = vim.fn.exepath(cmd[1])
+ end
+
+ return cmd
+ end
+
+ return {
+ cmd = get_cmd(vim.loop.cwd()),
+ on_new_config = function(new_config, root_dir)
+ new_config.cmd = get_cmd(root_dir)
+ end,
+ }
+end
diff --git a/lua/mason-lspconfig/server_configurations/apex_ls/init.lua b/lua/mason-lspconfig/server_configurations/apex_ls/init.lua
new file mode 100644
index 0000000..6251dc7
--- /dev/null
+++ b/lua/mason-lspconfig/server_configurations/apex_ls/init.lua
@@ -0,0 +1,8 @@
+local path = require "mason-core.path"
+
+---@param install_dir string
+return function(install_dir)
+ return {
+ apex_jar_path = path.concat { install_dir, "apex-jorje-lsp.jar" },
+ }
+end
diff --git a/lua/mason-lspconfig/server_configurations/bicep/init.lua b/lua/mason-lspconfig/server_configurations/bicep/init.lua
new file mode 100644
index 0000000..ddc0a2a
--- /dev/null
+++ b/lua/mason-lspconfig/server_configurations/bicep/init.lua
@@ -0,0 +1,5 @@
+return function()
+ return {
+ cmd = { "bicep-lsp" },
+ }
+end
diff --git a/lua/mason-lspconfig/server_configurations/bsl_ls/init.lua b/lua/mason-lspconfig/server_configurations/bsl_ls/init.lua
new file mode 100644
index 0000000..62dfa5c
--- /dev/null
+++ b/lua/mason-lspconfig/server_configurations/bsl_ls/init.lua
@@ -0,0 +1,5 @@
+return function()
+ return {
+ cmd = { "bsl-language-server" },
+ }
+end
diff --git a/lua/mason-lspconfig/server_configurations/elixirls/init.lua b/lua/mason-lspconfig/server_configurations/elixirls/init.lua
new file mode 100644
index 0000000..0c424b1
--- /dev/null
+++ b/lua/mason-lspconfig/server_configurations/elixirls/init.lua
@@ -0,0 +1,5 @@
+return function()
+ return {
+ cmd = { "elixir-ls" },
+ }
+end
diff --git a/lua/mason-lspconfig/server_configurations/esbonio/init.lua b/lua/mason-lspconfig/server_configurations/esbonio/init.lua
new file mode 100644
index 0000000..e9d8d98
--- /dev/null
+++ b/lua/mason-lspconfig/server_configurations/esbonio/init.lua
@@ -0,0 +1,5 @@
+return function()
+ return {
+ cmd = { "esbonio" },
+ }
+end
diff --git a/lua/mason-lspconfig/server_configurations/groovyls/init.lua b/lua/mason-lspconfig/server_configurations/groovyls/init.lua
new file mode 100644
index 0000000..495c759
--- /dev/null
+++ b/lua/mason-lspconfig/server_configurations/groovyls/init.lua
@@ -0,0 +1,3 @@
+return function()
+ return { cmd = "groovy-language-server" }
+end
diff --git a/lua/mason-lspconfig/server_configurations/julials/README.md b/lua/mason-lspconfig/server_configurations/julials/README.md
new file mode 100644
index 0000000..18e6880
--- /dev/null
+++ b/lua/mason-lspconfig/server_configurations/julials/README.md
@@ -0,0 +1,9 @@
+# julials
+
+## Configuring the Julia Environment
+
+The Julia Environment will be identified in the following order:
+
+1) user configuration (`lspconfig.julials.setup { julia_env_path = "/my/env" }`)
+2) if the `Project.toml` & `Manifest.toml` (or `JuliaProject.toml` & `JuliaManifest.toml`) files exists in the current project working directory, the current project working directory is used as the environment
+3) the result of `Pkg.Types.Context().env.project_file`
diff --git a/lua/mason-lspconfig/server_configurations/julials/init.lua b/lua/mason-lspconfig/server_configurations/julials/init.lua
new file mode 100644
index 0000000..6166e35
--- /dev/null
+++ b/lua/mason-lspconfig/server_configurations/julials/init.lua
@@ -0,0 +1,50 @@
+local path = require "mason-core.path"
+local platform = require "mason-core.platform"
+local fs = require "mason-core.fs"
+local _ = require "mason-core.functional"
+
+---@param install_dir string
+return function(install_dir)
+ return {
+ on_new_config = function(config, workspace_dir)
+ local env_path = config.julia_env_path and vim.fn.expand(config.julia_env_path)
+ if not env_path then
+ local file_exists = _.compose(fs.sync.file_exists, path.concat, _.concat { workspace_dir })
+ if file_exists { "Project.toml" } and file_exists { "Manifest.toml" } then
+ env_path = workspace_dir
+ elseif file_exists { "JuliaProject.toml" } and file_exists { "JuliaManifest.toml" } then
+ env_path = workspace_dir
+ end
+ end
+
+ if not env_path then
+ local ok, env = pcall(vim.fn.system, {
+ "julia",
+ "--startup-file=no",
+ "--history-file=no",
+ "-e",
+ "using Pkg; print(dirname(Pkg.Types.Context().env.project_file))",
+ })
+ if ok then
+ env_path = env
+ end
+ end
+
+ config.cmd = {
+ "julia",
+ "--startup-file=no",
+ "--history-file=no",
+ "--depwarn=no",
+ ("--project=%s"):format(path.concat { install_dir, "scripts", "environments", "languageserver" }),
+ path.concat { install_dir, "nvim-lsp.jl" },
+ vim.env.JULIA_DEPOT_PATH or "",
+ path.concat { install_dir, "symbolstorev5" },
+ env_path,
+ }
+ end,
+ cmd_env = {
+ JULIA_DEPOT_PATH = path.concat { install_dir, "lsdepot" },
+ JULIA_LOAD_PATH = platform.is.win and ";" or ":",
+ },
+ }
+end
diff --git a/lua/mason-lspconfig/server_configurations/kotlin_language_server/init.lua b/lua/mason-lspconfig/server_configurations/kotlin_language_server/init.lua
new file mode 100644
index 0000000..585797f
--- /dev/null
+++ b/lua/mason-lspconfig/server_configurations/kotlin_language_server/init.lua
@@ -0,0 +1,5 @@
+return function()
+ return {
+ cmd = { "kotlin-language-server" },
+ }
+end
diff --git a/lua/mason-lspconfig/server_configurations/ltex/init.lua b/lua/mason-lspconfig/server_configurations/ltex/init.lua
new file mode 100644
index 0000000..f24d86f
--- /dev/null
+++ b/lua/mason-lspconfig/server_configurations/ltex/init.lua
@@ -0,0 +1,5 @@
+return function()
+ return {
+ cmd = { "ltex-ls" },
+ }
+end
diff --git a/lua/mason-lspconfig/server_configurations/omnisharp/README.md b/lua/mason-lspconfig/server_configurations/omnisharp/README.md
new file mode 100644
index 0000000..7825578
--- /dev/null
+++ b/lua/mason-lspconfig/server_configurations/omnisharp/README.md
@@ -0,0 +1,16 @@
+# omnisharp
+
+## How to enable Omnisharp Mono
+
+By default, the `omnisharp` server will use the `dotnet` (NET6) runtime to run the server.
+To run the server using the Mono runtime, set the `use_modern_net` setting to `false`, like so:
+
+__This requires the `omnisharp-mono` package to be installed.__
+
+```lua
+local lspconfig = require("lspconfig")
+
+lspconfig.omnisharp.setup {
+ use_modern_net = false
+}
+```
diff --git a/lua/mason-lspconfig/server_configurations/omnisharp/init.lua b/lua/mason-lspconfig/server_configurations/omnisharp/init.lua
new file mode 100644
index 0000000..274ff18
--- /dev/null
+++ b/lua/mason-lspconfig/server_configurations/omnisharp/init.lua
@@ -0,0 +1,5 @@
+return function(install_dir, config)
+ return {
+ cmd = { config.use_modern_net == false and "omnisharp-mono" or "omnisharp" },
+ }
+end
diff --git a/lua/mason-lspconfig/server_configurations/perlnavigator/init.lua b/lua/mason-lspconfig/server_configurations/perlnavigator/init.lua
new file mode 100644
index 0000000..c376dc7
--- /dev/null
+++ b/lua/mason-lspconfig/server_configurations/perlnavigator/init.lua
@@ -0,0 +1,5 @@
+return function()
+ return {
+ cmd = { "perlnavigator", "--stdio" },
+ }
+end
diff --git a/lua/mason-lspconfig/server_configurations/powershell_es/init.lua b/lua/mason-lspconfig/server_configurations/powershell_es/init.lua
new file mode 100644
index 0000000..d36a580
--- /dev/null
+++ b/lua/mason-lspconfig/server_configurations/powershell_es/init.lua
@@ -0,0 +1,6 @@
+---@param install_dir string
+return function(install_dir)
+ return {
+ bundle_path = install_dir,
+ }
+end
diff --git a/lua/mason-lspconfig/server_configurations/psalm/init.lua b/lua/mason-lspconfig/server_configurations/psalm/init.lua
new file mode 100644
index 0000000..8dd3645
--- /dev/null
+++ b/lua/mason-lspconfig/server_configurations/psalm/init.lua
@@ -0,0 +1,5 @@
+return function()
+ return {
+ cmd = { "psalm-language-server" },
+ }
+end
diff --git a/lua/mason-lspconfig/server_configurations/pylsp/README.md b/lua/mason-lspconfig/server_configurations/pylsp/README.md
new file mode 100644
index 0000000..2434bb4
--- /dev/null
+++ b/lua/mason-lspconfig/server_configurations/pylsp/README.md
@@ -0,0 +1,16 @@
+# Pylsp
+
+## Installing pylsp plugins
+
+Pylsp has [third party plugins](https://github.com/python-lsp/python-lsp-server#3rd-party-plugins) which are not installed by default.
+
+In order for these plugins to work with the `pylsp` server managed by this plugin, they need to be installed in the same [virtual environment](https://docs.python.org/3/library/venv.html) as `pylsp`. For these reasons, there's a convenient `:PylspInstall <packages>` command that does this for you, for example:
+
+```vim
+:PylspInstall pyls-flake8 pylsp-mypy pyls-isort
+```
+
+The `:PylspInstall` command will only be available once the `pylsp` server has been set up.
+
+**Note that these extra pylsp plugins will not be reinstalled if you update/reinstall the `pylsp` server, you will have to manage
+them manually.**
diff --git a/lua/mason-lspconfig/server_configurations/pylsp/init.lua b/lua/mason-lspconfig/server_configurations/pylsp/init.lua
new file mode 100644
index 0000000..9626fdc
--- /dev/null
+++ b/lua/mason-lspconfig/server_configurations/pylsp/init.lua
@@ -0,0 +1,51 @@
+local a = require "mason-core.async"
+local _ = require "mason-core.functional"
+local pip3 = require "mason-core.managers.pip3"
+local process = require "mason-core.process"
+local notify = require "mason-core.notify"
+local spawn = require "mason-core.spawn"
+
+---@param install_dir string
+return function(install_dir)
+ vim.api.nvim_create_user_command(
+ "PylspInstall",
+ a.scope(function(opts)
+ local plugins = opts.fargs
+ local plugins_str = table.concat(plugins, ", ")
+ notify(("Installing %s..."):format(plugins_str))
+ local result = spawn.pip {
+ "install",
+ "-U",
+ "--disable-pip-version-check",
+ plugins,
+ stdio_sink = process.simple_sink(),
+ with_paths = { pip3.venv_path(install_dir) },
+ }
+ if vim.in_fast_event() then
+ a.scheduler()
+ end
+ result
+ :on_success(function()
+ notify(("Successfully installed pylsp plugins %s"):format(plugins_str))
+ end)
+ :on_failure(function()
+ notify("Failed to install requested pylsp plugins.", vim.log.levels.ERROR)
+ end)
+ end),
+ {
+ desc = "[mason.nvim] Installs the provided packages in the same venv as pylsp.",
+ nargs = "+",
+ complete = _.always {
+ "pyls-flake8",
+ "pylsp-mypy",
+ "pyls-spyder",
+ "pyls-isort",
+ "python-lsp-black",
+ "pyls-memestra",
+ "pylsp-rope",
+ },
+ }
+ )
+
+ return {}
+end
diff --git a/lua/mason-lspconfig/server_configurations/r_language_server/init.lua b/lua/mason-lspconfig/server_configurations/r_language_server/init.lua
new file mode 100644
index 0000000..84b004c
--- /dev/null
+++ b/lua/mason-lspconfig/server_configurations/r_language_server/init.lua
@@ -0,0 +1,5 @@
+return function()
+ return {
+ cmd = { "r-languageserver" },
+ }
+end
diff --git a/lua/mason-lspconfig/server_configurations/rescriptls/init.lua b/lua/mason-lspconfig/server_configurations/rescriptls/init.lua
new file mode 100644
index 0000000..2f55cc3
--- /dev/null
+++ b/lua/mason-lspconfig/server_configurations/rescriptls/init.lua
@@ -0,0 +1,5 @@
+return function()
+ return {
+ cmd = { "rescript-lsp", "--stdio" },
+ }
+end
diff --git a/lua/mason-lspconfig/server_configurations/solang/init.lua b/lua/mason-lspconfig/server_configurations/solang/init.lua
new file mode 100644
index 0000000..2d8dcf7
--- /dev/null
+++ b/lua/mason-lspconfig/server_configurations/solang/init.lua
@@ -0,0 +1,14 @@
+local path = require "mason-core.path"
+local process = require "mason-core.process"
+
+---@param install_dir string
+return function(install_dir)
+ return {
+ cmd_env = {
+ PATH = process.extend_path {
+ path.concat { install_dir, "llvm13.0", "bin" },
+ path.concat { install_dir, "llvm12.0", "bin" }, -- kept for backwards compatibility
+ },
+ },
+ }
+end
diff --git a/lua/mason-lspconfig/server_configurations/tflint/README.md b/lua/mason-lspconfig/server_configurations/tflint/README.md
new file mode 100644
index 0000000..51298d6
--- /dev/null
+++ b/lua/mason-lspconfig/server_configurations/tflint/README.md
@@ -0,0 +1,14 @@
+# tflint
+
+## Installing TFLint plugins
+
+TFLint has [third party plugins](https://github.com/terraform-linters/tflint/blob/master/docs/user-guide/plugins.md) which are not installed by default.
+
+To install TFLint plugins, there's a convenient `:TFLintInit` command that does this for you. It will use Neovim's
+current working directory to locate the plugins to install (according to `tflint --init`):
+
+```
+:TFLintInit
+```
+
+The `:TFLintInit` command will only be available once the `tflint` server has been set up.
diff --git a/lua/mason-lspconfig/server_configurations/visualforce_ls/init.lua b/lua/mason-lspconfig/server_configurations/visualforce_ls/init.lua
new file mode 100644
index 0000000..7d4c0ec
--- /dev/null
+++ b/lua/mason-lspconfig/server_configurations/visualforce_ls/init.lua
@@ -0,0 +1,5 @@
+return function()
+ return {
+ cmd = { "visualforce-language-server", "--stdio" },
+ }
+end
diff --git a/lua/mason-lspconfig/server_configurations/volar/init.lua b/lua/mason-lspconfig/server_configurations/volar/init.lua
new file mode 100644
index 0000000..e1d98e7
--- /dev/null
+++ b/lua/mason-lspconfig/server_configurations/volar/init.lua
@@ -0,0 +1,27 @@
+local fs = require "mason-core.fs"
+local path = require "mason-core.path"
+
+---@param install_dir string
+return function(install_dir)
+ ---@param dir string
+ local function get_tsserverlib_path(dir)
+ return path.concat { dir, "node_modules", "typescript", "lib", "tsserverlibrary.js" }
+ end
+
+ ---@param workspace_dir string|nil
+ local function get_typescript_server_path(workspace_dir)
+ local local_tsserverlib = workspace_dir ~= nil and get_tsserverlib_path(workspace_dir)
+ local vendored_tsserverlib = get_tsserverlib_path(install_dir)
+ if local_tsserverlib and fs.sync.file_exists(local_tsserverlib) then
+ return local_tsserverlib
+ else
+ return vendored_tsserverlib
+ end
+ end
+
+ return {
+ on_new_config = function(new_config, new_install_dir)
+ new_config.init_options.typescript.serverPath = get_typescript_server_path(new_install_dir)
+ end,
+ }
+end
diff --git a/lua/mason-lspconfig/settings.lua b/lua/mason-lspconfig/settings.lua
new file mode 100644
index 0000000..a405f95
--- /dev/null
+++ b/lua/mason-lspconfig/settings.lua
@@ -0,0 +1,27 @@
+local M = {}
+
+---@class MasonLspconfigSettings
+local DEFAULT_SETTINGS = {
+ -- A list of servers to automatically install if they're not already installed. Example: { "rust_analyzer@nightly", "sumneko_lua" }
+ -- This setting has no relation with the `automatic_installation` setting.
+ ensure_installed = {},
+
+ -- Whether servers that are set up (via lspconfig) should be automatically installed if they're not already installed.
+ -- This setting has no relation with the `ensure_installed` setting.
+ -- Can either be:
+ -- - false: Servers are not automatically installed.
+ -- - true: All servers set up via lspconfig are automatically installed.
+ -- - { exclude: string[] }: All servers set up via lspconfig, except the ones provided in the list, are automatically installed.
+ -- Example: automatic_installation = { exclude = { "rust_analyzer", "solargraph" } }
+ automatic_installation = false,
+}
+
+M._DEFAULT_SETTINGS = DEFAULT_SETTINGS
+M.current = M._DEFAULT_SETTINGS
+
+---@param opts MasonLspconfigSettings
+function M.set(opts)
+ M.current = vim.tbl_deep_extend("force", M.current, opts)
+end
+
+return M
diff --git a/lua/mason-lspconfig/win-exepath-compat.lua b/lua/mason-lspconfig/win-exepath-compat.lua
new file mode 100644
index 0000000..995bb6d
--- /dev/null
+++ b/lua/mason-lspconfig/win-exepath-compat.lua
@@ -0,0 +1,82 @@
+---On Windows, Mason will link executables as .cmd wrapper scripts in Mason's bin/ directory.
+---Some utilities, libuv in particular (which neovim's RPC client, among others, uses), have problems finding .cmd
+---executables in PATH.
+---The following is a table of lspconfig servers whose cmd will need to be expanded with |exepath()| in order to be
+---successfully located and started with lspconfig.
+return {
+ ["angularls"] = true,
+ ["arduino_language_server"] = true,
+ ["asm_lsp"] = true,
+ ["beancount"] = true,
+ ["bicep"] = true,
+ ["bsl_ls"] = true,
+ ["ccls"] = true,
+ ["clangd"] = true,
+ ["clarity_lsp"] = true,
+ ["clojure_lsp"] = true,
+ ["cmake"] = true,
+ ["codeqlls"] = true,
+ ["crystalline"] = true,
+ ["csharp_ls"] = true,
+ ["cssls"] = true,
+ ["denols"] = true,
+ ["dhall_lsp_server"] = true,
+ ["efm"] = true,
+ ["elixirls"] = true,
+ ["esbonio"] = true,
+ ["flux_lsp"] = true,
+ ["fortls"] = true,
+ ["fsautocomplete"] = true,
+ ["golangci_lint_ls"] = true,
+ ["gopls"] = true,
+ ["groovyls"] = true,
+ ["haxe_language_server"] = true,
+ ["hls"] = true,
+ ["jdtls"] = true,
+ ["jedi_language_server"] = true,
+ ["jsonnet_ls"] = true,
+ ["kotlin_language_server"] = true,
+ ["lelwel_ls"] = true,
+ ["lemminx"] = true,
+ ["ltex"] = true,
+ ["mm0_ls"] = true,
+ ["nickel_ls"] = true,
+ ["nimls"] = true,
+ ["ocamllsp"] = true,
+ ["omnisharp"] = true,
+ ["perlnavigator"] = true,
+ ["phpactor"] = true,
+ ["prosemd_lsp"] = true,
+ ["psalm"] = true,
+ ["puppet"] = true,
+ ["pylsp"] = true,
+ ["quick_lint_js"] = true,
+ ["reason_ls"] = true,
+ ["rescriptls"] = true,
+ ["rnix"] = true,
+ ["robotframework_ls"] = true,
+ ["rust_analyzer"] = true,
+ ["salt_ls"] = true,
+ ["scry"] = true,
+ ["serve_d"] = true,
+ ["slint_lsp"] = true,
+ ["solang"] = true,
+ ["sorbet"] = true,
+ ["sourcery"] = true,
+ ["sqlls"] = true,
+ ["sqls"] = true,
+ ["sumneko_lua"] = true,
+ ["svls"] = true,
+ ["taplo"] = true,
+ ["teal_ls"] = true,
+ ["terraformls"] = true,
+ ["texlab"] = true,
+ ["tflint"] = true,
+ ["theme_check"] = true,
+ ["vala_ls"] = true,
+ ["verible"] = true,
+ ["visualforce_ls"] = true,
+ ["vls"] = true,
+ ["zk"] = true,
+ ["zls"] = true,
+}
diff --git a/scripts/lua/mason-scripts/mason-lspconfig/generate.lua b/scripts/lua/mason-scripts/mason-lspconfig/generate.lua
new file mode 100644
index 0000000..66c2ba6
--- /dev/null
+++ b/scripts/lua/mason-scripts/mason-lspconfig/generate.lua
@@ -0,0 +1,54 @@
+local a = require "mason-core.async"
+local path = require "mason-core.path"
+local _ = require "mason-core.functional"
+local lspconfig_server_mapping = require "mason-lspconfig.mappings.server"
+local script_utils = require "mason-scripts.utils"
+
+local MASON_LSPCONFIG_DIR = path.concat { vim.loop.cwd(), "lua", "mason-lspconfig" }
+
+local function get_lspconfig(name)
+ return require(("lspconfig.server_configurations.%s"):format(name))
+end
+
+---@async
+local function create_lspconfig_filetype_map()
+ local filetype_map = {}
+
+ for _, server_name in ipairs(_.keys(lspconfig_server_mapping.lspconfig_to_package)) do
+ local config = get_lspconfig(server_name)
+ for _, filetype in ipairs(config.default_config.filetypes or {}) do
+ if not filetype_map[filetype] then
+ filetype_map[filetype] = {}
+ end
+ table.insert(filetype_map[filetype], server_name)
+ table.sort(filetype_map[filetype])
+ end
+ end
+
+ script_utils.write_file(
+ path.concat { MASON_LSPCONFIG_DIR, "mappings", "filetype.lua" },
+ "return " .. vim.inspect(filetype_map),
+ "w"
+ )
+end
+
+---@async
+local function ensure_valid_mapping()
+ local server_mappings = require "mason-lspconfig.mappings.server"
+ local registry = require "mason-registry"
+
+ for lspconfig_server, mason_package in pairs(server_mappings.lspconfig_to_package) do
+ local lspconfig_ok, server_config =
+ pcall(require, ("lspconfig.server_configurations.%s"):format(lspconfig_server))
+ local mason_ok, pkg = pcall(registry.get_package, mason_package)
+ assert(lspconfig_ok and server_config ~= nil, lspconfig_server .. " is not a valid lspconfig server name.")
+ assert(mason_ok and pkg ~= nil, mason_package .. " is not a valid Mason package name.")
+ end
+end
+
+a.run_blocking(function()
+ a.wait_all {
+ create_lspconfig_filetype_map,
+ ensure_valid_mapping,
+ }
+end)
diff --git a/scripts/lua/mason-scripts/utils.lua b/scripts/lua/mason-scripts/utils.lua
new file mode 100644
index 0000000..771d419
--- /dev/null
+++ b/scripts/lua/mason-scripts/utils.lua
@@ -0,0 +1,21 @@
+local fs = require "mason-core.fs"
+
+local M = {}
+
+---@async
+---@param path string
+---@param contents string
+---@param flags string
+function M.write_file(path, contents, flags)
+ fs.async.write_file(
+ path,
+ table.concat({
+ "-- THIS FILE IS GENERATED. DO NOT EDIT MANUALLY.",
+ "-- stylua: ignore start",
+ contents,
+ }, "\n"),
+ flags
+ )
+end
+
+return M
diff --git a/scripts/nvim.sh b/scripts/nvim.sh
new file mode 100755
index 0000000..c2dcb6b
--- /dev/null
+++ b/scripts/nvim.sh
@@ -0,0 +1,14 @@
+#!/usr/bin/env bash
+
+set -exuo pipefail
+
+declare -x DEPENDENCIES="${PWD}/dependencies"
+declare -x MASON_DIR="$PWD"
+declare -x MASON_SCRIPT_DIR="${PWD}/scripts"
+
+nvim -u NONE -E -R --headless \
+ --cmd "set rtp^=${MASON_SCRIPT_DIR},${MASON_DIR}" \
+ --cmd "set packpath^=${DEPENDENCIES}" \
+ --cmd "packloadall" \
+ --cmd "luafile $1" \
+ --cmd "q"
diff --git a/stylua.toml b/stylua.toml
new file mode 100644
index 0000000..eb74155
--- /dev/null
+++ b/stylua.toml
@@ -0,0 +1,2 @@
+indent_type = "Spaces"
+call_parentheses = "None"
diff --git a/tests/helpers/lua/dummy2_package.lua b/tests/helpers/lua/dummy2_package.lua
new file mode 100644
index 0000000..424e47d
--- /dev/null
+++ b/tests/helpers/lua/dummy2_package.lua
@@ -0,0 +1,14 @@
+local Pkg = require "mason-core.package"
+
+return Pkg.new {
+ name = "dummy2",
+ desc = [[This is a dummy2 package.]],
+ categories = { Pkg.Cat.LSP },
+ languages = { Pkg.Lang.Dummy2Lang },
+ homepage = "https://example.com",
+ ---@async
+ ---@param ctx InstallContext
+ install = function(ctx)
+ ctx.receipt:with_primary_source { type = "dummy2" }
+ end,
+}
diff --git a/tests/helpers/lua/dummy_package.lua b/tests/helpers/lua/dummy_package.lua
new file mode 100644
index 0000000..b38d1cd
--- /dev/null
+++ b/tests/helpers/lua/dummy_package.lua
@@ -0,0 +1,14 @@
+local Pkg = require "mason-core.package"
+
+return Pkg.new {
+ name = "dummy",
+ desc = [[This is a dummy package.]],
+ categories = { Pkg.Cat.LSP },
+ languages = { Pkg.Lang.DummyLang },
+ homepage = "https://example.com",
+ ---@async
+ ---@param ctx InstallContext
+ install = function(ctx)
+ ctx.receipt:with_primary_source { type = "dummy" }
+ end,
+}
diff --git a/tests/helpers/lua/luassertx.lua b/tests/helpers/lua/luassertx.lua
new file mode 100644
index 0000000..55ea0d7
--- /dev/null
+++ b/tests/helpers/lua/luassertx.lua
@@ -0,0 +1,70 @@
+local assert = require "luassert"
+local match = require "luassert.match"
+local a = require "mason-core.async"
+
+local function wait_for(_, arguments)
+ ---@type (fun()): Function to execute until it does not error.
+ local assertions_fn = arguments[1]
+ ---@type number: Timeout in milliseconds. Defaults to 5000.
+ local timeout = arguments[2]
+ timeout = timeout or 15000
+
+ local start = vim.loop.hrtime()
+ local is_ok, err
+ repeat
+ is_ok, err = pcall(assertions_fn)
+ if not is_ok then
+ a.sleep(math.min(timeout, 100))
+ end
+ until is_ok or ((vim.loop.hrtime() - start) / 1e6) > timeout
+
+ if not is_ok then
+ error(err)
+ end
+
+ return is_ok
+end
+
+local function tbl_containing(_, arguments, _)
+ return function(value)
+ local expected = arguments[1]
+ for key, val in pairs(expected) do
+ if match.is_matcher(val) then
+ if not val(value[key]) then
+ return false
+ end
+ elseif value[key] ~= val then
+ return false
+ end
+ end
+ return true
+ end
+end
+
+local function list_containing(_, arguments, _)
+ return function(value)
+ local expected = arguments[1]
+ for _, val in pairs(value) do
+ if match.is_matcher(expected) then
+ if expected(val) then
+ return true
+ end
+ elseif expected == val then
+ return true
+ end
+ end
+ return false
+ end
+end
+
+local function instanceof(_, arguments, _)
+ return function(value)
+ local expected_mt = arguments[1]
+ return getmetatable(value) == expected_mt
+ end
+end
+
+assert:register("matcher", "tbl_containing", tbl_containing)
+assert:register("matcher", "list_containing", list_containing)
+assert:register("matcher", "instanceof", instanceof)
+assert:register("assertion", "wait_for", wait_for)
diff --git a/tests/helpers/lua/test_helpers.lua b/tests/helpers/lua/test_helpers.lua
new file mode 100644
index 0000000..57c0b4f
--- /dev/null
+++ b/tests/helpers/lua/test_helpers.lua
@@ -0,0 +1,49 @@
+---@diagnostic disable: lowercase-global
+local util = require "luassert.util"
+local spy = require "luassert.spy"
+
+local a = require "mason-core.async"
+local InstallHandle = require "mason-core.installer.handle"
+local InstallContext = require "mason-core.installer.context"
+local registry = require "mason-registry"
+
+function async_test(suspend_fn)
+ return function()
+ local ok, err = pcall(a.run_blocking, suspend_fn)
+ if not ok then
+ error(err, util.errorlevel())
+ end
+ end
+end
+
+mockx = {
+ just_runs = function() end,
+ returns = function(val)
+ return function()
+ return val
+ end
+ end,
+ throws = function(exception)
+ return function()
+ error(exception, 2)
+ end
+ end,
+}
+
+---@param package_name string
+function InstallHandleGenerator(package_name)
+ return InstallHandle.new(registry.get_package(package_name))
+end
+
+---@param handle InstallHandle
+---@param opts InstallContextOpts | nil
+function InstallContextGenerator(handle, opts)
+ local context = InstallContext.new(handle, opts or {})
+ context.spawn = setmetatable({}, {
+ __index = function(s, cmd)
+ s[cmd] = spy.new(mockx.just_runs())
+ return s[cmd]
+ end,
+ })
+ return context
+end
diff --git a/tests/mason-lspconfig/api/command_spec.lua b/tests/mason-lspconfig/api/command_spec.lua
new file mode 100644
index 0000000..0551c77
--- /dev/null
+++ b/tests/mason-lspconfig/api/command_spec.lua
@@ -0,0 +1,71 @@
+local spy = require "luassert.spy"
+local stub = require "luassert.stub"
+local match = require "luassert.match"
+
+local server_mappings = require "mason-lspconfig.mappings.server"
+local filetype_mappings = require "mason-lspconfig.mappings.filetype"
+local api = require "mason-lspconfig.api.command"
+local registry = require "mason-registry"
+local Pkg = require "mason-core.package"
+
+describe(":LspInstall", function()
+ server_mappings.lspconfig_to_package["dummylsp"] = "dummy"
+ server_mappings.package_to_lspconfig["dummy"] = "dummylsp"
+ filetype_mappings.dummylang = { "dummylsp" }
+
+ it("should install the provided servers", function()
+ local dummy = registry.get_package "dummy"
+ spy.on(Pkg, "install")
+ api.LspInstall { "dummylsp@1.0.0" }
+ assert.spy(Pkg.install).was_called(1)
+ assert.spy(Pkg.install).was_called_with(match.ref(dummy), {
+ version = "1.0.0",
+ })
+ end)
+
+ it(
+ "should prompt user for server to install based on filetype",
+ async_test(function()
+ local dummy = registry.get_package "dummy"
+ spy.on(Pkg, "install")
+ stub(vim.ui, "select")
+ vim.ui.select.invokes(function(items, opts, callback)
+ callback "dummylsp"
+ end)
+ vim.cmd [[new | setf dummylang]]
+ api.LspInstall {}
+ assert.spy(Pkg.install).was_called(1)
+ assert.spy(Pkg.install).was_called_with(match.ref(dummy), {
+ version = nil,
+ })
+ assert.spy(vim.ui.select).was_called(1)
+ assert.spy(vim.ui.select).was_called_with({ "dummylsp" }, match.is_table(), match.is_function())
+ end)
+ )
+
+ it(
+ "should not prompt user for server to install if no filetype match exists",
+ async_test(function()
+ spy.on(Pkg, "install")
+ stub(vim.ui, "select")
+ vim.cmd [[new | setf nolsplang]]
+ api.LspInstall {}
+ assert.spy(Pkg.install).was_called(0)
+ assert.spy(vim.ui.select).was_called(0)
+ end)
+ )
+end)
+
+describe(":LspUninstall", function()
+ server_mappings.lspconfig_to_package["dummylsp"] = "dummy"
+ server_mappings.package_to_lspconfig["dummy"] = "dummylsp"
+ filetype_mappings.dummylang = { "dummylsp" }
+
+ it("should uninstall the provided servers", function()
+ local dummy = registry.get_package "dummy"
+ spy.on(Pkg, "uninstall")
+ api.LspUninstall { "dummylsp" }
+ assert.spy(Pkg.uninstall).was_called(1)
+ assert.spy(Pkg.uninstall).was_called_with(match.ref(dummy))
+ end)
+end)
diff --git a/tests/mason-lspconfig/setup_spec.lua b/tests/mason-lspconfig/setup_spec.lua
new file mode 100644
index 0000000..9b960c0
--- /dev/null
+++ b/tests/mason-lspconfig/setup_spec.lua
@@ -0,0 +1,141 @@
+local match = require "luassert.match"
+local stub = require "luassert.stub"
+local spy = require "luassert.spy"
+
+local Pkg = require "mason-core.package"
+local mason_lspconfig = require "mason-lspconfig"
+local server_mappings = require "mason-lspconfig.mappings.server"
+local registry = require "mason-registry"
+local filetype_mappings = require "mason-lspconfig.mappings.filetype"
+
+describe("mason-lspconfig setup", function()
+ before_each(function()
+ server_mappings.lspconfig_to_package["dummylsp"] = "dummy"
+ server_mappings.lspconfig_to_package["dummy2lsp"] = "dummy2"
+ server_mappings.package_to_lspconfig["dummy"] = "dummylsp"
+ server_mappings.package_to_lspconfig["dummy2"] = "dummy2lsp"
+ filetype_mappings.dummylang = { "dummylsp", "dummy2lsp" }
+ require("lspconfig.util").on_setup = nil
+ local settings = require "mason-lspconfig.settings"
+ settings.set(settings._DEFAULT_SETTINGS)
+ vim.fn.delete(vim.env.INSTALL_ROOT_DIR, "rf")
+ end)
+
+ it("should set up user commands", function()
+ mason_lspconfig.setup()
+ local user_commands = vim.api.nvim_get_commands {}
+
+ assert.is_true(match.tbl_containing {
+ bang = false,
+ bar = false,
+ nargs = "*",
+ complete = "custom",
+ definition = "Install one or more LSP servers.",
+ }(user_commands["LspInstall"]))
+
+ assert.is_true(match.tbl_containing {
+ bang = false,
+ bar = false,
+ definition = "Uninstall one or more LSP servers.",
+ nargs = "+",
+ complete = "custom",
+ }(user_commands["LspUninstall"]))
+ end)
+
+ it(
+ "should install servers listed in ensure_installed",
+ async_test(function()
+ local dummy = registry.get_package "dummy"
+ spy.on(Pkg, "install")
+
+ mason_lspconfig.setup { ensure_installed = { "dummylsp@1.0.0" } }
+
+ assert.spy(Pkg.install).was_called(1)
+ assert.spy(Pkg.install).was_called_with(match.ref(dummy), {
+ version = "1.0.0",
+ })
+ assert.wait_for(function()
+ assert.is_true(dummy.handle:is_closed())
+ end)
+ end)
+ )
+
+ it(
+ "should automatically install servers",
+ async_test(function()
+ local dummy = registry.get_package "dummy"
+ local dummy2 = registry.get_package "dummy2"
+ spy.on(Pkg, "install")
+
+ mason_lspconfig.setup { automatic_installation = true }
+ local lspconfig = require "lspconfig"
+ spy.on(lspconfig.dummylsp, "setup")
+ spy.on(lspconfig.dummy2lsp, "setup")
+ lspconfig.dummylsp.setup {}
+ lspconfig.dummy2lsp.setup {}
+
+ assert.spy(Pkg.install).was_called(2)
+ assert.spy(Pkg.install).was_called_with(match.ref(dummy))
+ assert.spy(Pkg.install).was_called_with(match.ref(dummy2))
+
+ assert.wait_for(function()
+ assert.is_true(dummy.handle:is_closed())
+ assert.is_true(dummy2.handle:is_closed())
+ assert.spy(lspconfig.dummylsp.setup).was_called(2)
+ assert.spy(lspconfig.dummy2lsp.setup).was_called(2)
+ end)
+ end)
+ )
+end)
+
+describe("mason-lspconfig setup_handlers", function()
+ before_each(function()
+ server_mappings.lspconfig_to_package["dummylsp"] = "dummy"
+ server_mappings.lspconfig_to_package["dummy2lsp"] = "dummy2"
+ server_mappings.package_to_lspconfig["dummy"] = "dummylsp"
+ server_mappings.package_to_lspconfig["dummy2"] = "dummy2lsp"
+ filetype_mappings.dummylang = { "dummylsp", "dummy2lsp" }
+ require("lspconfig.util").on_setup = nil
+ local settings = require "mason-lspconfig.settings"
+ settings.set(settings._DEFAULT_SETTINGS)
+ end)
+
+ it("should call default handler", function()
+ stub(registry, "get_installed_package_names")
+ registry.get_installed_package_names.returns { "dummy" }
+ local default_handler = spy.new()
+
+ mason_lspconfig.setup_handlers { default_handler }
+
+ assert.spy(default_handler).was_called(1)
+ assert.spy(default_handler).was_called_with "dummylsp"
+ end)
+
+ it("should call dedicated handler", function()
+ stub(registry, "get_installed_package_names")
+ registry.get_installed_package_names.returns { "dummy" }
+ local dedicated_handler = spy.new()
+ local default_handler = spy.new()
+
+ mason_lspconfig.setup_handlers {
+ default_handler,
+ ["dummylsp"] = dedicated_handler,
+ }
+
+ assert.spy(default_handler).was_called(0)
+ assert.spy(dedicated_handler).was_called(1)
+ assert.spy(dedicated_handler).was_called_with "dummylsp"
+ end)
+
+ it("should print warning if registering handler for non-existent server name", function()
+ spy.on(vim, "notify")
+ mason_lspconfig.setup_handlers {
+ doesnt_exist_server = spy.new(),
+ }
+ assert.spy(vim.notify).was_called(1)
+ assert.spy(vim.notify).was_called_with(
+ "[mason.nvim] mason-lspconfig.setup_handlers: Received handler for unknown lspconfig server name: doesnt_exist_server.",
+ vim.log.levels.WARN
+ )
+ end)
+end)
diff --git a/tests/minimal_init.vim b/tests/minimal_init.vim
new file mode 100644
index 0000000..c3ffb58
--- /dev/null
+++ b/tests/minimal_init.vim
@@ -0,0 +1,39 @@
+" Avoid neovim/neovim#11362
+set display=lastline
+set directory=""
+set noswapfile
+
+let $mason = getcwd()
+let $test_helpers = getcwd() .. "/tests/helpers"
+let $dependencies = getcwd() .. "/dependencies"
+
+set rtp^=$mason,$test_helpers
+set packpath=$dependencies
+
+packloadall
+
+lua require("luassertx")
+lua require("test_helpers")
+
+lua <<EOF
+local index = require "mason-registry.index"
+index["dummy"] = "dummy_package"
+index["dummy2"] = "dummy2_package"
+
+local configs = require 'lspconfig.configs'
+configs.dummylsp = { default_config = {} }
+configs.dummy2lsp = { default_config = {} }
+
+require("mason").setup {
+ install_root_dir = vim.env.INSTALL_ROOT_DIR,
+}
+EOF
+
+function! RunTests() abort
+ lua <<EOF
+ require("plenary.test_harness").test_directory(os.getenv("FILE") or "./tests", {
+ minimal_init = vim.fn.getcwd() .. "/tests/minimal_init.vim",
+ sequential = true,
+ })
+EOF
+endfunction