[
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: 'status: needs triage'\nassignees: ''\n---\n\n<!--\nBefore opening an issue, please take a look at the [troubleshooting guide](https://github.com/haskell/vscode-haskell#troubleshooting). This explains some common issues and will also help you to find the information that the issue template asks for.\n\nWhen filing an issue, please fill out as much of the information below as you can. This helps us to debug your issue, but is not required!\n-->\n\n### Your environment\n\nWhich OS do you use:\n\n<!-- Windows, MacOS, Ubuntu, ArchLinux, etc... -->\n\n### Steps to reproduce\n\n<!-- Tell us how to reproduce this issue, including screenshots if you think they can be useful -->\n\n### Expected behaviour\n\n<!-- Tell us what should happen. -->\n\n### Actual behaviour\n\n<!-- Tell us what happens instead. -->\n\n### Include debug information\n\nExecute in the root of your project the command `haskell-language-server-wrapper --debug .` and paste the logs here (you can find the executable location [here](https://github.com/haskell/vscode-haskell#downloaded-binaries)):\n\n<details>\n<summary>\nDebug output:\n</summary>\n\n```\n<paste your logs here>\n```\n\n</details>\n\nPaste the contents of extension specific log, you can check instructions about how to find it [here](https://github.com/haskell/vscode-haskell#troubleshooting)\n\n<details>\n<summary>\nExtension log:\n</summary>\n\n```\n<paste your logs here>\n```\n\n</details>\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n---\n\n**Is your feature request related to a problem? Please describe.**\n\n<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->\n\n**Describe the solution you'd like**\n\n<!-- A clear and concise description of what you want to happen. -->\n\n**Describe alternatives you've considered**\n\n<!-- A clear and concise description of any alternative solutions or features you've considered. -->\n\n**Additional context**\n\n<!-- Add any other context or screenshots about the feature request here. -->\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  # NOTE: Dependabot official configuration documentation:\n  # https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically/configuration-options-for-dependency-updates#package-ecosystem\n\n  # Maintain dependencies for internal GitHub Actions CI for pull requests\n  - package-ecosystem: 'github-actions'\n    directory: '/'\n    schedule:\n      interval: 'weekly'\n\n  - package-ecosystem: 'npm'\n    directory: '/'\n    schedule:\n      interval: 'weekly'\n"
  },
  {
    "path": ".github/workflows/package.yml",
    "content": "on:\n  push:\n    branches:\n      - master\n  pull_request:\n    branches:\n      - '**'\n\njobs:\n  build:\n    strategy:\n      fail-fast: false\n      matrix:\n        os: [ubuntu-latest]\n    runs-on: ${{ matrix.os }}\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n\n      - name: Install Node.js\n        uses: actions/setup-node@v6\n        with:\n          ## make sure this corresponds with the version in release.yml\n          node-version: latest\n\n      - run: |\n          npm ci\n\n      - name: Package extension\n        run: npx vsce package\n      - name: Upload extension vsix to workflow artifacts\n        uses: actions/upload-artifact@v7\n        with:\n          name: haskell-${{ github.sha }}.vsix\n          path: haskell-*.vsix\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "on:\n  release:\n    types: [prereleased, released]\n\nname: Deploy Extension\njobs:\n  publish-extension:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n      - uses: actions/setup-node@v6\n        with:\n          ## make sure this corresponds with the version in test.yml\n          node-version: latest\n      - run: npm ci\n\n      - name: Package Extension\n        id: packageExtension\n        uses: HaaLeo/publish-vscode-extension@v2\n        with:\n          pat: stub\n          dryRun: true\n          preRelease: ${{ github.event.action == 'prereleased' }}\n\n      ## Make sure the artifact is added to the release.\n      - name: Upload extension vsix to workflow artifacts\n        uses: actions/upload-artifact@v7\n        with:\n          name: haskell-${{ github.event.release.tag_name }}.vsix\n          path: ${{ steps.packageExtension.outputs.vsixPath }}\n\n      ## If this is a release job, publish to VSCode Marketplace,\n      ## otherwise publish a pre-release to VSCode Marketplace\n      - name: Publish to Visual Studio Marketplace\n        id: publishToVSMarketplace\n        uses: HaaLeo/publish-vscode-extension@v2\n        with:\n          pat: ${{ secrets.VS_MARKETPLACE_TOKEN }}\n          registryUrl: https://marketplace.visualstudio.com\n          extensionFile: ${{ steps.packageExtension.outputs.vsixPath }}\n          preRelease: ${{ github.event.action == 'prereleased' }}\n\n      ## If this is a release job, publish to VSX Marketplace,\n      ## otherwise publish a pre-release to VSX Marketplace\n      - name: Publish to Open VSX Registry\n        id: publishToOpenVSX\n        continue-on-error: true\n        uses: HaaLeo/publish-vscode-extension@v2\n        with:\n          pat: ${{ secrets.OPEN_VSX_TOKEN }}\n          extensionFile: ${{ steps.packageExtension.outputs.vsixPath }}\n          preRelease: ${{ github.event.action == 'prereleased' }}\n"
  },
  {
    "path": ".github/workflows/test.yml",
    "content": "on:\n  push:\n    branches:\n      - master\n  pull_request:\n    branches:\n      - '**'\n\njobs:\n  build:\n    strategy:\n      fail-fast: false\n      matrix:\n        os: [macos-latest, ubuntu-latest, windows-latest]\n        ghc: [8.10.7, 9.6.7, 9.8.4, 9.12.2]\n    runs-on: ${{ matrix.os }}\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n\n      - name: Install Node.js\n        uses: actions/setup-node@v6\n        with:\n          ## make sure this corresponds with the version in release.yml\n          node-version: latest\n\n      # Install test dependencies\n      - run: npm ci\n      - run: npm run webpack\n\n      # Setup toolchains, install ghcup, install ghc, etc...\n      - name: Install GHCup\n        run: |\n          curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh\n        shell: bash\n        env:\n          BOOTSTRAP_HASKELL_NONINTERACTIVE: 1\n          BOOTSTRAP_HASKELL_MINIMAL: 1\n\n      - name: Check GHCup (Windows)\n        run: |\n          echo \"c:/ghcup/bin\" >> $GITHUB_PATH\n        shell: bash\n        if: runner.os == 'Windows'\n\n      - name: Check GHCup (Unix)\n        run: |\n          echo \"${HOME}/.ghcup/bin\" >> $GITHUB_PATH\n        shell: bash\n        if: runner.os != 'Windows'\n\n      - name: Toolchain settings\n        run: |\n          ghcup upgrade -i -f\n          export GHCUP_INSTALL_BASE_PREFIX=$(pwd)/test-workspace/bin\n          ghcup config set cache true\n\n          ghcup install stack latest\n          ghcup install cabal latest\n\n          ghcup install ghc ${{ matrix.ghc }}\n          ghcup set ghc ${{ matrix.ghc }}\n\n          # This is a prefetched, fallback HLS version.\n          # We want to make sure, we still support old GHC versions\n          # and graciously fallback to an HLS version that supports the old GHC version, such as 8.10.7\n          ghcup install hls 2.2.0.0\n          ghcup install hls latest\n        shell: bash\n\n      # Run the tests\n      - name: Run the test on Linux\n        run: |\n          export GHCUP_INSTALL_BASE_PREFIX=$(pwd)/test-workspace/bin\n          export PATH=\"$(pwd)/test-workspace/bin/.ghcup/bin:$PATH\"\n          xvfb-run -s '-screen 0 640x480x16' -a npm run test\n        shell: bash\n        if: runner.os == 'Linux'\n      - name: Run the test on macOS\n        run: |\n          export GHCUP_INSTALL_BASE_PREFIX=$(pwd)/test-workspace/bin\n          export PATH=\"$(pwd)/test-workspace/bin/.ghcup/bin:$PATH\"\n          npm run test\n        shell: bash\n        if: runner.os == 'macOS'\n      - name: Run the test on Windows\n        run: |\n          export GHCUP_INSTALL_BASE_PREFIX=$(pwd)/test-workspace/bin\n          export PATH=\"$(pwd)/test-workspace/bin/ghcup/bin:$PATH\"\n          npm run test\n        shell: bash\n        if: runner.os == 'Windows'\n\n      # Create package artefacts\n      - name: Delete test artefacts\n        # The test-suite doesn't clean up correctly after itself.\n        # This is a poor man's workaround that after test execution,\n        # the test-workspace still contains binaries and caches.\n        run: |\n          rm -rf test-workspace\n          rm -rf out\n        shell: bash\n"
  },
  {
    "path": ".gitignore",
    "content": "out\r\nnode_modules\r\n.vscode-test\r\n.DS_Store\r\ndist\r\n*.vsix\r\n.husky\r\n\r\n# Ignore everything in test-workspace\r\ntest-workspace/*\r\n\r\n# Except the .gitkeep file\r\n!test-workspace/.gitkeep\r\n\r\n"
  },
  {
    "path": ".prettierignore",
    "content": "node_modules/\ntest-workspace/\n.vscode/\n.vscode-test/\nout/\ndist/\nwebpack.config.js\n"
  },
  {
    "path": ".prettierrc",
    "content": "printWidth: 120\nsingleQuote: true\n"
  },
  {
    "path": ".vscode/extensions.json",
    "content": "{\n  \"recommendations\": [\n    \"dbaeumer.vscode-eslint\",\n    \"esbenp.prettier-vscode\"\n  ]\n}"
  },
  {
    "path": ".vscode/launch.json",
    "content": "// A launch configuration that compiles the extension and then opens it inside a new window\n{\n  \"version\": \"0.2.0\",\n  \"configurations\": [\n    {\n      \"name\": \"Extension\",\n      \"type\": \"extensionHost\",\n      \"request\": \"launch\",\n      \"runtimeExecutable\": \"${execPath}\",\n      \"args\": [\"--extensionDevelopmentPath=${workspaceRoot}\"],\n      \"sourceMaps\": true,\n      \"outFiles\": [\"${workspaceRoot}/dist/**/*.js\"],\n      \"preLaunchTask\": \"npm: webpack\"\n    },\n    {\n      \"name\": \"Extension Tests\",\n      \"type\": \"extensionHost\",\n      \"request\": \"launch\",\n      \"runtimeExecutable\": \"${execPath}\",\n      \"testConfiguration\": \"${workspaceFolder}/.vscode-test.js\",\n      \"args\": [\"--extensionDevelopmentPath=${workspaceRoot}\", \"--extensionTestsPath=${workspaceRoot}/out/test\"],\n      \"sourceMaps\": true,\n      \"outFiles\": [\"${workspaceRoot}/out/test/**/*.js\"],\n      \"preLaunchTask\": \"npm: pretest\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".vscode/settings.json",
    "content": "// Place your settings in this file to overwrite default and user settings.\n{\n  \"files.exclude\": {\n    \"dist\": true, // set this to true to hide the \"dist\" folder with the compiled JS files\n    \".vscode-test\": true,\n    \"node_modules\": true\n  },\n  \"search.exclude\": {\n    \"dist\": true // set this to false to include \"dist\" folder in search results\n  },\n  \"typescript.tsdk\": \"./node_modules/typescript/lib\",\n  \"editor.tabSize\": 2, // we want to use the TS server from our node_modules folder to control its version,\n  \"editor.formatOnSave\": true,\n  \"files.associations\": {\n    \".prettierrc\": \"yaml\"\n  },\n  \"files.eol\": \"\\n\",\n  \"haskell.formattingProvider\": \"stylish-haskell\"\n}"
  },
  {
    "path": ".vscode/tasks.json",
    "content": "// See https://go.microsoft.com/fwlink/?LinkId=733558\n// for the documentation about the tasks.json format\n{\n  \"version\": \"2.0.0\",\n  \"tasks\": [\n    {\n      \"type\": \"npm\",\n      \"script\": \"watch\",\n      \"problemMatcher\": {\n        \"owner\": \"typescript\",\n        \"applyTo\": \"closedDocuments\",\n        \"fileLocation\": [\"absolute\"],\n        \"pattern\": {\n          \"regexp\": \"<nothing>\"\n        },\n        \"background\": {\n          \"activeOnStart\": true,\n          \"beginsPattern\": {\n            \"regexp\": \"Compilation (.*?)starting…\"\n          },\n          \"endsPattern\": {\n            \"regexp\": \"Compilation (.*?)finished\"\n          }\n        }\n      },\n      \"isBackground\": true,\n      \"presentation\": {\n        \"reveal\": \"never\"\n      },\n      \"group\": {\n        \"kind\": \"build\",\n        \"isDefault\": true\n      }\n    },\n    {\n      \"type\": \"npm\",\n      \"script\": \"test\",\n      \"group\": {\n        \"kind\": \"test\",\n        \"isDefault\": true\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": ".vscode-test.js",
    "content": "const { defineConfig } = require('@vscode/test-cli');\n\nmodule.exports = defineConfig([\n  {\n    label: 'integration-tests',\n    files: 'out/test/**/*.test.js',\n    version: 'stable',\n    workspaceFolder: './test-workspace',\n    installExtensions: ['justusadam.language-haskell'],\n    mocha: {\n      timeout: 120 * 1000, // 2 minute timeout\n    },\n  },\n  // you can specify additional test configurations, too\n]);\n"
  },
  {
    "path": ".vscodeignore",
    "content": ".vscode/**\n.vscode-test/**\ntypings/**\nout/test/**\ntest/**\nsrc/**\n**/*.map\n.gitignore\ntsconfig.json\nnode_modules\nout\nsrc\nwebpack.config.json\n"
  },
  {
    "path": "Changelog.md",
    "content": "# Changelog for vscode-haskell\n\n## 2.8.0\n\n- Migrate project to npm 11.9.0\n  ([#1336](https://github.com/haskell/vscode-haskell/pull/1336)) by @fendor\n- Standardized LICENSE for vscode-haskell\n  ([#1290](https://github.com/haskell/vscode-haskell/pull/1290)) by @Shubhashish-Chakraborty\n\n## 2.7.0\n\n- Introduce the `StatusBarItem`\n  ([#1237](https://github.com/haskell/vscode-haskell/pull/1237)) by @fendor\n\n## 2.6.1\n\n- Prefer the `set` version for `cabal` and `stack` if there is any\n  ([#1275](https://github.com/haskell/vscode-haskell/pull/1275)) by @fendor\n- Make js debugger work\n  ([#1258](https://github.com/haskell/vscode-haskell/pull/1258)) by @dyniec\n- Prepare release 2.6.0\n  ([#1103](https://github.com/haskell/vscode-haskell/pull/1103)) by @fendor\n\n## 2.6.0\n\n- Add option to enable/disable `.cabal` file support\n  ([#1223](https://github.com/haskell/vscode-haskell/pull/1223)) by @fendor\n- Upgrade project to use latest eslint version\n  ([#1150](https://github.com/haskell/vscode-haskell/pull/1150)) by @fendor\n- Fix windows CI\n  ([#1149](https://github.com/haskell/vscode-haskell/pull/1149)) by @fendor\n- Manually install ghcup into image\n  ([#1119](https://github.com/haskell/vscode-haskell/pull/1119)) by @fendor\n- bump vscode-languageclient version to 9.0.1\n  ([#1108](https://github.com/haskell/vscode-haskell/pull/1108)) by @jetjinser\n- Add cabalFormattingProvider to package.json\n  ([#1100](https://github.com/haskell/vscode-haskell/pull/1100)) by @fendor\n\n## 2.5.3\n\n- Split out packaging action\n  ([#1080](https://github.com/haskell/vscode-haskell/pull/1080)) by @fendor\n- Add Session Loading style to list of known configs\n  ([#1077](https://github.com/haskell/vscode-haskell/pull/1077)) by @fendor\n- Tooling update\n  ([#1043](https://github.com/haskell/vscode-haskell/pull/1043)) by @bzm3r\n- Add `haskell.plugin.fourmolu.config.path` option\n  ([#987](https://github.com/haskell/vscode-haskell/pull/987)) by @georgefst\n\n## 2.5.2\n\n- Includes changes of the 2.4.3 release\n\n## 2.5.1\n\n- Includes changes of the 2.4.2 release\n\n## 2.5.0\n\n- Add tracking of cabal files\n  ([#618](https://github.com/haskell/vscode-haskell/pull/618)) by @fendor\n\n## 2.4.3\n\n- Address invalid byte sequence error #1022\n  ([#1022](https://github.com/haskell/vscode-haskell/pull/1022)) by @felixlinker\n- Always set the cwd for the executable (#1011)\n  ([#1011](https://github.com/haskell/vscode-haskell/pull/1011)) by @fendor\n\n## 2.4.2\n\n- Add stan plugin option #1000\n  ([#1000](https://github.com/haskell/vscode-haskell/pull/1000)) by @fendor\n- Probe for GHCup binary wrt #962\n  ([#963](https://github.com/haskell/vscode-haskell/pull/963)) by @hasufell\n- Bump old hls version and upgrade test runner to macos-latest\n  ([#960](https://github.com/haskell/vscode-haskell/pull/960)) by @July541\n- Increase time limitation to make test on Windows more stable\n  ([#959](https://github.com/haskell/vscode-haskell/pull/959)) by @July541\n- Update release docs for refreshing CI tokens\n  ([#942](https://github.com/haskell/vscode-haskell/pull/942)) by @fendor\n\n## 2.4.1\n\n- Downgrade vscode-languageclient\n  ([#934](https://github.com/haskell/vscode-haskell/pull/934)) by @fendor\n- Bump vscode to 1.80.0\n  ([#912](https://github.com/haskell/vscode-haskell/pull/912)) by @July541\n\n## 2.4.0\n\n- Prepare release 2.4.0\n  ([#906](https://github.com/haskell/vscode-haskell/pull/906)) by @VeryMilkyJoe\n- Simplify tests\n  ([#904](https://github.com/haskell/vscode-haskell/pull/904)) by @July541\n- Remove unused code\n  ([#898](https://github.com/haskell/vscode-haskell/pull/898)) by @fendor\n- Remove hoogle command from vscode extension\n  ([#896](https://github.com/haskell/vscode-haskell/pull/896)) by @fendor\n- Update readme\n  ([#886](https://github.com/haskell/vscode-haskell/pull/886)) by @VeryMilkyJoe\n- Fix broken tests\n  ([#880](https://github.com/haskell/vscode-haskell/pull/880)) by @July541\n- Update README.md: clarify how to use Stack with vscode-haskell extension\n  ([#874](https://github.com/haskell/vscode-haskell/pull/874)) by @miguel-negrao\n- Remove debugger tools from CI\n  ([#873](https://github.com/haskell/vscode-haskell/pull/873)) by @fendor\n- Refactor tests to work correctly\n  ([#872](https://github.com/haskell/vscode-haskell/pull/872)) by @July541\n- Downgrade vscode language client to 7.0.0\n  ([#853](https://github.com/haskell/vscode-haskell/pull/853)) by @fendor\n- Update badge url for VSCode Marketplace\n  ([#851](https://github.com/haskell/vscode-haskell/pull/851)) by @fendor\n\n## 2.2.4\n\n- Downgrade vscode language client to 7.0.0\n  ([#843](https://github.com/haskell/vscode-haskell/pull/853)) by @fendor\n\n## 2.2.3\n\n- Prepare release 2.2.3\n  ([#843](https://github.com/haskell/vscode-haskell/pull/843)) by @fendor\n- Add new plugins fields\n  ([#842](https://github.com/haskell/vscode-haskell/pull/842)) by @fendor\n  - Migrate to eslint\n    ([#782](https://github.com/haskell/vscode-haskell/pull/782)) by @fendor\n- Bump minor versions of package dependencies\n  ([#781](https://github.com/haskell/vscode-haskell/pull/781)) by @fendor\n- Update unsupported GHC doc link\n  ([#776](https://github.com/haskell/vscode-haskell/pull/776)) by @limaak\n- Fix release CI\n  ([#775](https://github.com/haskell/vscode-haskell/pull/775)) by @fendor\n- Fix mistake in generated ChangeLog\n  ([#774](https://github.com/haskell/vscode-haskell/pull/774)) by @fendor\n\n## 2.2.2\n\n- Add link to HLS installation webpage\n  ([#751](https://github.com/haskell/vscode-haskell/pull/751)) by @fendor\n- Change scope of serverExecutablePath to machine-overridable\n  ([#742](https://github.com/haskell/vscode-haskell/pull/742)) by @fendor\n- Add Fourmolu config property\n  ([#736](https://github.com/haskell/vscode-haskell/pull/736)) by @georgefst\n- Add missing configuration options for the latest HLS version\n  ([#717](https://github.com/haskell/vscode-haskell/pull/717)) by @fendor\n- Change sensible to sensitive\n  ([#709](https://github.com/haskell/vscode-haskell/pull/709)) by @ploeh\n\n## 2.2.1\n\n- Fix test-suite for new GHCUp release\n  ([#672](https://github.com/haskell/vscode-haskell/pull/672)) by @fendor\n- Bump webpack from 5.73.0 to 5.74.0\n  ([#657](https://github.com/haskell/vscode-haskell/pull/657)) by @fendor\n- Bump typescript from 4.4.0 to 4.7.4\n  ([#657](https://github.com/haskell/vscode-haskell/pull/657)) by @fendor\n- Bump @types/node from 18.0.4 to 18.6.1\n  ([#657](https://github.com/haskell/vscode-haskell/pull/657)) by @fendor\n- Bump @typescript-eslint/eslint-plugin from 5.30.6 to 5.31.0\n  ([#657](https://github.com/haskell/vscode-haskell/pull/657)) by @fendor\n- Bump @typescript-eslint/parser from 5.30.6 to 5.31.0\n  ([#657](https://github.com/haskell/vscode-haskell/pull/657)) by @fendor\n- Bump prettier from 2.6.2 to 2.7.1\n  ([#657](https://github.com/haskell/vscode-haskell/pull/657)) by @fendor\n- Bump mocha from 9.2.1 to 10.0.0\n  ([#657](https://github.com/haskell/vscode-haskell/pull/657)) by @fendor\n- Add dependabot.yml\n  ([#633](https://github.com/haskell/vscode-haskell/pull/633)) by @fendor\n- Replace x32 with ia32 for Architecture matching\n  ([#631](https://github.com/haskell/vscode-haskell/pull/631)) by @fendor\n- Toolchain management dialog: add hint for beginners\n  ([#621](https://github.com/haskell/vscode-haskell/pull/621)) by @runeksvendsen\n- Fix trace.server option\n  ([#617](https://github.com/haskell/vscode-haskell/pull/617)) by @coltenwebb\n- Add TOC\n  ([#615](https://github.com/haskell/vscode-haskell/pull/615)) by @hasufell\n- Cleanups\n  ([#605](https://github.com/haskell/vscode-haskell/pull/605)) by @hasufell\n- Link to VSCode settings page\n  ([#603](https://github.com/haskell/vscode-haskell/pull/603)) by @hasufell\n- Refactor toInstall shenanigans\n  ([#600](https://github.com/haskell/vscode-haskell/pull/600)) by @hasufell\n- Fix confusing download dialog popup\n  ([#599](https://github.com/haskell/vscode-haskell/pull/599)) by @hasufell\n- More troubleshooting\n  ([#598](https://github.com/haskell/vscode-haskell/pull/598)) by @hasufell\n\n## 2.2.0\n\n- Bump version to 2.2.0 (Syncs up pre-release and release version)\n  ([#594](https://github.com/haskell/vscode-haskell/pull/594)) by @fendor\n\n## 2.0.1\n\n- Bad error message when ghcup is not installed\n  ([#591](https://github.com/haskell/vscode-haskell/pull/591)) by @hasufell\n- Better error message if we can't find a HLS version for a given GHC\n  ([#588](https://github.com/haskell/vscode-haskell/pull/588)) by @hasufell\n- Properly convert release metadata from json\n  ([#585](https://github.com/haskell/vscode-haskell/pull/585)) by @fendor\n- Ignore missing entries in Release Metadata\n  ([#582](https://github.com/haskell/vscode-haskell/pull/582)) by @fendor\n- Add Tool class and print stacktraces\n  ([#579](https://github.com/haskell/vscode-haskell/pull/579)) by @fendor\n- List Env Vars we care about only\n  ([#578](https://github.com/haskell/vscode-haskell/pull/578)) by @fendor\n- Prepare pre-release 2.1.0\n  ([#574](https://github.com/haskell/vscode-haskell/pull/574)) by @fendor\n- Enable pre-release feature for VSX Marketplace\n  ([#573](https://github.com/haskell/vscode-haskell/pull/573)) by @fendor\n- Add prettier script\n  ([#566](https://github.com/haskell/vscode-haskell/pull/566)) by @fendor\n- Remove accidental run command\n  ([#565](https://github.com/haskell/vscode-haskell/pull/565)) by @fendor\n- Upgrade dependencies\n  ([#564](https://github.com/haskell/vscode-haskell/pull/564)) by @fendor\n- Add new configuration options for rename plugin\n  ([#563](https://github.com/haskell/vscode-haskell/pull/563)) by @OliverMadine\n- Introduce 'haskell.toolchain' setting\n  ([#562](https://github.com/haskell/vscode-haskell/pull/562)) by @hasufell\n- Improve\n  ([#558](https://github.com/haskell/vscode-haskell/pull/558)) by @hasufell\n- Remove stdout/sterr from user error message\n  ([#556](https://github.com/haskell/vscode-haskell/pull/556)) by @fendor\n- Fix npm security issue\n  ([#555](https://github.com/haskell/vscode-haskell/pull/555)) by @fendor\n- No colour output for GHCup\n  ([#554](https://github.com/haskell/vscode-haskell/pull/554)) by @fendor\n- Add eval plugin configuration\n  ([#549](https://github.com/haskell/vscode-haskell/pull/549)) by @xsebek\n- Manage all the Haskell things\n  ([#547](https://github.com/haskell/vscode-haskell/pull/547)) by @hasufell\n- Consider user installed HLSes (e.g. via ghcup compile)\n  ([#543](https://github.com/haskell/vscode-haskell/pull/543)) by @hasufell\n- Update README.MD GHC support\n  ([#537](https://github.com/haskell/vscode-haskell/pull/537)) by @cptwunderlich\n- fix: change deprecated Haskell Platform install link to GHCup\n  ([#536](https://github.com/haskell/vscode-haskell/pull/536)) by @HEIGE-PCloud\n- Update HLS installation method\n  ([#533](https://github.com/haskell/vscode-haskell/pull/533)) by @hasufell\n- Fixes related with paths\n  ([#518](https://github.com/haskell/vscode-haskell/pull/518)) by @jneira\n- Reorganize troubleshooting section\n  ([#516](https://github.com/haskell/vscode-haskell/pull/516)) by @jneira\n\n## 1.8.0\n\nThis release includes some interesting new features:\n\n- You can now pass custom environment variables to the lsp server\n  with the `haskell.serverEnvironment` config option per project basis,\n  thanks to [@jacobprudhomme](https://github.com/jacobprudhomme).\n  - For example: `\"haskell.serverEnvironment\": { \"XDG_CACHE_HOME\": \"/path/to/my/cache\" }`\n- With this version the extension will try to use the newer lsp server version\n  which supports the ghc used by the project being loaded, thanks to [@mduerig](https://github.com/mduerig)\n  - WARNING: This will suppose it will use an older version than the latest one,\n    without its features and bug fixes.\n- The extension has lot of more log traces now, which hopefully will help to\n  identify the cause of issues\n\n### Pull requests merged for 1.8.0\n\n- Update supported ghc versions for hls-1.5.1\n  ([#514](https://github.com/haskell/vscode-haskell/pull/514)) by @jneira\n- Fix hole_severity option: Use integer instead of string\n  ([#511](https://github.com/haskell/vscode-haskell/pull/511)) by @mirko-plowtech\n- Update issue templates\n  ([#509](https://github.com/haskell/vscode-haskell/pull/509)) by @jneira\n- Add traces for download hls\n  ([#508](https://github.com/haskell/vscode-haskell/pull/508)) by @jneira\n- support old hls versions compatible with the requested ghc version\n  ([#506](https://github.com/haskell/vscode-haskell/pull/506)) by @mduerig\n- Fix ci: ensure we have a supported ghc version in PATH\n  ([#496](https://github.com/haskell/vscode-haskell/pull/496)) by @jneira\n- Trace environment variables\n  ([#495](https://github.com/haskell/vscode-haskell/pull/495)) by @jneira\n- Pass environment variables to LSP\n  ([#494](https://github.com/haskell/vscode-haskell/pull/494)) by @jacobprudhomme\n- Reorganize README\n  ([#491](https://github.com/haskell/vscode-haskell/pull/491)) by @jneira\n- Fix error handling of server exec discovery in windows\n  ([#486](https://github.com/haskell/vscode-haskell/pull/486)) by @jneira\n- Bump versions of ts, cheerio, mocha\n  ([#485](https://github.com/haskell/vscode-haskell/pull/485)) by @jneira\n- Improve serverExecutablePath description and error when pointing to a directory\n  ([#484](https://github.com/haskell/vscode-haskell/pull/484)) by @jneira\n- Add integration smoke test\n  ([#481](https://github.com/haskell/vscode-haskell/pull/481)) by @jneira\n- Setup the test suite\n  ([#475](https://github.com/haskell/vscode-haskell/pull/475)) by @jneira\n\n## 1.7.1\n\n- Bug fix release due to #471 and fixed with #469 thanks to [@berberman](https://github.com/berberman)\n\n## 1.7.0\n\n- Add an option to set server command line arguments thanks to [@cdsmith](https://github.com/cdsmith) <https://github.com/haskell/vscode-haskell/pull/464>\n  - It includes a new config option `haskell.serverExtraArgs` to being able to pass extra argument to the lsp server executable\n- Update config options to match last haskell-language-server version <https://github.com/haskell/vscode-haskell/pull/463>\n  - It removes `haskell.diagnosticsOnChange` and `haskell.formatOnImportOn` cause they were unused in the server\n  - It adds `haskell.checkProject`, `haskell.maxCompletions` and `haskell.plugin.refineImports.globalOn`\n- Fix showDocumentation command thanks to [@pranaysashank](https://github.com/pranaysashank) <https://github.com/haskell/vscode-haskell/pull/452>\n  - It fixes partially showing the documentation directly in vscode. The documentation is rendered but internal links still does not work\n  - Two config options has been added: `haskell.openDocumentationInHackage` and `haskell.openSourceInHackage` with default value `true`\n    - So documentation will be opened using the hackage url in an external navigator by default\n    - If you prefer having them in vscode you will need to change them to `false`\n- Create output channel only if there are no existing clients thanks to [@pranaysashank](https://github.com/pranaysashank) <https://github.com/haskell/vscode-haskell/pull/448>\n  - This fixes the creation of several output channels for the extension\n\n## 1.6.1\n\n- Fix wrapper call to get project ghc version in windows with spaces in path (<https://github.com/haskell/vscode-haskell/pull/439>)\n\n## 1.6.0\n\n- Bump up vscode version to 1.52.0 (#424) by [@berberman](https://github.com/berberman)\n  - To match the lsp spec version used in haskell-language-version and fix <https://github.com/haskell/haskell-language-server/issues/2068>\n\n## 1.5.1\n\n- Add much more logging in the client side, configured with `haskell.trace.client`\n- Fix error handling of `working out project ghc` and a bug when the path to the executable contains spaces (See #421)\n  - And dont use a shell to spawn the subprocess in non windows systems\n  - Show the progress as a cancellable notification\n- Add commands `Start Haskell LSP server` and `Stop Haskell LSP server`\n\n## 1.5.0\n\n- Emit warning about limited support for ghc-9.x on hls executable download\n- Fix `working out project ghc` progress notificacion\n- Fix tactics config, thanks to @isovector\n- Update server config to match haskell-language-server-1.3.0 one\n\n## 1.4.0\n\n- Restore `resource` scope for `haskell.serverExecutablePath` temporary. The `machine` scope will be set again after giving users a period of time to let them adapt theirs workflows and changing or adding some option in the extension itself to help that adjustement (see #387).\n\n## 1.3.0\n\n- Add `haskell.releasesURL` option to override where to look for HLS releases search for HLS downloads, thanks to @soiamsoNG\n- With this version _the only supported lsp server variant is [`haskell-language-server`](https://github.com/haskell/haskell-language-server)_\n- Add support for generic plugin configuration. Thanks to it, each plugin capability (diagnostics, code actions, code lenses, etc) or the entire plugin can be disabled\n- Add some plugin specic options:\n  - [wingman](https://haskellwingman.dev/) (aka tactics) plugin\n    - `haskell.plugin.tactic.config.features`: Feature set used by the plugin\n    - `haskell.plugin.tactics.config.hole_severity`: The severity to use when showing hole diagnostics\n    - `haskell.plugin.tactic.config.max_use_ctor_actions`: Maximum number of `Use constructor <x>` code actions that can appear\n    - `haskell.plugin.tactics.config.timeout_duration`: The timeout for Wingman actions, in seconds\n  - completions\n    - `haskell.plugin.ghcide-completions.config.autoExtendOn`: Extends the import list automatically when completing a out-of-scope identifier\n    - `haskell.plugin.ghcide-completions.config.snippetsOn`: Inserts snippets when using code completions\n  - type signature lenses - `haskell.plugin.ghcide-type-lenses.config.mode`: Control how type lenses are shown\n- The option `haskell.serverExecutablePath` has now `machine` scope, so it can be only changed globally by the user. It avoids a potential security vulnerability as folders containing `.vscode/settings.json` with that option could execute arbitrary programs.\n- Deprecated options:\n  - `haskell.hlintOn`: use `haskell.plugin.hlint.globalOn` instead.\n  - `haskell.completionSnippetsOn`: use `haskell.plugin.ghcide-completions.config.snippetsOn`\n- Fixed a small typo that caused the server not to be loaded in `.lhs` files, thanks to @Max7cd\n\n## 1.2.0\n\n- Add option to open local documentation on Hackage (@DunetsNM)\n- Add `haskell.updateBehaviour` option to configure when to check for updates\n  (@WorldSEnder)\n- Use locally installed servers on connection failure (@WorldSEnder)\n\n## 1.1.0\n\n- Add Fourmolu as a plugin formatter provider (@georgefst)\n- Remove the `haskell.enable` configuration option, since VS Code now allows\n  you to disable extensions on a per workspace basis\n- Display errors when fetching from the GitHub API properly\n\n## 1.0.1\n\n- Switch the default formatter to Ormolu to match haskell-language-server\n- Fix `haskell.serverExecutablePath` not working with absolute paths on Windows\n  (@winestone)\n- Improve the help text and error message when `haskell.serverExecutablePath`\n  is not found\n- Fix the rendering of the markdown table in the README (@Darren8098)\n\n## 1.0.0\n\n- vscode-haskell now lives under the Haskell organisation\n- Can now download zip archived binaries, which the Windows binaries are now distributed as\n- Improve README (@pepeiborra @jaspervdj)\n\n## 0.1.1\n\n- Fix the restart server and import identifier commands\n\n## 0.1.0\n\n`vscode-hie-server`/`Haskell Language Server` is now just Haskell, and will soon\nbe published under the Haskell organisation as `haskell-vscode`.\nThis release makes haskell-language-server the default langauge server of choice\nand automatically downloads and installs binaries. Installation from source is\nstill supported though and any binaries located on your PATH for the selected\nlangauge server will be used instead.\n\n### Important!\n\nAs part of this, your configuration may be reset as the keys move from\n`languageServerHaskell.completionSnippetsOn` to `haskell.completionSnippetsOn`.\n\n- Fix the document and source browser\n- Remove obselete commands that are no longer supported by any of the language\n  servers\n  - Show type command\n  - Insert type command\n  - HaRe commands\n  - Case split commands\n\n## 0.0.40\n\nChange the way the backend is configured, simplifying it.\n\n- remove wrapper scripts (hie-vscode.sh/hie-vscode.bat)\n- dropdown choice between `haskell-ide-engine`, `haskell-language-server` or\n  `ghcide` in the `hieVariant` setting.\n- this can be overridden by an explicit `hieExecutablePath`, as before.\n\n## 0.0.39\n\nRemove verbose logging option, it is not longer supported.\n\n## 0.0.38\n\nBump dependencies\n\n## 0.0.37\n\nTrying again, working 0.0.35\n\n- Add Restart command (@gdziadkiewicz)\n- Add Ormolu as a formatter option (@DavSanchez)\n- Update README\n\n## 0.0.36\n\n- Roll back to 0.0.34\n\n## 0.0.35\n\n- Add Restart command (@gdziadkiewicz)\n- Add Ormolu as a formatter option (@DavSanchez)\n- Update README\n\n## 0.0.34\n\n- Remove --lsp parameter from hie-vscode.bat\n\n## 0.0.33\n\n- Introduced configuration setting `noLspParam`, default `false` to control\n  setting the `--lsp` flag for the hie server. So by default we will set the\n  command line argument for the server, but it can be turned off.\n\n## 0.0.32\n\n- Re-enable the `--lsp` flag for the hie server\n- Update some deps for security vulnerabilities\n\n## 0.0.31\n\n- Log to stderr (vscode output) by default, add option for logfile (@bubba)\n\n## 0.0.30\n\n- Bundle using webpack (@chrismwendt)\n- Bump protocol version to 3.15 prerelease (@alanz)\n  This allows working progress reporting from hie.\n- Update casesplit plugin (@Avi-D-coder)\n\n## 0.0.29\n\n- bump protocol version to 3.15 (prerelease) (@alanz)\n- upgrade deps, including avoiding vulnerabilities on lodash (@alanz)\n- warn about compile time and wrapped hie (@janat08)\n\n## 0.0.28\n\n- remove unused `lsp` flag (@bubba)\n- do not start `hie` if `hie-wrapper` crashes (@bubba)\n- Expose diagnosticsOnChange option for settings (Frederik Ramcke)\n- Avoid CVE on `extend` package\n- Enable displaying window progress (@bubba)\n\n## 0.0.27\n\n- Re-enable search feature for documentation (@anonimitoraf)\n  Accesed via `ctrl-f`.\n\n## 0.0.26\n\n- Show documentation content using Webview API (@EdAllonby)\n- npm audit fix (@alanz)\n\n## 0.0.25\n\n- Add vsce dependency to \"Contributing\" document (@EdAllonby)\n- Add formatterProvider config (@bubba)\n- Bugfix for stack version on windows (@beauzeaux)\n- Update settings to match hie version 0.7.0.0 (@alanz)\n- npm audit fix (@bubba)\n\n## 0.0.24\n\n- Add snippet config option (@bubba)\n\n## 0.0.23\n\n- Fix multi-process issue, where vscode would launch multiple hie instances.\n  By @kfigiela\n\n## 0.0.22\n\n- Add configuration option to enable liquid haskell processing. This\n  is a preview feature of hie from\n  ca2d3eaa19da8ec9d55521b461d8e2e8cffee697 on 2019-09-05.\n\n## 0.0.21\n\n- Remove languageServerHaskell.useHieWrapper, We now use hie-wrapper\n  by default.\n- Update the vscode-languageclient to v4.4.0\n- Fix #98 Import identifier insertion line `moduleLine` is now the\n  first line that is (trimmed) `where` or ends with `where` or ends\n  with `)where`. (@mpilgrem)\n\n## 0.0.20\n\n- Add the case-split function (@txsmith). Required hie >= 0.2.1.0\n- Update the vscode-languageclient to v4.2.0 (@Bubba)\n- Use the hie-wrapper executable now installed with hie to choose the\n  right version of hie to use for the given project.\n\n## 0.0.19\n\n- Fix hie launch on windows with logging off (#90). Thanks @Tehnix.\n\n## 0.0.18\n\n- Support GHC 8.4.3 in the wrapper file\n- The `languageServerHaskell.trace.server` parameter now affects\n  `/tmp/hie.log`, as well as ghc-mod `--vomit` output.\n- Add an Import identifier command, by @chrismwendt\n\n## 0.0.17\n\n- Support GHC 8.4.2 in the wrapper file\n- Update dependencies to avoid security vulnerability.\n- Use os.tmpdir() for the hie.log file\n\n## 0.0.15\n\nSupport the new webview-api for the documentation browser, thanks to @AlexeyRaga.\n\n## 0.0.14\n\nRevert `vscode-languageclient` dependency to version 3.5.0, since version 4.x for some\nreason breaks the documentation browser.\n\n## 0.0.13\n\nAdd configuration to set the path to your HIE executable, if it's not on your PATH. Note\nthat this adds the `--lsp` argument to the call of this executable.\n\n## 0.0.12\n\nAdd configuration to enable/disable HIE, useful for multi-root workspaces.\n\n## 0.0.11\n\nAdd additional marketplace categories.\n\n## 0.0.10\n\nAdd support for multi-root workspaces, thanks to @tehnix. See the README section\non [_Using multi-root workspaces_](https://github.com/alanz/vscode-hie-server#using-multi-root-workspaces) for more.\n\n## 0.0.9\n\nPublish to the visual studio marketplace through travis CI via git tags. E.g.\n`git tag -a 0.0.9 -m \"Version 0.0.9\"` and then `git push origin 0.0.9`.\n\n## 0.0.8\n\nAdd new haskell-ide-engine logo, thanks to @damienflament\n\nAdd rudimentary support for detecting the project GHC version and using the\nappropriate hie version. This currently only works on Linux (contributors on\nother platforms, please jump in with appropriate scripts) and requires\n`haskell-ide-engine` built via the `Makefile` added in\nhttps://github.com/haskell/haskell-ide-engine/pull/447. Thanks to @Tehnix\n\n## 0.0.7\n\nUpdate `package-lock.json` to fresh dependencies.\n\nAdd show type _of selected expression_ on hover feature, by @halhenke\n\nAdded options for how to display the same information when using the show type\ncommand menu, by @halhenke\n\nMoved the configuration setting about showing trace information into the proper\nscope, by @halhenke\n\n## 0.0.6\n\nUpdate `package-lock.json` to fresh dependencies.\n\nUpdate the installation check on Win32 platforms, by @soylens.\n\nUse `tslint` on the plugin sources, by @halhenke.\n\n## 0.0.5\n\nStop the output channel from taking focus on startup, by @Tehnix and @halhenke\n\nRework and improve the document layout, for gihub and the marketplace, by @Tehnix\n\nSet up Travis testing an potential auto-deply to marketplace, by @Tehnix\n\n## 0.0.4\n\nShow documents in a tab, by @AlexeyRaga\n\nAdd a configuration option to enable/disable `hlint`.\n\n## 0.0.3\n\nAdd \"Haskell: Show type\" command, bound to Ctrl-alt-t (Cmd-alt-t on mac). This\ncalls the `ghc-mod` `type` command on the current cursor location or highlighted\nregion. Thanks to @AlexeyRaga\n\nAdd a check for having the `hie` executable in the path on startup, to prevent\nan endless failure to start if the executable is not there. Thanks to @DavidEichman\n\n## 0.0.2\n\nAdd some HaRe commands, accesible via the command palette.\n\n## 0.0.1\n\nInitial release of haskell-ide-engine VS Code extension, for brave pioneers.\n"
  },
  {
    "path": "GenChangelogs.hs",
    "content": "#!/usr/bin/env cabal\n{- cabal:\nbuild-depends: base, bytestring, process, text, github, time >= 1.9\n-}\n\n{-# LANGUAGE OverloadedStrings #-}\n{-# LANGUAGE RecordWildCards   #-}\n\nimport           Control.Monad\nimport qualified Data.ByteString.Char8    as BS\nimport           Data.List\nimport           Data.Maybe\nimport qualified Data.Text                as T\nimport           Data.Time.Format.ISO8601\nimport           Data.Time.LocalTime\nimport           GitHub\nimport           System.Environment\nimport           System.Process\n\nmain = do\n  callCommand \"git fetch --tags\"\n  tag <- last . lines <$>\n    readProcess \"git\" [\"tag\", \"--list\", \"--sort=v:refname\"] \"\"\n\n  lastDateStr <- last . lines <$> readProcess \"git\" [\"show\", \"-s\", \"--format=%cI\", \"-1\", tag] \"\"\n  lastDate <- zonedTimeToUTC <$> iso8601ParseM lastDateStr\n\n  args <- getArgs\n  let githubReq = case args of\n                    []      -> github'\n                    token:_ -> github (OAuth $ BS.pack token)\n  prs <- githubReq $ pullRequestsForR \"haskell\" \"vscode-haskell\" stateClosed FetchAll\n  let prsAfterLastTag = either (error . show)\n                        (foldMap (\\pr -> [pr | inRange pr, isNotDependabot pr]))\n                        prs\n      inRange pr\n        | Just mergedDate <- simplePullRequestMergedAt pr = mergedDate > lastDate\n        | otherwise = False\n\n      isNotDependabot SimplePullRequest{..} =\n        untagName (simpleUserLogin simplePullRequestUser) /= \"dependabot[bot]\"\n\n  forM_ prsAfterLastTag $ \\SimplePullRequest{..} ->\n    putStrLn $ T.unpack $ \"- \" <> simplePullRequestTitle <> \"\\n\" <>\n      \"  ([#\" <> T.pack (show $ unIssueNumber simplePullRequestNumber) <> \"](\" <> getUrl simplePullRequestHtmlUrl <> \"))\" <>\n      \"  by @\" <> untagName (simpleUserLogin simplePullRequestUser)\n"
  },
  {
    "path": "LICENSE",
    "content": "Based on https://github.com/Microsoft/vscode-languageserver-node-example\nwhich has the following license requirement :\n\n-----------------------------------------------------\n\nCopyright (c) Microsoft Corporation\n\nAll rights reserved. \n\nMIT License\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE."
  },
  {
    "path": "README.md",
    "content": "# Haskell for Visual Studio Code\n\n[![vsmarketplacebadge](https://vsmarketplacebadges.dev/version/haskell.haskell.png)](https://marketplace.visualstudio.com/items?itemName=haskell.haskell)\n\nThis extension adds language support for [Haskell](https://haskell.org), powered by the [Haskell Language Server](https://github.com/haskell/haskell-language-server).\nAs almost all features are provided by the server you might find interesting read its [documentation](https://haskell-language-server.readthedocs.io).\n\n## Table of Contents\n\n- [Haskell for Visual Studio Code](#haskell-for-visual-studio-code)\n  - [Table of Contents](#table-of-contents)\n  - [Setup](#setup)\n  - [Features](#features)\n  - [Requirements](#requirements)\n  - [Configuration options](#configuration-options)\n    - [Path to server executable](#path-to-server-executable)\n      - [Security warning](#security-warning)\n    - [Set additional environment variables for the server](#set-additional-environment-variables-for-the-server)\n    - [Downloaded binaries](#downloaded-binaries)\n    - [Setting a specific toolchain](#setting-a-specific-toolchain)\n    - [Supported GHC versions](#supported-ghc-versions)\n  - [Using multi-root workspaces](#using-multi-root-workspaces)\n  - [Investigating and reporting problems](#investigating-and-reporting-problems)\n  - [FAQ](#faq)\n    - [Troubleshooting](#troubleshooting)\n      - [Check issues and tips in the haskell-language-server project](#check-issues-and-tips-in-the-haskell-language-server-project)\n      - [Restarting the language server](#restarting-the-language-server)\n      - [`Failed to get project GHC version` on darwin M1 with stack](#failed-to-get-project-ghc-version-on-darwin-m1-with-stack)\n      - [`GHC ABIs don't match`](#ghc-abis-dont-match)\n      - [Using an old configuration](#using-an-old-configuration)\n      - [Stack/Cabal/GHC can not be found](#stackcabalghc-can-not-be-found)\n  - [Contributing](#contributing)\n  - [Release Notes](#release-notes)\n\n## Setup\n\nThis Extension comes with \"batteries\"-included and can manage your Haskell Language Server installations for you,\npowered by [GHCup](https://www.haskell.org/ghcup/).\nInstallation of [GHCup](https://www.haskell.org/ghcup/) can not happen automatically, so if you want your HLS installations to be\nmanaged by the Extension, you will have to follow the [installation instructions for GHCup](https://www.haskell.org/ghcup/).\n\n**Note:** Make sure you have a working `ghcup` installation, before launching the Extension.\n\n## Features\n\nYou can watch demos for some of these features [here](https://haskell-language-server.readthedocs.io/en/latest/features.html#demos).\n\n- Warning and error diagnostics from GHC\n- Type information and documentation on hover\n- Jump to definition: [for now only for local code definitions](https://github.com/haskell/haskell-language-server/issues/708)\n- Document symbols\n- Highlight references in document\n- Code completion\n- Show documentation and sources in hackage\n- Formatting via [Brittany](https://github.com/lspitzner/brittany), [Floskell](https://github.com/ennocramer/floskell), [Fourmolu](https://github.com/fourmolu/fourmolu), [Ormolu](https://github.com/tweag/ormolu) or [Stylish Haskell](https://github.com/haskell/stylish-haskell)\n- [Multi-root workspace](https://code.visualstudio.com/docs/editor/multi-root-workspaces) support\n- [Code evaluation](https://haskell-language-server.readthedocs.io/en/latest/features.html#code-evaluation), see its [Tutorial](https://github.com/haskell/haskell-language-server/blob/master/plugins/hls-eval-plugin/README.md)\n- [Integration with](https://haskell-language-server.readthedocs.io/en/latest/features.html#retrie-integration) [retrie](https://hackage.haskell.org/package/retrie), a powerful, easy-to-use codemodding tool\n- [Code lenses for explicit import lists](https://haskell-language-server.readthedocs.io/en/latest/features.html#explicit-import-lists)\n- [Generate functions from type signatures, and intelligently complete holes using](https://haskell-language-server.readthedocs.io/en/latest/features.html#wingman) [Wingman (tactics)](https://github.com/haskell/haskell-language-server/tree/master/plugins/hls-tactics-plugin)\n- [Integration](https://haskell-language-server.readthedocs.io/en/latest/features.html#hlint) with [hlint](https://github.com/ndmitchell/hlint), the most used haskell linter, to show diagnostics and apply hints via [apply-refact](https://github.com/mpickering/apply-refact)\n- [Module name suggestions](https://haskell-language-server.readthedocs.io/en/latest/features.html#module-names) for insertion or correction\n- [Call hierarchy support](https://haskell-language-server.readthedocs.io/en/latest/features.html#call-hierarchy)\n\n## Requirements\n\n- For standalone `.hs`/`.lhs` files, [ghc](https://www.haskell.org/ghc/) must be installed and on the PATH. The easiest way to install it is with [ghcup](https://www.haskell.org/ghcup/).\n- For Cabal based projects, both ghc and [cabal-install](https://www.haskell.org/cabal/) must be installed and on the PATH. It can also be installed with [ghcup](https://www.haskell.org/ghcup/) or [Chocolatey](https://www.haskell.org/platform/windows.html) on Windows.\n- For Stack based projects, [stack](http://haskellstack.org) must be installed and on the PATH and must be [configured to use GHC binaries installed by GHCup](https://www.haskell.org/ghcup/guide/#stack-integration).\n- If you are installing from an offline VSIX file, you need to install [language-haskell](https://github.com/JustusAdam/language-haskell) too after installation (either from the marketplace or offline).\n- Alternatively, you can let the extension manage your entire toolchain automatically (you'll be asked on first startup) via\n  [ghcup](https://www.haskell.org/ghcup/), which should be pre-installed\n\n## Configuration options\n\nFor a general picture about the server configuration, including the project setup, [you can consult the server documentation about the topic](https://haskell-language-server.readthedocs.io/en/latest/configuration.html).\n\nFor information on how to set configuration in VSCode, see [here](https://code.visualstudio.com/docs/getstarted/settings).\n\n### Path to server executable\n\nIf your server is manually installed and not on your path, you can also manually set the path to the executable.\n\n```json\n\"haskell.serverExecutablePath\": \"~/.local/bin/haskell-language-server\"\n```\n\nThere are a few placeholders which will be expanded:\n\n- `~`, `${HOME}` and `${home}` will be expanded into your users' home folder.\n- `${workspaceFolder}` and `${workspaceRoot}` will expand into your current project root.\n\n#### Security warning\n\nThe option has `machine-overridable` scope so it can be changed per workspace.\nThis supposes it could be used to execute arbitrary programs adding a `.vscode/settings.json` in the workspace folder including this option with the appropriate path.\nSee [#387](https://github.com/haskell/vscode-haskell/issues/387) for more details.\n\n### Set additional environment variables for the server\n\nYou can add additional environment variables for the lsp server using the configuration option `haskell.serverEnvironment`. For example, to change the cache directory used by the server you could set:\n\n```json\n{ \"haskell.serverEnvironment\": { \"XDG_CACHE_HOME\": \"/path/to/my/cache\" } }\n```\n\nas the server uses the XDG specification for cache directories.\n\nThe environment _only will be visible for the lsp server_, not for other extension tasks like find the server executable.\n\n### Downloaded binaries\n\nThis extension will download `haskell-language-server` binaries and the rest of the toolchain if you selected to use GHCup during\nfirst start. Check the `haskell.manageHLS` setting.\n\nIt will then download the newest version of haskell-language-server which has support for the required ghc.\nThat means it could use an older version than the latest one, without the last features and bug fixes.\nFor example, if a project needs ghc-8.10.4 the extension will download and use haskell-language-server-1.4.0, the latest version which supported ghc-8.10.4. Even if the latest global haskell language-server version is 1.5.1.\n\nIf you have disk space issues, check `ghcup gc --help`.\n\nYou can also instruct the extension to use a different installation directory for the toolchain,\ne.g. to not interfere with system GHCup installation. Depending on your platform, add the full\nresolved path like so:\n\n```json\n  \"haskell.serverEnvironment\": {\n    \"GHCUP_INSTALL_BASE_PREFIX\": \"/home/foo/.config/Code/User/globalStorage/haskell.haskell/\"\n  }\n```\n\nThe internal storage paths for the extension depend on the platform:\n\n| Platform | Path                                                                            |\n| -------- | ------------------------------------------------------------------------------- |\n| macOS    | `~/Library/Application\\ Support/Code/User/globalStorage/haskell.haskell/.ghcup` |\n| Windows  | `%APPDATA%\\Code\\User\\globalStorage\\haskell.haskell\\ghcup`                       |\n| Linux    | `$HOME/.config/Code/User/globalStorage/haskell.haskell/.ghcup`                  |\n\nIf you want to manage HLS yourself, set `haskell.manageHLS` to `PATH` and make sure HLS is in your PATH\nor set `haskell.serverExecutablePath` (overrides all other settings) to a valid executable.\n\nIf you need to set mirrors for ghcup download info, check the settings `haskell.metadataURL` and `haskell.releasesURL`.\n\n### Setting a specific toolchain\n\nWhen `manageHLS` is set to `GHCup`, you can define a specific toolchain (`hls`, `ghc`, `cabal` and `stack`),\neither globally or per project. E.g.:\n\n```json\n{\n  \"haskell.toolchain\": {\n    \"hls\": \"1.6.1.1\",\n    \"cabal\": \"recommended\",\n    \"stack\": null\n  }\n}\n```\n\nThis means:\n\n1. install the `ghc` version corresponding to the project (default, because it's omitted)\n2. install `hls` 1.6.1.1\n3. install the recommended `cabal` version from ghcup\n4. don't install any `stack` version\n\nAnother config could be:\n\n```json\n{\n  \"haskell.toolchain\": {\n    \"ghc\": \"9.2.2\",\n    \"hls\": \"latest\",\n    \"cabal\": \"recommended\"\n  }\n}\n```\n\nMeaning:\n\n1. install `ghc` 9.2.2 regardless of what the project requires\n2. always install latest `hls`, even if it doesn't support the given GHC version\n3. install recommended `cabal`\n4. install latest `stack` (default, because it's omitted)\n\nThe defaults (when omitted) are as follows:\n\n1. install the project required `ghc` (corresponding to `with-compiler` setting in `cabal.project` for example)\n2. install the latest `hls` version that supports the project required ghc version\n3. install latest `cabal`\n4. install latest `stack`\n\nWhen a the value is `null`, the extension will refrain from installing it.\n\nAt last, if you don't want `ghcup` to manage any of the external tools except `hls`, you can use:\n\n```json\n{\n  \"haskell.toolchain\": {\n    \"ghc\": null,\n    \"cabal\": null,\n    \"stack\": null\n  }\n}\n```\n\n### Supported GHC versions\n\nYou can check each GHC version's support status and the policy followed for deprecations [here](https://haskell-language-server.readthedocs.io/en/latest/support/ghc-version-support.html).\n\n[Building from source](https://haskell-language-server.readthedocs.io/en/latest/installation.html) may support more versions!\n\nThe exact list of binaries can be checked in the last release of haskell-language-server: <https://github.com/haskell/haskell-language-server/releases/latest>\n\n## Using multi-root workspaces\n\nFirst, check out [what multi-root workspaces](https://code.visualstudio.com/docs/editor/multi-root-workspaces) are. The idea of using multi-root workspaces, is to be able to work on several different Haskell projects, where the GHC version or stackage LTS could differ, and have it work smoothly.\n\nThe language server is now started for each workspace folder you have in your multi-root workspace, and several configurations are on a resource (i.e. folder) scope, instead of window (i.e. global) scope.\n\n## Investigating and reporting problems\n\n1. Go to extensions and right click `Haskell` and choose `Extensions Settings`\n2. Scroll down to `Haskell › Trace: Server` and set it to `messages`.\n3. Set `Haskell › Trace: Client` to `debug`. It will print all the environment variables so take care it does not contain any sensitive information before sharing it.\n4. Restart vscode and reproduce your problem\n5. Go to the main menu and choose `View -> Output` (`Ctrl + Shift + U`)\n6. On the new Output panel that opens on the right side in the drop down menu choose `Haskell (<your project>)`\n\nPlease include the output when filing any issues on the [haskell-language-server](https://github.com/haskell/haskell-language-server/issues/new) issue tracker.\n\n## FAQ\n\n### Troubleshooting\n\n#### Check issues and tips in the haskell-language-server project\n\n- Usually the error or unexpected behaviour is already reported in the [haskell language server issue tracker](https://github.com/haskell/haskell-language-server/issues). Finding the issue could be useful to help resolve it and sometimes includes a workaround for the issue.\n- You can also check the [troubleshooting section](https://haskell-language-server.readthedocs.io/en/latest/troubleshooting.html) in the server documentation.\n\n#### Restarting the language server\n\n- Sometimes the language server might get stuck in a rut and stop responding to your latest changes.\n  Should this occur you can try restarting the language server with <kbd>Ctrl</kbd> <kbd>shift</kbd> <kbd>P</kbd>/<kbd>⌘</kbd> <kbd>shift</kbd> <kbd>P</kbd> > Restart Haskell LSP Server.\n\n#### `Failed to get project GHC version` on darwin M1 with stack\n\nIf you have installed stack via the official cannels, the binary will not be M1 native, but x86 and trigger the rosetta compatibility layer. GHCup provides real stack/HLS M1 binaries, so make sure you install stack via GHCup. Also see https://github.com/haskell/haskell-language-server/issues/2864\n\n#### `GHC ABIs don't match`\n\nIf you are using certain versions of GHC (such as 9.0.2 or 9.2.4) while running Stack, you may encounter this issue due to an outdated GHC bindist installed by Stack. The vscode-haskell extension does not support the use of GHC binaries managed by Stack. Therefore, it is recommended to configure Stack to allow GHCup to install these binaries instead. Please [refer to the instructions provided by ghcup for this purpose](https://www.haskell.org/ghcup/guide/#stack-integration).\n\nIf you really want to use GHC 9.0.2 managed by Stack, force it to install the fixed bindist (that includes profiling libs) by adding this to your stack.yaml (depending on your platform):\n\n```yml\nsetup-info:\n  ghc:\n    linux64-tinfo6:\n      9.0.2:\n        url: 'https://downloads.haskell.org/ghc/9.0.2/ghc-9.0.2a-x86_64-fedora27-linux.tar.xz'\n```\n\nNow make sure to remove cached/installed libraries to avoid getting segfaults at runtime.\n\nAs a final workaround, you can try to compile HLS from source (the extension should pick it up) via ghcup, see [https://haskell-language-server.readthedocs.io/en/stable/installation.html#ghcup](https://haskell-language-server.readthedocs.io/en/stable/installation.html#ghcup). In any case, the recommended approach is to let GHCup install the GHC binaries.\n\n#### `hGetContents: invalid argument (invalid byte sequence)`\n\nThis problem was encountered on darwin M2 with ghcup.\nShould you see the error that the \"Haskell server crashed 5 times in the last 3 minutes,\" you can check the Haskell output to see whether this was due to an error mentioning `hGetContents: invalid argument (invalid byte sequence)`.\nIf this is the case, setting `terminal.integrated.detectLocale` to `off` might resolve your issue.\n\n#### Using an old configuration\n\nIf something just doesn't work, but you recall an old configuration that did, you\nmay try forcing a particular setting, e.g. by disabling all automatic installations\nexcept HLS:\n\n```json\n    \"haskell.toolchain\": {\n        \"hls\": \"1.6.1.0\",\n        \"ghc\": null,\n        \"cabal\": null,\n        \"stack\": null\n    }\n```\n\n#### Stack/Cabal/GHC can not be found\n\nAlso make sure GHCup is installed and in `$PATH`. If you're not starting VSCode from the terminal, you might need to add `${HOME}/.ghcup/bin` to PATH like so:\n\n```json\n  \"haskell.serverEnvironment\": {\n    \"PATH\": \"${HOME}/.ghcup/bin:$PATH\"\n  }\n```\n\n## Contributing\n\nIf you want to help, get started by reading [Contributing](./docs/Contributing.md) for more details.\n\n## Release Notes\n\nSee the [Changelog](./Changelog.md) for more details.\n"
  },
  {
    "path": "docs/Contributing.md",
    "content": "# Contributing\n\n## Dependencies and Building\n\nRun `npm install` in the project root to install the development dependencies.\n\nYou can also package up the extension with\n\n- `npm install` to install the dependencies\n- `npm run package` which creates an extension package at `haskell-<version>.vsix`.\n\n_Note:_ that if you get errors running `vsce package`, it might help running `npm run pretest` directly, since that gives the actual error output of the TypeScript compilation.\n\n## Developing inside VS Code\n\n- Launch VS Code, press `File` > `Open Folder`, open the `vscode-haskell` folder;\n- press `F5` to open a new window with the `vscode-haskell` loaded (this will overwrite existing ones, e.g. from the marketplace);\n- open a Haskell file with the **new** editor to test the LSP client;\n\nYou are now ready to make changes and debug. You can,\n\n- set breakpoints in your code inside `src/extension.ts` to debug your extension;\n- find output from your extension in the debug console;\n- make changes to the code, and then\n- relaunch the extension from the debug toolbar\n\n_Note_: you can also reload (`Ctrl+R` or `Cmd+R` on macOS) the VS Code window with your extension to load your changes\n\n#### Formatting\n\n[prettier](https://prettier.io) is automatically run on each commit via husky. If you are developing within VS Code, the settings are set to auto format on save.\nThe configurations for prettier are located in `.prettierrc`.\n\n## Navigating the Files\n\nA brief overview of the files,\n\n- `package.json` contains the basic information about the package, see [the full manifest for more](https://code.visualstudio.com/docs/extensionAPI/extension-manifest), such as telling VS Code which scope the LSP works on (Haskell and Literate Haskell in our case), and possible configuration\n- `src/extension.ts` is the main entrypoint to the extension, and handles launching the language server.\n- `src/hlsBinaries.ts` handles automatically installing the pre-built `haskell-language-server` binaries\n- `src/utils.ts` has some functions for downloading files and checking if executables are on the path\n- `src/docsBrowser.ts` contains the logic for displaying the documentation browser (e.g. hover over a type like `mapM_` and click `Documentation` or `Source`)\n\n## Helpful Reading Material\n\nWe recommend checking out [Your First VS Code Extension](https://code.visualstudio.com/docs/extensions/example-hello-world) and [Creating a Language Server](https://code.visualstudio.com/docs/extensions/example-language-server) for some introduction to VS Code extensions.\n"
  },
  {
    "path": "docs/Release.md",
    "content": "# Release Checklist\n\nFollow this list for items that must be completed for release of the `vscode-haskell` extension.\n\n- [ ] Run `npm audit` for security vulnerabilities.\n  - Fix vulnerabilities.\n- [ ] Run `npm outdated` to find outdated package version, review what needs to be updated.\n- [ ] Run `cat test/testdata/schema/*/vscode-extension-schema.golden.json | jq --sort-keys -s add` in the `haskell-language-server` repo and add new configuration items.\n- [ ] SemVer Compatible Version Bump in `package.json`\n  - For pre-releases, we follow the version convention at: https://code.visualstudio.com/api/working-with-extensions/publishing-extension#prerelease-extensions. We use `major.EVEN_NUMBER.patch` for release versions and `major.ODD_NUMBER.patch` for pre-release versions. For example: `2.0.*` for release and `2.1.*` for pre-release.\n- [ ] `git switch -c release-<version>`\n- [ ] Update ChangeLog.md. The output of `./GenChangelogs.hs` usually suffices.\n- [ ] Update the README.md to have no outdated information.\n- [ ] Make sure CI is succeeding.\n- [ ] Perform the release by creating a [release in Github](https://github.com/haskell/vscode-haskell/releases)\n  - Github actions will automatically release the extension to VSCode- and VSX-Marketplace.\n  - If you want to create a pre-release, create a [pre-release in Github](https://github.com/haskell/vscode-haskell/releases). The github action will perform the appropriate actions automatically and publish the pre-release of the extension to VSCode- and VSX-Marketplace.\n\n## Branching policy\n\nSometimes there is a release (stable) and pre-release (unstable) at the same time and we need to do a release for the stable release and sometimes we need to do a release for the pre-release series.\nTo simplify the release management, the following policy is in place:\n\n- The branch `master` contains the current pre-release\n  - As such, its `package.json` must always have the form `major.ODD_NUMBER.patch`\n  - Dependency version bumps are automatically performed by dependabot against `master`\n  - For each release, a tag must be created\n- Stable releases are located on a separate branch called `release-<major.EVEN_NUMBER>`\n  - Before a release, the branch is rebased on top of current master\n  - For each stable release, a tag must be created of the form `major.EVEN_NUMBER.patch`\n\n## Release CI\n\nThe release CI has access tokens for VSX Marketplace and the VSCode Marketplace.\n\nSeemingly, the VSX Marketplace token does not expire. If it is lost for some reason, follow the steps below. Fendor can also generate a VSX Marketplace token.\n\nThe latter needs to be refreshed once a year.\n\n- Send an email to `committee@haskell.org` requesting the token\n  - Include your public GPG key so they can send you the token encrypted\n- Update the repository secrets\n  - People from the [@haskell-ide](https://github.com/orgs/haskell/teams/haskell-ide) have full access to the vscode-haskell repo and can update secrets\n\nLast time the VSCode Marketplace token was updated: 2023-08-17\n"
  },
  {
    "path": "eslint.config.mjs",
    "content": "import globals from 'globals';\nimport eslint from '@eslint/js';\nimport { defineConfig } from 'eslint/config';\nimport tseslint from 'typescript-eslint';\n\nexport default defineConfig(\n  { files: ['**/*.{js,mjs,cjs,ts}'] },\n  {\n    languageOptions: {\n      globals: globals.node,\n      parserOptions: { projectService: true, tsconfigRootDir: import.meta.dirname },\n    },\n  },\n  {\n    // disables type checking for this file only\n    files: ['eslint.config.mjs'],\n    extends: [tseslint.configs.disableTypeChecked],\n  },\n  eslint.configs.recommended,\n  tseslint.configs.recommendedTypeChecked,\n  {\n    rules: {\n      // turn off these lints as we access workspaceConfiguration fields.\n      // So far, there was no bug found with these unsafe accesses.\n      '@typescript-eslint/no-unsafe-assignment': 'off',\n      '@typescript-eslint/no-unsafe-member-access': 'off',\n      // Sometimes, the 'any' just saves too much time.\n      '@typescript-eslint/no-explicit-any': 'off',\n      '@typescript-eslint/no-floating-promises': 'error',\n      '@typescript-eslint/no-unused-vars': [\n        'error',\n        {\n          args: 'all',\n          argsIgnorePattern: '^_',\n          caughtErrors: 'all',\n          caughtErrorsIgnorePattern: '^_',\n          destructuredArrayIgnorePattern: '^_',\n          varsIgnorePattern: '^_',\n          ignoreRestSiblings: true,\n        },\n      ],\n    },\n  },\n);\n"
  },
  {
    "path": "flake.nix",
    "content": "{\n  description = \"VS Code Haskell extension development environment\";\n\n  inputs = {\n    nixpkgs.url = \"github:NixOS/nixpkgs/nixos-unstable\";\n    # flake-utils for cross-platform support\n    flake-utils.url = \"github:numtide/flake-utils\";\n  };\n\n  # Outputs define what our flake produces\n  outputs =\n    {\n      self,\n      nixpkgs,\n      flake-utils,\n    }:\n    # This function creates outputs for each system (x86_64-linux, aarch64-darwin, etc.)\n    flake-utils.lib.eachDefaultSystem (\n      system:\n      let\n        # Import nixpkgs for our specific system\n        pkgs = nixpkgs.legacyPackages.${system};\n\n      in\n      {\n        # Development shell with Node.js and VS Code extension tools\n        devShells.default = pkgs.mkShell {\n          name = \"vscode-haskell-dev\";\n\n          packages = with pkgs; [\n            # Node.js runtime (LTS version)\n            nodejs_20\n\n            # Package managers\n            corepack # Enables yarn/npm/pnpm via packageManager field\n\n            # VS Code extension development\n            vscode # For testing extension\n            vsce # VS Code Extension CLI (publish/package)\n\n            # Additional tools\n            git\n            nixpkgs-fmt\n          ];\n\n          shellHook = ''\n            echo \"VS Code Haskell Extension Dev Environment\"\n            echo \"Node: $(node --version)\"\n            echo \"npm: $(npm --version 2>/dev/null || echo 'not active')\"\n            echo \"\"\n            echo \"Available commands:\"\n            echo \"  npm install     - Install dependencies\"\n            echo \"  npm build       - Build extension with webpack\"\n            echo \"  npm run watch   - Watch mode\"\n            echo \"  vsce package     - Create .vsix package\"\n            echo \"  vsce publish     - Publish to marketplace\"\n          '';\n        };\n      }\n    );\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"haskell\",\n  \"displayName\": \"Haskell\",\n  \"description\": \"Haskell language support powered by the Haskell Language Server\",\n  \"version\": \"2.8.0\",\n  \"license\": \"MIT\",\n  \"publisher\": \"haskell\",\n  \"engines\": {\n    \"vscode\": \"^1.102.0\"\n  },\n  \"keywords\": [\n    \"language\",\n    \"haskell\",\n    \"cabal\",\n    \"stack\",\n    \"lsp\",\n    \"multi-root ready\"\n  ],\n  \"homepage\": \"https://github.com/haskell/vscode-haskell\",\n  \"repository\": \"https://github.com/haskell/vscode-haskell.git\",\n  \"bugs\": {\n    \"url\": \"https://github.com/haskell/vscode-haskell/issues\"\n  },\n  \"categories\": [\n    \"Programming Languages\",\n    \"Formatters\",\n    \"Linters\",\n    \"Other\"\n  ],\n  \"icon\": \"images/hls-logo.png\",\n  \"galleryBanner\": {\n    \"color\": \"#22172A\",\n    \"theme\": \"dark\"\n  },\n  \"activationEvents\": [\n    \"onLanguage:haskell\",\n    \"onLanguage:literate haskell\",\n    \"onLanguage:cabal\"\n  ],\n  \"main\": \"./dist/extension\",\n  \"contributes\": {\n    \"languages\": [\n      {\n        \"id\": \"haskell\",\n        \"aliases\": [\n          \"Haskell\",\n          \"haskell\"\n        ],\n        \"extensions\": [\n          \".hs\"\n        ]\n      },\n      {\n        \"id\": \"cabal\",\n        \"aliases\": [\n          \"Cabal\"\n        ],\n        \"extensions\": [\n          \".cabal\"\n        ]\n      },\n      {\n        \"id\": \"literate haskell\",\n        \"aliases\": [\n          \"Literate Haskell\",\n          \"literate Haskell\"\n        ],\n        \"extensions\": [\n          \".lhs\"\n        ]\n      }\n    ],\n    \"configuration\": {\n      \"type\": \"object\",\n      \"title\": \"Haskell\",\n      \"properties\": {\n        \"haskell.formattingProvider\": {\n          \"scope\": \"resource\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"brittany\",\n            \"floskell\",\n            \"fourmolu\",\n            \"ormolu\",\n            \"stylish-haskell\",\n            \"none\"\n          ],\n          \"default\": \"ormolu\",\n          \"description\": \"The formatter to use when formatting a document or range. Ensure the plugin is enabled.\"\n        },\n        \"haskell.cabalFormattingProvider\": {\n          \"scope\": \"resource\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"cabal-gild\",\n            \"cabal-fmt\",\n            \"none\"\n          ],\n          \"default\": \"cabal-gild\",\n          \"description\": \"The formatter to use when formatting a document or range of a cabal formatter. Ensure the plugin is enabled.\"\n        },\n        \"haskell.openDocumentationInHackage\": {\n          \"scope\": \"resource\",\n          \"type\": \"boolean\",\n          \"default\": true,\n          \"description\": \"When opening 'Documentation' for external libraries, open in hackage by default. Set to false to instead open in vscode.\"\n        },\n        \"haskell.openSourceInHackage\": {\n          \"scope\": \"resource\",\n          \"type\": \"boolean\",\n          \"default\": true,\n          \"description\": \"When opening 'Source' for external libraries, open in hackage by default. Set to false to instead open in vscode.\"\n        },\n        \"haskell.trace.server\": {\n          \"scope\": \"resource\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"off\",\n            \"messages\",\n            \"verbose\"\n          ],\n          \"default\": \"off\",\n          \"description\": \"Traces the communication between VS Code and the language server.\"\n        },\n        \"haskell.trace.client\": {\n          \"scope\": \"resource\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"off\",\n            \"error\",\n            \"info\",\n            \"debug\"\n          ],\n          \"default\": \"info\",\n          \"description\": \"Sets the log level in the client side.\"\n        },\n        \"haskell.logFile\": {\n          \"scope\": \"resource\",\n          \"type\": \"string\",\n          \"default\": \"\",\n          \"description\": \"If set, redirects the logs to a file.\"\n        },\n        \"haskell.releasesURL\": {\n          \"scope\": \"resource\",\n          \"type\": \"string\",\n          \"default\": \"\",\n          \"description\": \"An optional URL to override where ghcup checks for HLS-GHC compatibility list (usually at: https://raw.githubusercontent.com/haskell/ghcup-metadata/master/hls-metadata-0.0.1.json)\"\n        },\n        \"haskell.metadataURL\": {\n          \"scope\": \"resource\",\n          \"type\": \"string\",\n          \"default\": \"\",\n          \"description\": \"An optional URL to override where ghcup checks for tool download info (usually at: https://raw.githubusercontent.com/haskell/ghcup-metadata/master/ghcup-0.0.7.yaml)\"\n        },\n        \"haskell.releasesDownloadStoragePath\": {\n          \"scope\": \"resource\",\n          \"type\": \"string\",\n          \"default\": \"\",\n          \"markdownDescription\": \"An optional path where downloaded metadata will be stored. Check the default value [here](https://github.com/haskell/vscode-haskell#downloaded-binaries)\"\n        },\n        \"haskell.serverExecutablePath\": {\n          \"scope\": \"machine-overridable\",\n          \"type\": \"string\",\n          \"default\": \"\",\n          \"markdownDescription\": \"Manually set a language server executable. Can be something on the $PATH or the full path to the executable itself. Works with `~,` `${HOME}` and `${workspaceFolder}`.\"\n        },\n        \"haskell.serverExtraArgs\": {\n          \"scope\": \"resource\",\n          \"type\": \"string\",\n          \"default\": \"\",\n          \"markdownDescription\": \"Pass additional arguments to the language server.\"\n        },\n        \"haskell.ghcupExecutablePath\": {\n          \"scope\": \"resource\",\n          \"type\": \"string\",\n          \"default\": \"\",\n          \"markdownDescription\": \"Manually set a ghcup executable path.\"\n        },\n        \"haskell.serverEnvironment\": {\n          \"scope\": \"resource\",\n          \"type\": \"object\",\n          \"default\": {},\n          \"markdownDescription\": \"Define environment variables for the language server.\"\n        },\n        \"haskell.promptBeforeDownloads\": {\n          \"scope\": \"machine\",\n          \"type\": \"boolean\",\n          \"default\": \"true\",\n          \"markdownDescription\": \"Prompt before performing any downloads.\"\n        },\n        \"haskell.manageHLS\": {\n          \"scope\": \"resource\",\n          \"type\": \"string\",\n          \"default\": \"PATH\",\n          \"description\": \"How to manage/find HLS installations.\",\n          \"enum\": [\n            \"GHCup\",\n            \"PATH\"\n          ],\n          \"enumDescriptions\": [\n            \"Will use ghcup and manage Haskell toolchain in the default location (usually '~/.ghcup')\",\n            \"Discovers HLS and other executables in system PATH\"\n          ]\n        },\n        \"haskell.toolchain\": {\n          \"scope\": \"resource\",\n          \"type\": \"object\",\n          \"default\": {},\n          \"description\": \"When manageHLS is set to GHCup, this can overwrite the automatic toolchain configuration with a more specific one. When a tool is omitted, the extension will manage the version (for 'ghc' we try to figure out the version the project requires). The format is '{\\\"tool\\\": \\\"version\\\", ...}'. 'version' accepts all identifiers that 'ghcup' accepts.\"\n        },\n        \"haskell.upgradeGHCup\": {\n          \"scope\": \"resource\",\n          \"type\": \"boolean\",\n          \"default\": true,\n          \"description\": \"Whether to upgrade GHCup automatically when 'manageHLS' is set to 'GHCup'.\"\n        },\n        \"haskell.checkProject\": {\n          \"scope\": \"resource\",\n          \"type\": \"boolean\",\n          \"default\": true,\n          \"description\": \"Whether to typecheck the entire project on load. It could drive to bad performance in large projects.\"\n        },\n        \"haskell.sessionLoading\": {\n          \"scope\": \"resource\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"singleComponent\",\n            \"multipleComponents\"\n          ],\n          \"default\": \"singleComponent\",\n          \"description\": \"Preferred approach for loading package components. Setting this to 'multiple components' (EXPERIMENTAL) allows the build tool (such as `cabal` or `stack`) to [load multiple components at once](https://github.com/haskell/cabal/pull/8726), which is a significant improvement.\",\n          \"enumDescriptions\": [\n            \"Always load only a single component at a time. This is the most reliable option if you encountered any issues with the other options.\",\n            \"Prefer a multiple component session, if the build tool supports it. At the moment, only `cabal` supports multiple components session loading. If the `cabal` version does not support loading multiple components at once, we gracefully fall back to \\\"singleComponent\\\" mode.\"\n          ]\n        },\n        \"haskell.supportCabalFiles\": {\n          \"scope\": \"resource\",\n          \"default\": \"automatic\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"enable\",\n            \"disable\",\n            \"automatic\"\n          ],\n          \"description\": \"Enable Language Server support for `.cabal` files. Requires Haskell Language Server version >= 1.9.0.0.\",\n          \"enumDescriptions\": [\n            \"Enable Language Server support for `.cabal` files\",\n            \"Disable Language Server support for `.cabal` files\",\n            \"Enable Language Server support for `.cabal` files if the HLS version supports it.\"\n          ]\n        },\n        \"haskell.maxCompletions\": {\n          \"scope\": \"resource\",\n          \"default\": 40,\n          \"type\": \"integer\",\n          \"description\": \"Maximum number of completions sent to the editor.\"\n        },\n        \"haskell.plugin.alternateNumberFormat.globalOn\": {\n          \"default\": true,\n          \"description\": \"Enables alternateNumberFormat plugin\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.cabal-fmt.config.path\": {\n          \"default\": \"cabal-fmt\",\n          \"markdownDescription\": \"Set path to 'cabal-fmt' executable\",\n          \"scope\": \"resource\",\n          \"type\": \"string\"\n        },\n        \"haskell.plugin.cabal-gild.config.path\": {\n          \"default\": \"cabal-gild\",\n          \"markdownDescription\": \"Set path to 'cabal-gild' executable\",\n          \"scope\": \"resource\",\n          \"type\": \"string\"\n        },\n        \"haskell.plugin.cabal.codeActionsOn\": {\n          \"default\": true,\n          \"description\": \"Enables cabal code actions\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.cabal.completionOn\": {\n          \"default\": true,\n          \"description\": \"Enables cabal completions\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.cabal.diagnosticsOn\": {\n          \"default\": true,\n          \"description\": \"Enables cabal diagnostics\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.cabal.hoverOn\": {\n          \"default\": true,\n          \"description\": \"Enables cabal hover\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.cabal.symbolsOn\": {\n          \"default\": true,\n          \"description\": \"Enables cabal symbols\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.cabalHaskellIntegration.globalOn\": {\n          \"default\": true,\n          \"description\": \"Enables cabalHaskellIntegration plugin\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.callHierarchy.globalOn\": {\n          \"default\": true,\n          \"description\": \"Enables callHierarchy plugin\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.changeTypeSignature.globalOn\": {\n          \"default\": true,\n          \"description\": \"Enables changeTypeSignature plugin\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.class.codeActionsOn\": {\n          \"default\": true,\n          \"description\": \"Enables class code actions\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.class.codeLensOn\": {\n          \"default\": true,\n          \"description\": \"Enables class code lenses\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.eval.codeActionsOn\": {\n          \"default\": true,\n          \"description\": \"Enables eval code actions\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.eval.codeLensOn\": {\n          \"default\": true,\n          \"description\": \"Enables eval code lenses\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.eval.config.diff\": {\n          \"default\": true,\n          \"markdownDescription\": \"Enable the diff output (WAS/NOW) of eval lenses\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.eval.config.exception\": {\n          \"default\": false,\n          \"markdownDescription\": \"Enable marking exceptions with `*** Exception:` similarly to doctest and GHCi.\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.explicit-fields.codeActionsOn\": {\n          \"default\": true,\n          \"description\": \"Enables explicit-fields code actions\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.explicit-fields.inlayHintsOn\": {\n          \"default\": true,\n          \"description\": \"Enables explicit-fields inlay hints\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.explicit-fixity.globalOn\": {\n          \"default\": true,\n          \"description\": \"Enables explicit-fixity plugin\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.fourmolu.config.external\": {\n          \"default\": false,\n          \"markdownDescription\": \"Call out to an external \\\"fourmolu\\\" executable, rather than using the bundled library.\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.fourmolu.config.path\": {\n          \"default\": \"fourmolu\",\n          \"markdownDescription\": \"Set path to executable (for \\\"external\\\" mode).\",\n          \"scope\": \"resource\",\n          \"type\": \"string\"\n        },\n        \"haskell.plugin.gadt.globalOn\": {\n          \"default\": true,\n          \"description\": \"Enables gadt plugin\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.ghcide-code-actions-bindings.globalOn\": {\n          \"default\": true,\n          \"description\": \"Enables ghcide-code-actions-bindings plugin\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.ghcide-code-actions-fill-holes.globalOn\": {\n          \"default\": true,\n          \"description\": \"Enables ghcide-code-actions-fill-holes plugin\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.ghcide-code-actions-imports-exports.globalOn\": {\n          \"default\": true,\n          \"description\": \"Enables ghcide-code-actions-imports-exports plugin\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.ghcide-code-actions-type-signatures.globalOn\": {\n          \"default\": true,\n          \"description\": \"Enables ghcide-code-actions-type-signatures plugin\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.ghcide-completions.config.autoExtendOn\": {\n          \"default\": true,\n          \"markdownDescription\": \"Extends the import list automatically when completing a out-of-scope identifier\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.ghcide-completions.config.snippetsOn\": {\n          \"default\": true,\n          \"markdownDescription\": \"Inserts snippets when using code completions\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.ghcide-completions.globalOn\": {\n          \"default\": true,\n          \"description\": \"Enables ghcide-completions plugin\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.ghcide-hover-and-symbols.hoverOn\": {\n          \"default\": true,\n          \"description\": \"Enables ghcide-hover-and-symbols hover\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.ghcide-hover-and-symbols.symbolsOn\": {\n          \"default\": true,\n          \"description\": \"Enables ghcide-hover-and-symbols symbols\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.ghcide-type-lenses.config.mode\": {\n          \"default\": \"always\",\n          \"description\": \"Control how type lenses are shown\",\n          \"enum\": [\n            \"always\",\n            \"exported\",\n            \"diagnostics\"\n          ],\n          \"enumDescriptions\": [\n            \"Always displays type lenses of global bindings\",\n            \"Only display type lenses of exported global bindings\",\n            \"Follows error messages produced by GHC about missing signatures\"\n          ],\n          \"scope\": \"resource\",\n          \"type\": \"string\"\n        },\n        \"haskell.plugin.ghcide-type-lenses.globalOn\": {\n          \"default\": true,\n          \"description\": \"Enables ghcide-type-lenses plugin\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.hlint.codeActionsOn\": {\n          \"default\": true,\n          \"description\": \"Enables hlint code actions\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.hlint.config.flags\": {\n          \"default\": [],\n          \"markdownDescription\": \"Flags used by hlint\",\n          \"scope\": \"resource\",\n          \"type\": \"array\"\n        },\n        \"haskell.plugin.hlint.diagnosticsOn\": {\n          \"default\": true,\n          \"description\": \"Enables hlint diagnostics\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.importLens.codeActionsOn\": {\n          \"default\": true,\n          \"description\": \"Enables importLens code actions\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.importLens.codeLensOn\": {\n          \"default\": true,\n          \"description\": \"Enables importLens code lenses\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.importLens.inlayHintsOn\": {\n          \"default\": true,\n          \"description\": \"Enables importLens inlay hints\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.moduleName.globalOn\": {\n          \"default\": true,\n          \"description\": \"Enables moduleName plugin\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.ormolu.config.external\": {\n          \"default\": false,\n          \"markdownDescription\": \"Call out to an external \\\"ormolu\\\" executable, rather than using the bundled library\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.overloaded-record-dot.globalOn\": {\n          \"default\": true,\n          \"description\": \"Enables overloaded-record-dot plugin\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.pragmas-completion.globalOn\": {\n          \"default\": true,\n          \"description\": \"Enables pragmas-completion plugin\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.pragmas-disable.globalOn\": {\n          \"default\": true,\n          \"description\": \"Enables pragmas-disable plugin\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.pragmas-suggest.globalOn\": {\n          \"default\": true,\n          \"description\": \"Enables pragmas-suggest plugin\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.qualifyImportedNames.globalOn\": {\n          \"default\": true,\n          \"description\": \"Enables qualifyImportedNames plugin\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.rename.config.crossModule\": {\n          \"default\": false,\n          \"markdownDescription\": \"Enable experimental cross-module renaming\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.rename.globalOn\": {\n          \"default\": true,\n          \"description\": \"Enables rename plugin\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.retrie.globalOn\": {\n          \"default\": true,\n          \"description\": \"Enables retrie plugin\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.semanticTokens.config.classMethodToken\": {\n          \"default\": \"method\",\n          \"description\": \"LSP semantic token type to use for typeclass methods\",\n          \"enum\": [\n            \"namespace\",\n            \"type\",\n            \"class\",\n            \"enum\",\n            \"interface\",\n            \"struct\",\n            \"typeParameter\",\n            \"parameter\",\n            \"variable\",\n            \"property\",\n            \"enumMember\",\n            \"event\",\n            \"function\",\n            \"method\",\n            \"macro\",\n            \"keyword\",\n            \"modifier\",\n            \"comment\",\n            \"string\",\n            \"number\",\n            \"regexp\",\n            \"operator\",\n            \"decorator\"\n          ],\n          \"enumDescriptions\": [\n            \"LSP Semantic Token Type: namespace\",\n            \"LSP Semantic Token Type: type\",\n            \"LSP Semantic Token Type: class\",\n            \"LSP Semantic Token Type: enum\",\n            \"LSP Semantic Token Type: interface\",\n            \"LSP Semantic Token Type: struct\",\n            \"LSP Semantic Token Type: typeParameter\",\n            \"LSP Semantic Token Type: parameter\",\n            \"LSP Semantic Token Type: variable\",\n            \"LSP Semantic Token Type: property\",\n            \"LSP Semantic Token Type: enumMember\",\n            \"LSP Semantic Token Type: event\",\n            \"LSP Semantic Token Type: function\",\n            \"LSP Semantic Token Type: method\",\n            \"LSP Semantic Token Type: macro\",\n            \"LSP Semantic Token Type: keyword\",\n            \"LSP Semantic Token Type: modifier\",\n            \"LSP Semantic Token Type: comment\",\n            \"LSP Semantic Token Type: string\",\n            \"LSP Semantic Token Type: number\",\n            \"LSP Semantic Token Type: regexp\",\n            \"LSP Semantic Token Type: operator\",\n            \"LSP Semantic Token Type: decorator\"\n          ],\n          \"scope\": \"resource\",\n          \"type\": \"string\"\n        },\n        \"haskell.plugin.semanticTokens.config.classToken\": {\n          \"default\": \"class\",\n          \"description\": \"LSP semantic token type to use for typeclasses\",\n          \"enum\": [\n            \"namespace\",\n            \"type\",\n            \"class\",\n            \"enum\",\n            \"interface\",\n            \"struct\",\n            \"typeParameter\",\n            \"parameter\",\n            \"variable\",\n            \"property\",\n            \"enumMember\",\n            \"event\",\n            \"function\",\n            \"method\",\n            \"macro\",\n            \"keyword\",\n            \"modifier\",\n            \"comment\",\n            \"string\",\n            \"number\",\n            \"regexp\",\n            \"operator\",\n            \"decorator\"\n          ],\n          \"enumDescriptions\": [\n            \"LSP Semantic Token Type: namespace\",\n            \"LSP Semantic Token Type: type\",\n            \"LSP Semantic Token Type: class\",\n            \"LSP Semantic Token Type: enum\",\n            \"LSP Semantic Token Type: interface\",\n            \"LSP Semantic Token Type: struct\",\n            \"LSP Semantic Token Type: typeParameter\",\n            \"LSP Semantic Token Type: parameter\",\n            \"LSP Semantic Token Type: variable\",\n            \"LSP Semantic Token Type: property\",\n            \"LSP Semantic Token Type: enumMember\",\n            \"LSP Semantic Token Type: event\",\n            \"LSP Semantic Token Type: function\",\n            \"LSP Semantic Token Type: method\",\n            \"LSP Semantic Token Type: macro\",\n            \"LSP Semantic Token Type: keyword\",\n            \"LSP Semantic Token Type: modifier\",\n            \"LSP Semantic Token Type: comment\",\n            \"LSP Semantic Token Type: string\",\n            \"LSP Semantic Token Type: number\",\n            \"LSP Semantic Token Type: regexp\",\n            \"LSP Semantic Token Type: operator\",\n            \"LSP Semantic Token Type: decorator\"\n          ],\n          \"scope\": \"resource\",\n          \"type\": \"string\"\n        },\n        \"haskell.plugin.semanticTokens.config.dataConstructorToken\": {\n          \"default\": \"enumMember\",\n          \"description\": \"LSP semantic token type to use for data constructors\",\n          \"enum\": [\n            \"namespace\",\n            \"type\",\n            \"class\",\n            \"enum\",\n            \"interface\",\n            \"struct\",\n            \"typeParameter\",\n            \"parameter\",\n            \"variable\",\n            \"property\",\n            \"enumMember\",\n            \"event\",\n            \"function\",\n            \"method\",\n            \"macro\",\n            \"keyword\",\n            \"modifier\",\n            \"comment\",\n            \"string\",\n            \"number\",\n            \"regexp\",\n            \"operator\",\n            \"decorator\"\n          ],\n          \"enumDescriptions\": [\n            \"LSP Semantic Token Type: namespace\",\n            \"LSP Semantic Token Type: type\",\n            \"LSP Semantic Token Type: class\",\n            \"LSP Semantic Token Type: enum\",\n            \"LSP Semantic Token Type: interface\",\n            \"LSP Semantic Token Type: struct\",\n            \"LSP Semantic Token Type: typeParameter\",\n            \"LSP Semantic Token Type: parameter\",\n            \"LSP Semantic Token Type: variable\",\n            \"LSP Semantic Token Type: property\",\n            \"LSP Semantic Token Type: enumMember\",\n            \"LSP Semantic Token Type: event\",\n            \"LSP Semantic Token Type: function\",\n            \"LSP Semantic Token Type: method\",\n            \"LSP Semantic Token Type: macro\",\n            \"LSP Semantic Token Type: keyword\",\n            \"LSP Semantic Token Type: modifier\",\n            \"LSP Semantic Token Type: comment\",\n            \"LSP Semantic Token Type: string\",\n            \"LSP Semantic Token Type: number\",\n            \"LSP Semantic Token Type: regexp\",\n            \"LSP Semantic Token Type: operator\",\n            \"LSP Semantic Token Type: decorator\"\n          ],\n          \"scope\": \"resource\",\n          \"type\": \"string\"\n        },\n        \"haskell.plugin.semanticTokens.config.functionToken\": {\n          \"default\": \"function\",\n          \"description\": \"LSP semantic token type to use for functions\",\n          \"enum\": [\n            \"namespace\",\n            \"type\",\n            \"class\",\n            \"enum\",\n            \"interface\",\n            \"struct\",\n            \"typeParameter\",\n            \"parameter\",\n            \"variable\",\n            \"property\",\n            \"enumMember\",\n            \"event\",\n            \"function\",\n            \"method\",\n            \"macro\",\n            \"keyword\",\n            \"modifier\",\n            \"comment\",\n            \"string\",\n            \"number\",\n            \"regexp\",\n            \"operator\",\n            \"decorator\"\n          ],\n          \"enumDescriptions\": [\n            \"LSP Semantic Token Type: namespace\",\n            \"LSP Semantic Token Type: type\",\n            \"LSP Semantic Token Type: class\",\n            \"LSP Semantic Token Type: enum\",\n            \"LSP Semantic Token Type: interface\",\n            \"LSP Semantic Token Type: struct\",\n            \"LSP Semantic Token Type: typeParameter\",\n            \"LSP Semantic Token Type: parameter\",\n            \"LSP Semantic Token Type: variable\",\n            \"LSP Semantic Token Type: property\",\n            \"LSP Semantic Token Type: enumMember\",\n            \"LSP Semantic Token Type: event\",\n            \"LSP Semantic Token Type: function\",\n            \"LSP Semantic Token Type: method\",\n            \"LSP Semantic Token Type: macro\",\n            \"LSP Semantic Token Type: keyword\",\n            \"LSP Semantic Token Type: modifier\",\n            \"LSP Semantic Token Type: comment\",\n            \"LSP Semantic Token Type: string\",\n            \"LSP Semantic Token Type: number\",\n            \"LSP Semantic Token Type: regexp\",\n            \"LSP Semantic Token Type: operator\",\n            \"LSP Semantic Token Type: decorator\"\n          ],\n          \"scope\": \"resource\",\n          \"type\": \"string\"\n        },\n        \"haskell.plugin.semanticTokens.config.moduleToken\": {\n          \"default\": \"namespace\",\n          \"description\": \"LSP semantic token type to use for modules\",\n          \"enum\": [\n            \"namespace\",\n            \"type\",\n            \"class\",\n            \"enum\",\n            \"interface\",\n            \"struct\",\n            \"typeParameter\",\n            \"parameter\",\n            \"variable\",\n            \"property\",\n            \"enumMember\",\n            \"event\",\n            \"function\",\n            \"method\",\n            \"macro\",\n            \"keyword\",\n            \"modifier\",\n            \"comment\",\n            \"string\",\n            \"number\",\n            \"regexp\",\n            \"operator\",\n            \"decorator\"\n          ],\n          \"enumDescriptions\": [\n            \"LSP Semantic Token Type: namespace\",\n            \"LSP Semantic Token Type: type\",\n            \"LSP Semantic Token Type: class\",\n            \"LSP Semantic Token Type: enum\",\n            \"LSP Semantic Token Type: interface\",\n            \"LSP Semantic Token Type: struct\",\n            \"LSP Semantic Token Type: typeParameter\",\n            \"LSP Semantic Token Type: parameter\",\n            \"LSP Semantic Token Type: variable\",\n            \"LSP Semantic Token Type: property\",\n            \"LSP Semantic Token Type: enumMember\",\n            \"LSP Semantic Token Type: event\",\n            \"LSP Semantic Token Type: function\",\n            \"LSP Semantic Token Type: method\",\n            \"LSP Semantic Token Type: macro\",\n            \"LSP Semantic Token Type: keyword\",\n            \"LSP Semantic Token Type: modifier\",\n            \"LSP Semantic Token Type: comment\",\n            \"LSP Semantic Token Type: string\",\n            \"LSP Semantic Token Type: number\",\n            \"LSP Semantic Token Type: regexp\",\n            \"LSP Semantic Token Type: operator\",\n            \"LSP Semantic Token Type: decorator\"\n          ],\n          \"scope\": \"resource\",\n          \"type\": \"string\"\n        },\n        \"haskell.plugin.semanticTokens.config.operatorToken\": {\n          \"default\": \"operator\",\n          \"description\": \"LSP semantic token type to use for operators\",\n          \"enum\": [\n            \"namespace\",\n            \"type\",\n            \"class\",\n            \"enum\",\n            \"interface\",\n            \"struct\",\n            \"typeParameter\",\n            \"parameter\",\n            \"variable\",\n            \"property\",\n            \"enumMember\",\n            \"event\",\n            \"function\",\n            \"method\",\n            \"macro\",\n            \"keyword\",\n            \"modifier\",\n            \"comment\",\n            \"string\",\n            \"number\",\n            \"regexp\",\n            \"operator\",\n            \"decorator\"\n          ],\n          \"enumDescriptions\": [\n            \"LSP Semantic Token Type: namespace\",\n            \"LSP Semantic Token Type: type\",\n            \"LSP Semantic Token Type: class\",\n            \"LSP Semantic Token Type: enum\",\n            \"LSP Semantic Token Type: interface\",\n            \"LSP Semantic Token Type: struct\",\n            \"LSP Semantic Token Type: typeParameter\",\n            \"LSP Semantic Token Type: parameter\",\n            \"LSP Semantic Token Type: variable\",\n            \"LSP Semantic Token Type: property\",\n            \"LSP Semantic Token Type: enumMember\",\n            \"LSP Semantic Token Type: event\",\n            \"LSP Semantic Token Type: function\",\n            \"LSP Semantic Token Type: method\",\n            \"LSP Semantic Token Type: macro\",\n            \"LSP Semantic Token Type: keyword\",\n            \"LSP Semantic Token Type: modifier\",\n            \"LSP Semantic Token Type: comment\",\n            \"LSP Semantic Token Type: string\",\n            \"LSP Semantic Token Type: number\",\n            \"LSP Semantic Token Type: regexp\",\n            \"LSP Semantic Token Type: operator\",\n            \"LSP Semantic Token Type: decorator\"\n          ],\n          \"scope\": \"resource\",\n          \"type\": \"string\"\n        },\n        \"haskell.plugin.semanticTokens.config.patternSynonymToken\": {\n          \"default\": \"macro\",\n          \"description\": \"LSP semantic token type to use for pattern synonyms\",\n          \"enum\": [\n            \"namespace\",\n            \"type\",\n            \"class\",\n            \"enum\",\n            \"interface\",\n            \"struct\",\n            \"typeParameter\",\n            \"parameter\",\n            \"variable\",\n            \"property\",\n            \"enumMember\",\n            \"event\",\n            \"function\",\n            \"method\",\n            \"macro\",\n            \"keyword\",\n            \"modifier\",\n            \"comment\",\n            \"string\",\n            \"number\",\n            \"regexp\",\n            \"operator\",\n            \"decorator\"\n          ],\n          \"enumDescriptions\": [\n            \"LSP Semantic Token Type: namespace\",\n            \"LSP Semantic Token Type: type\",\n            \"LSP Semantic Token Type: class\",\n            \"LSP Semantic Token Type: enum\",\n            \"LSP Semantic Token Type: interface\",\n            \"LSP Semantic Token Type: struct\",\n            \"LSP Semantic Token Type: typeParameter\",\n            \"LSP Semantic Token Type: parameter\",\n            \"LSP Semantic Token Type: variable\",\n            \"LSP Semantic Token Type: property\",\n            \"LSP Semantic Token Type: enumMember\",\n            \"LSP Semantic Token Type: event\",\n            \"LSP Semantic Token Type: function\",\n            \"LSP Semantic Token Type: method\",\n            \"LSP Semantic Token Type: macro\",\n            \"LSP Semantic Token Type: keyword\",\n            \"LSP Semantic Token Type: modifier\",\n            \"LSP Semantic Token Type: comment\",\n            \"LSP Semantic Token Type: string\",\n            \"LSP Semantic Token Type: number\",\n            \"LSP Semantic Token Type: regexp\",\n            \"LSP Semantic Token Type: operator\",\n            \"LSP Semantic Token Type: decorator\"\n          ],\n          \"scope\": \"resource\",\n          \"type\": \"string\"\n        },\n        \"haskell.plugin.semanticTokens.config.recordFieldToken\": {\n          \"default\": \"property\",\n          \"description\": \"LSP semantic token type to use for record fields\",\n          \"enum\": [\n            \"namespace\",\n            \"type\",\n            \"class\",\n            \"enum\",\n            \"interface\",\n            \"struct\",\n            \"typeParameter\",\n            \"parameter\",\n            \"variable\",\n            \"property\",\n            \"enumMember\",\n            \"event\",\n            \"function\",\n            \"method\",\n            \"macro\",\n            \"keyword\",\n            \"modifier\",\n            \"comment\",\n            \"string\",\n            \"number\",\n            \"regexp\",\n            \"operator\",\n            \"decorator\"\n          ],\n          \"enumDescriptions\": [\n            \"LSP Semantic Token Type: namespace\",\n            \"LSP Semantic Token Type: type\",\n            \"LSP Semantic Token Type: class\",\n            \"LSP Semantic Token Type: enum\",\n            \"LSP Semantic Token Type: interface\",\n            \"LSP Semantic Token Type: struct\",\n            \"LSP Semantic Token Type: typeParameter\",\n            \"LSP Semantic Token Type: parameter\",\n            \"LSP Semantic Token Type: variable\",\n            \"LSP Semantic Token Type: property\",\n            \"LSP Semantic Token Type: enumMember\",\n            \"LSP Semantic Token Type: event\",\n            \"LSP Semantic Token Type: function\",\n            \"LSP Semantic Token Type: method\",\n            \"LSP Semantic Token Type: macro\",\n            \"LSP Semantic Token Type: keyword\",\n            \"LSP Semantic Token Type: modifier\",\n            \"LSP Semantic Token Type: comment\",\n            \"LSP Semantic Token Type: string\",\n            \"LSP Semantic Token Type: number\",\n            \"LSP Semantic Token Type: regexp\",\n            \"LSP Semantic Token Type: operator\",\n            \"LSP Semantic Token Type: decorator\"\n          ],\n          \"scope\": \"resource\",\n          \"type\": \"string\"\n        },\n        \"haskell.plugin.semanticTokens.config.typeConstructorToken\": {\n          \"default\": \"enum\",\n          \"description\": \"LSP semantic token type to use for type constructors\",\n          \"enum\": [\n            \"namespace\",\n            \"type\",\n            \"class\",\n            \"enum\",\n            \"interface\",\n            \"struct\",\n            \"typeParameter\",\n            \"parameter\",\n            \"variable\",\n            \"property\",\n            \"enumMember\",\n            \"event\",\n            \"function\",\n            \"method\",\n            \"macro\",\n            \"keyword\",\n            \"modifier\",\n            \"comment\",\n            \"string\",\n            \"number\",\n            \"regexp\",\n            \"operator\",\n            \"decorator\"\n          ],\n          \"enumDescriptions\": [\n            \"LSP Semantic Token Type: namespace\",\n            \"LSP Semantic Token Type: type\",\n            \"LSP Semantic Token Type: class\",\n            \"LSP Semantic Token Type: enum\",\n            \"LSP Semantic Token Type: interface\",\n            \"LSP Semantic Token Type: struct\",\n            \"LSP Semantic Token Type: typeParameter\",\n            \"LSP Semantic Token Type: parameter\",\n            \"LSP Semantic Token Type: variable\",\n            \"LSP Semantic Token Type: property\",\n            \"LSP Semantic Token Type: enumMember\",\n            \"LSP Semantic Token Type: event\",\n            \"LSP Semantic Token Type: function\",\n            \"LSP Semantic Token Type: method\",\n            \"LSP Semantic Token Type: macro\",\n            \"LSP Semantic Token Type: keyword\",\n            \"LSP Semantic Token Type: modifier\",\n            \"LSP Semantic Token Type: comment\",\n            \"LSP Semantic Token Type: string\",\n            \"LSP Semantic Token Type: number\",\n            \"LSP Semantic Token Type: regexp\",\n            \"LSP Semantic Token Type: operator\",\n            \"LSP Semantic Token Type: decorator\"\n          ],\n          \"scope\": \"resource\",\n          \"type\": \"string\"\n        },\n        \"haskell.plugin.semanticTokens.config.typeFamilyToken\": {\n          \"default\": \"interface\",\n          \"description\": \"LSP semantic token type to use for type families\",\n          \"enum\": [\n            \"namespace\",\n            \"type\",\n            \"class\",\n            \"enum\",\n            \"interface\",\n            \"struct\",\n            \"typeParameter\",\n            \"parameter\",\n            \"variable\",\n            \"property\",\n            \"enumMember\",\n            \"event\",\n            \"function\",\n            \"method\",\n            \"macro\",\n            \"keyword\",\n            \"modifier\",\n            \"comment\",\n            \"string\",\n            \"number\",\n            \"regexp\",\n            \"operator\",\n            \"decorator\"\n          ],\n          \"enumDescriptions\": [\n            \"LSP Semantic Token Type: namespace\",\n            \"LSP Semantic Token Type: type\",\n            \"LSP Semantic Token Type: class\",\n            \"LSP Semantic Token Type: enum\",\n            \"LSP Semantic Token Type: interface\",\n            \"LSP Semantic Token Type: struct\",\n            \"LSP Semantic Token Type: typeParameter\",\n            \"LSP Semantic Token Type: parameter\",\n            \"LSP Semantic Token Type: variable\",\n            \"LSP Semantic Token Type: property\",\n            \"LSP Semantic Token Type: enumMember\",\n            \"LSP Semantic Token Type: event\",\n            \"LSP Semantic Token Type: function\",\n            \"LSP Semantic Token Type: method\",\n            \"LSP Semantic Token Type: macro\",\n            \"LSP Semantic Token Type: keyword\",\n            \"LSP Semantic Token Type: modifier\",\n            \"LSP Semantic Token Type: comment\",\n            \"LSP Semantic Token Type: string\",\n            \"LSP Semantic Token Type: number\",\n            \"LSP Semantic Token Type: regexp\",\n            \"LSP Semantic Token Type: operator\",\n            \"LSP Semantic Token Type: decorator\"\n          ],\n          \"scope\": \"resource\",\n          \"type\": \"string\"\n        },\n        \"haskell.plugin.semanticTokens.config.typeSynonymToken\": {\n          \"default\": \"type\",\n          \"description\": \"LSP semantic token type to use for type synonyms\",\n          \"enum\": [\n            \"namespace\",\n            \"type\",\n            \"class\",\n            \"enum\",\n            \"interface\",\n            \"struct\",\n            \"typeParameter\",\n            \"parameter\",\n            \"variable\",\n            \"property\",\n            \"enumMember\",\n            \"event\",\n            \"function\",\n            \"method\",\n            \"macro\",\n            \"keyword\",\n            \"modifier\",\n            \"comment\",\n            \"string\",\n            \"number\",\n            \"regexp\",\n            \"operator\",\n            \"decorator\"\n          ],\n          \"enumDescriptions\": [\n            \"LSP Semantic Token Type: namespace\",\n            \"LSP Semantic Token Type: type\",\n            \"LSP Semantic Token Type: class\",\n            \"LSP Semantic Token Type: enum\",\n            \"LSP Semantic Token Type: interface\",\n            \"LSP Semantic Token Type: struct\",\n            \"LSP Semantic Token Type: typeParameter\",\n            \"LSP Semantic Token Type: parameter\",\n            \"LSP Semantic Token Type: variable\",\n            \"LSP Semantic Token Type: property\",\n            \"LSP Semantic Token Type: enumMember\",\n            \"LSP Semantic Token Type: event\",\n            \"LSP Semantic Token Type: function\",\n            \"LSP Semantic Token Type: method\",\n            \"LSP Semantic Token Type: macro\",\n            \"LSP Semantic Token Type: keyword\",\n            \"LSP Semantic Token Type: modifier\",\n            \"LSP Semantic Token Type: comment\",\n            \"LSP Semantic Token Type: string\",\n            \"LSP Semantic Token Type: number\",\n            \"LSP Semantic Token Type: regexp\",\n            \"LSP Semantic Token Type: operator\",\n            \"LSP Semantic Token Type: decorator\"\n          ],\n          \"scope\": \"resource\",\n          \"type\": \"string\"\n        },\n        \"haskell.plugin.semanticTokens.config.typeVariableToken\": {\n          \"default\": \"typeParameter\",\n          \"description\": \"LSP semantic token type to use for type variables\",\n          \"enum\": [\n            \"namespace\",\n            \"type\",\n            \"class\",\n            \"enum\",\n            \"interface\",\n            \"struct\",\n            \"typeParameter\",\n            \"parameter\",\n            \"variable\",\n            \"property\",\n            \"enumMember\",\n            \"event\",\n            \"function\",\n            \"method\",\n            \"macro\",\n            \"keyword\",\n            \"modifier\",\n            \"comment\",\n            \"string\",\n            \"number\",\n            \"regexp\",\n            \"operator\",\n            \"decorator\"\n          ],\n          \"enumDescriptions\": [\n            \"LSP Semantic Token Type: namespace\",\n            \"LSP Semantic Token Type: type\",\n            \"LSP Semantic Token Type: class\",\n            \"LSP Semantic Token Type: enum\",\n            \"LSP Semantic Token Type: interface\",\n            \"LSP Semantic Token Type: struct\",\n            \"LSP Semantic Token Type: typeParameter\",\n            \"LSP Semantic Token Type: parameter\",\n            \"LSP Semantic Token Type: variable\",\n            \"LSP Semantic Token Type: property\",\n            \"LSP Semantic Token Type: enumMember\",\n            \"LSP Semantic Token Type: event\",\n            \"LSP Semantic Token Type: function\",\n            \"LSP Semantic Token Type: method\",\n            \"LSP Semantic Token Type: macro\",\n            \"LSP Semantic Token Type: keyword\",\n            \"LSP Semantic Token Type: modifier\",\n            \"LSP Semantic Token Type: comment\",\n            \"LSP Semantic Token Type: string\",\n            \"LSP Semantic Token Type: number\",\n            \"LSP Semantic Token Type: regexp\",\n            \"LSP Semantic Token Type: operator\",\n            \"LSP Semantic Token Type: decorator\"\n          ],\n          \"scope\": \"resource\",\n          \"type\": \"string\"\n        },\n        \"haskell.plugin.semanticTokens.config.variableToken\": {\n          \"default\": \"variable\",\n          \"description\": \"LSP semantic token type to use for variables\",\n          \"enum\": [\n            \"namespace\",\n            \"type\",\n            \"class\",\n            \"enum\",\n            \"interface\",\n            \"struct\",\n            \"typeParameter\",\n            \"parameter\",\n            \"variable\",\n            \"property\",\n            \"enumMember\",\n            \"event\",\n            \"function\",\n            \"method\",\n            \"macro\",\n            \"keyword\",\n            \"modifier\",\n            \"comment\",\n            \"string\",\n            \"number\",\n            \"regexp\",\n            \"operator\",\n            \"decorator\"\n          ],\n          \"enumDescriptions\": [\n            \"LSP Semantic Token Type: namespace\",\n            \"LSP Semantic Token Type: type\",\n            \"LSP Semantic Token Type: class\",\n            \"LSP Semantic Token Type: enum\",\n            \"LSP Semantic Token Type: interface\",\n            \"LSP Semantic Token Type: struct\",\n            \"LSP Semantic Token Type: typeParameter\",\n            \"LSP Semantic Token Type: parameter\",\n            \"LSP Semantic Token Type: variable\",\n            \"LSP Semantic Token Type: property\",\n            \"LSP Semantic Token Type: enumMember\",\n            \"LSP Semantic Token Type: event\",\n            \"LSP Semantic Token Type: function\",\n            \"LSP Semantic Token Type: method\",\n            \"LSP Semantic Token Type: macro\",\n            \"LSP Semantic Token Type: keyword\",\n            \"LSP Semantic Token Type: modifier\",\n            \"LSP Semantic Token Type: comment\",\n            \"LSP Semantic Token Type: string\",\n            \"LSP Semantic Token Type: number\",\n            \"LSP Semantic Token Type: regexp\",\n            \"LSP Semantic Token Type: operator\",\n            \"LSP Semantic Token Type: decorator\"\n          ],\n          \"scope\": \"resource\",\n          \"type\": \"string\"\n        },\n        \"haskell.plugin.semanticTokens.globalOn\": {\n          \"default\": false,\n          \"description\": \"Enables semanticTokens plugin\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.signatureHelp.globalOn\": {\n          \"default\": true,\n          \"description\": \"Enables signatureHelp plugin\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.splice.globalOn\": {\n          \"default\": true,\n          \"description\": \"Enables splice plugin\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        },\n        \"haskell.plugin.stan.globalOn\": {\n          \"default\": false,\n          \"description\": \"Enables stan plugin\",\n          \"scope\": \"resource\",\n          \"type\": \"boolean\"\n        }\n      }\n    },\n    \"commands\": [\n      {\n        \"command\": \"haskell.commands.restartExtension\",\n        \"title\": \"Haskell: Restart vscode-haskell extension\",\n        \"description\": \"Restart the vscode-haskell extension. Reloads configuration.\"\n      },\n      {\n        \"command\": \"haskell.commands.restartServer\",\n        \"title\": \"Haskell: Restart Haskell LSP server\",\n        \"description\": \"Restart the Haskell LSP server\"\n      },\n      {\n        \"command\": \"haskell.commands.startServer\",\n        \"title\": \"Haskell: Start Haskell LSP server\",\n        \"description\": \"Start the Haskell LSP server\"\n      },\n      {\n        \"command\": \"haskell.commands.stopServer\",\n        \"title\": \"Haskell: Stop Haskell LSP server\",\n        \"description\": \"Stop the Haskell LSP server\"\n      }\n    ]\n  },\n  \"scripts\": {\n    \"vscode:prepublish\": \"webpack --mode production\",\n    \"package\": \"npx vsce package --out=dist/\",\n    \"build\": \"npm install\",\n    \"webpack\": \"webpack --mode none\",\n    \"watch\": \"webpack --mode development --watch\",\n    \"lint\": \"eslint -c eslint.config.mjs src\",\n    \"lint-fix\": \"eslint --fix -c eslint.config.mjs src\",\n    \"push-tag\": \"git tag -a $npm_package_version -m \\\"Version $npm_package_version\\\" && git push origin $npm_package_version\",\n    \"pretest\": \"tsc --alwaysStrict -p ./\",\n    \"format\": \"prettier . --write\",\n    \"test\": \"vscode-test\"\n  },\n  \"husky\": {\n    \"hooks\": {\n      \"pre-commit\": \"pretty-quick --staged\"\n    }\n  },\n  \"devDependencies\": {\n    \"@eslint/js\": \"^9.37.0\",\n    \"@types/mocha\": \"^10.0.10\",\n    \"@types/node\": \"^22.15.29\",\n    \"@types/vscode\": \"^1.102.0\",\n    \"@types/which\": \"^3.0.4\",\n    \"@typescript-eslint/eslint-plugin\": \"^8.56.0\",\n    \"@typescript-eslint/parser\": \"^8.56.1\",\n    \"@vscode/test-cli\": \"^0.0.12\",\n    \"@vscode/test-electron\": \"^2.5.2\",\n    \"corepack\": \"^0.34.6\",\n    \"eslint\": \"^9.37.0\",\n    \"eslint-webpack-plugin\": \"^5.0.3\",\n    \"glob\": \"^13.0.5\",\n    \"globals\": \"^17.4.0\",\n    \"husky\": \"^9.1.7\",\n    \"mocha\": \"^11.7.5\",\n    \"prettier\": \"^3.8.1\",\n    \"ts-loader\": \"^9.5.2\",\n    \"typescript\": \"^5.9.3\",\n    \"typescript-eslint\": \"^8.57.0\",\n    \"webpack\": \"^5.105.4\",\n    \"webpack-cli\": \"^6.0.1\"\n  },\n  \"extensionDependencies\": [\n    \"justusadam.language-haskell\"\n  ],\n  \"dependencies\": {\n    \"ts-pattern\": \"^5.9.0\",\n    \"vscode-languageclient\": \"^9.0.1\",\n    \"which\": \"^6.0.1\"\n  },\n  \"packageManager\": \"npm@11.9.0\"\n}\n"
  },
  {
    "path": "src/commands/constants.ts",
    "content": "export const RestartExtensionCommandName = 'haskell.commands.restartExtension';\nexport const RestartServerCommandName = 'haskell.commands.restartServer';\nexport const StartServerCommandName = 'haskell.commands.startServer';\nexport const StopServerCommandName = 'haskell.commands.stopServer';\nexport const OpenLogsCommandName = 'haskell.commands.openLogs';\nexport const ShowExtensionVersions = 'haskell.commands.showVersions';\n"
  },
  {
    "path": "src/config.ts",
    "content": "import { OutputChannel, Uri, window, WorkspaceConfiguration, WorkspaceFolder } from 'vscode';\nimport { expandHomeDir, IEnvVars } from './utils';\nimport * as path from 'path';\nimport { Logger } from 'vscode-languageclient';\nimport { ExtensionLogger } from './logger';\nimport { GHCupConfig } from './ghcup';\n\nexport type LogLevel = 'off' | 'messages' | 'verbose';\nexport type ClientLogLevel = 'off' | 'error' | 'info' | 'debug';\n\nexport type Config = {\n  /**\n   * Unique name per workspace folder (useful for multi-root workspaces).\n   */\n  langName: string;\n  logLevel: LogLevel;\n  clientLogLevel: ClientLogLevel;\n  logFilePath?: string;\n  workingDir: string;\n  outputChannel: OutputChannel;\n  serverArgs: string[];\n  serverEnvironment: IEnvVars;\n  ghcupConfig: GHCupConfig;\n};\n\nexport function initConfig(workspaceConfig: WorkspaceConfiguration, uri: Uri, folder?: WorkspaceFolder): Config {\n  // Set a unique name per workspace folder (useful for multi-root workspaces).\n  const langName = 'Haskell' + (folder ? ` (${folder.name})` : '');\n  const currentWorkingDir = folder ? folder.uri.fsPath : path.dirname(uri.fsPath);\n\n  const logLevel = getLogLevel(workspaceConfig);\n  const clientLogLevel = getClientLogLevel(workspaceConfig);\n\n  const logFile = getLogFile(workspaceConfig);\n  const logFilePath = resolveLogFilePath(logFile, currentWorkingDir);\n\n  const outputChannel: OutputChannel = window.createOutputChannel(langName);\n  const serverArgs = getServerArgs(workspaceConfig, logLevel, logFilePath);\n\n  return {\n    langName: langName,\n    logLevel: logLevel,\n    clientLogLevel: clientLogLevel,\n    logFilePath: logFilePath,\n    workingDir: currentWorkingDir,\n    outputChannel: outputChannel,\n    serverArgs: serverArgs,\n    serverEnvironment: workspaceConfig.serverEnvironment,\n    ghcupConfig: {\n      metadataUrl: workspaceConfig.metadataURL as string,\n      upgradeGHCup: workspaceConfig.get('upgradeGHCup') as boolean,\n      executablePath: workspaceConfig.get('ghcupExecutablePath') as string,\n    },\n  };\n}\n\nexport function initLoggerFromConfig(config: Config): ExtensionLogger {\n  return new ExtensionLogger('client', config.clientLogLevel, config.outputChannel, config.logFilePath);\n}\n\nexport function logConfig(logger: Logger, config: Config) {\n  if (config.logFilePath) {\n    logger.info(`Writing client log to file ${config.logFilePath}`);\n  }\n  logger.log('Environment variables:');\n  Object.entries(process.env).forEach(([key, value]: [string, string | undefined]) => {\n    // only list environment variables that we actually care about.\n    // this makes it safe for users to just paste the logs to whoever,\n    // and avoids leaking secrets.\n    if (['PATH'].includes(key)) {\n      logger.log(`  ${key}: ${value}`);\n    }\n  });\n}\n\nfunction getLogFile(workspaceConfig: WorkspaceConfiguration) {\n  const logFile_: unknown = workspaceConfig.logFile;\n  let logFile: string | undefined;\n  if (typeof logFile_ === 'string') {\n    logFile = logFile_ !== '' ? logFile_ : undefined;\n  }\n  return logFile;\n}\n\nfunction getClientLogLevel(workspaceConfig: WorkspaceConfiguration): ClientLogLevel {\n  const clientLogLevel_: unknown = workspaceConfig.trace.client;\n  let clientLogLevel;\n  if (typeof clientLogLevel_ === 'string') {\n    switch (clientLogLevel_) {\n      case 'off':\n      case 'error':\n      case 'info':\n      case 'debug':\n        clientLogLevel = clientLogLevel_;\n        break;\n      default:\n        throw new Error(\"Option \\\"haskell.trace.client\\\" is expected to be one of 'off', 'error', 'info', 'debug'.\");\n    }\n  } else {\n    throw new Error('Option \"haskell.trace.client\" is expected to be a string');\n  }\n  return clientLogLevel;\n}\n\nfunction getLogLevel(workspaceConfig: WorkspaceConfiguration): LogLevel {\n  const logLevel_: unknown = workspaceConfig.trace.server;\n  let logLevel;\n  if (typeof logLevel_ === 'string') {\n    switch (logLevel_) {\n      case 'off':\n      case 'messages':\n      case 'verbose':\n        logLevel = logLevel_;\n        break;\n      default:\n        throw new Error(\"Option \\\"haskell.trace.server\\\" is expected to be one of 'off', 'messages', 'verbose'.\");\n    }\n  } else {\n    throw new Error('Option \"haskell.trace.server\" is expected to be a string');\n  }\n  return logLevel;\n}\n\nfunction resolveLogFilePath(logFile: string | undefined, currentWorkingDir: string): string | undefined {\n  return logFile !== undefined ? path.resolve(currentWorkingDir, expandHomeDir(logFile)) : undefined;\n}\n\nfunction getServerArgs(workspaceConfig: WorkspaceConfiguration, logLevel: LogLevel, logFilePath?: string): string[] {\n  const serverArgs = ['--lsp']\n    .concat(logLevel === 'messages' ? ['-d'] : [])\n    .concat(logFilePath !== undefined ? ['-l', logFilePath] : []);\n\n  const rawExtraArgs: unknown = workspaceConfig.serverExtraArgs;\n  if (typeof rawExtraArgs === 'string' && rawExtraArgs !== '') {\n    const e = rawExtraArgs.split(' ');\n    serverArgs.push(...e);\n  }\n\n  // We don't want empty strings in our args\n  return serverArgs.map((x) => x.trim()).filter((x) => x !== '');\n}\n"
  },
  {
    "path": "src/docsBrowser.ts",
    "content": "import { dirname } from 'path';\nimport {\n  CancellationToken,\n  commands,\n  CompletionContext,\n  CompletionItem,\n  CompletionList,\n  Disposable,\n  env,\n  Hover,\n  MarkdownString,\n  MarkedString,\n  Position,\n  ProviderResult,\n  TextDocument,\n  Uri,\n  ViewColumn,\n  window,\n  workspace,\n} from 'vscode';\nimport { ProvideCompletionItemsSignature, ProvideHoverSignature } from 'vscode-languageclient';\n\nasync function showDocumentation({\n  title,\n  localPath,\n  hackageUri,\n}: {\n  title: string;\n  localPath: string;\n  hackageUri: string;\n}) {\n  const arr = localPath.match(/([^/]+)\\.[^.]+$/);\n  const ttl = arr !== null && arr.length === 2 ? arr[1].replace(/-/gi, '.') : title;\n  const documentationDirectory = dirname(localPath);\n  let panel;\n  try {\n    const docUri = Uri.parse(documentationDirectory);\n\n    // Make sure to use Uri.parse here, as path will already have 'file:///' in it\n    panel = window.createWebviewPanel('haskell.showDocumentationPanel', ttl, ViewColumn.Beside, {\n      localResourceRoots: [docUri],\n      enableFindWidget: true,\n      enableCommandUris: true,\n      enableScripts: true,\n    });\n\n    const encoded = encodeURIComponent(JSON.stringify({ hackageUri, inWebView: true }));\n    const hackageCmd = 'command:haskell.openDocumentationOnHackage?' + encoded;\n\n    const bytes = await workspace.fs.readFile(Uri.parse(localPath));\n\n    const addBase = `\n          <base href=\"${panel.webview.asWebviewUri(Uri.parse(documentationDirectory)).toString()}/\">\n          `;\n\n    panel.webview.html = `\n          <html>\n          ${addBase}\n          <body>\n          <div><a href=\"${hackageCmd}\">Open on Hackage</a></div>\n          ${bytes.toString()}\n          </body>\n          </html>\n          `;\n  } catch (e) {\n    if (e instanceof Error) {\n      await window.showErrorMessage(e.message);\n    }\n  }\n  return panel;\n}\n\n// registers the browser in VSCode infrastructure\nexport function registerDocsBrowser(): Disposable {\n  return commands.registerCommand('haskell.showDocumentation', showDocumentation);\n}\n\nasync function openDocumentationOnHackage({\n  hackageUri,\n  inWebView = false,\n}: {\n  hackageUri: string;\n  inWebView: boolean;\n}) {\n  try {\n    // open on Hackage and close the original webview in VS code\n    await env.openExternal(Uri.parse(hackageUri));\n    if (inWebView) {\n      await commands.executeCommand('workbench.action.closeActiveEditor');\n    }\n  } catch (e) {\n    if (e instanceof Error) {\n      await window.showErrorMessage(e.message);\n    }\n  }\n}\n\nexport function registerDocsOpenOnHackage(): Disposable {\n  return commands.registerCommand('haskell.openDocumentationOnHackage', openDocumentationOnHackage);\n}\n\nexport function hoverLinksMiddlewareHook(\n  document: TextDocument,\n  position: Position,\n  token: CancellationToken,\n  next: ProvideHoverSignature,\n): ProviderResult<Hover> {\n  const res = next(document, position, token);\n  return Promise.resolve(res).then((r) => {\n    if (r !== null && r !== undefined) {\n      r.contents = r.contents.map(processLink);\n    }\n    return r;\n  });\n}\n\nexport function completionLinksMiddlewareHook(\n  document: TextDocument,\n  position: Position,\n  context: CompletionContext,\n  token: CancellationToken,\n  next: ProvideCompletionItemsSignature,\n): ProviderResult<CompletionItem[] | CompletionList> {\n  const res = next(document, position, context, token);\n\n  function processCI(ci: CompletionItem): void {\n    if (ci.documentation) {\n      ci.documentation = processLink(ci.documentation);\n    }\n  }\n\n  return Promise.resolve(res).then((r) => {\n    if (r instanceof Array) {\n      r.forEach(processCI);\n    } else if (r) {\n      r.items.forEach(processCI);\n    }\n    return r;\n  });\n}\n\nfunction processLink(ms: MarkdownString | MarkedString): string | MarkdownString {\n  const openDocsInHackage = workspace.getConfiguration('haskell').get('openDocumentationInHackage');\n  const openSourceInHackage = workspace.getConfiguration('haskell').get('openSourceInHackage');\n  function transform(s: string): string {\n    return s.replace(\n      /\\[(.+)\\]\\((file:.+\\/doc\\/(?:.*html\\/libraries\\/)?([^/]+)\\/(?:.*\\/)?(.+\\.html#?.*))\\)/gi,\n      (_all, title, localPath, packageName, fileAndAnchor) => {\n        let hackageUri: string;\n        if (title === 'Documentation') {\n          hackageUri = `https://hackage.haskell.org/package/${packageName}/docs/${fileAndAnchor}`;\n          const encoded = encodeURIComponent(JSON.stringify({ title, localPath, hackageUri }));\n          let cmd: string;\n          if (openDocsInHackage) {\n            cmd = 'command:haskell.openDocumentationOnHackage?' + encoded;\n          } else {\n            cmd = 'command:haskell.showDocumentation?' + encoded;\n          }\n          return `[${title}](${cmd})`;\n        } else if (title === 'Source' && typeof fileAndAnchor === 'string') {\n          const moduleLocation = fileAndAnchor.replace(/-/gi, '.');\n          hackageUri = `https://hackage.haskell.org/package/${packageName}/docs/src/${moduleLocation}`;\n          const encoded = encodeURIComponent(JSON.stringify({ title, localPath, hackageUri }));\n          let cmd: string;\n          if (openSourceInHackage) {\n            cmd = 'command:haskell.openDocumentationOnHackage?' + encoded;\n          } else {\n            cmd = 'command:haskell.showDocumentation?' + encoded;\n          }\n          return `[${title}](${cmd})`;\n        } else {\n          return s;\n        }\n      },\n    );\n  }\n  if (typeof ms === 'string') {\n    return transform(ms);\n  } else if (ms instanceof MarkdownString) {\n    const mstr = new MarkdownString(transform(ms.value));\n    mstr.isTrusted = true;\n    return mstr;\n  } else {\n    return ms.value;\n  }\n}\n"
  },
  {
    "path": "src/errors.ts",
    "content": "import { Uri } from 'vscode';\n\nexport class HlsError extends Error {}\n\nexport class MissingToolError extends HlsError {\n  public readonly tool: string;\n  constructor(tool: string) {\n    let prettyTool: string;\n    switch (tool.toLowerCase()) {\n      case 'stack':\n        prettyTool = 'Stack';\n        break;\n      case 'cabal':\n        prettyTool = 'Cabal';\n        break;\n      case 'ghc':\n        prettyTool = 'GHC';\n        break;\n      case 'ghcup':\n        prettyTool = 'GHCup';\n        break;\n      case 'haskell-language-server':\n      case 'hls':\n        prettyTool = 'HLS';\n        break;\n      default:\n        prettyTool = tool;\n        break;\n    }\n    super(`Project requires ${prettyTool} but it isn't installed`);\n    this.tool = prettyTool;\n  }\n\n  public installLink(): Uri | null {\n    switch (this.tool) {\n      case 'Stack':\n        return Uri.parse('https://docs.haskellstack.org/en/stable/install_and_upgrade/');\n      case 'GHCup':\n      case 'Cabal':\n      case 'HLS':\n      case 'GHC':\n        return Uri.parse('https://www.haskell.org/ghcup/');\n      default:\n        return null;\n    }\n  }\n}\n\nexport class NoMatchingHls extends Error {\n  constructor(readonly ghcProjVersion: string) {\n    super(`HLS does not support GHC ${ghcProjVersion} yet.`);\n  }\n  public docLink(): Uri {\n    return Uri.parse('https://haskell-language-server.readthedocs.io/en/latest/support/ghc-version-support.html');\n  }\n}\n"
  },
  {
    "path": "src/extension.ts",
    "content": "import { commands, env, ExtensionContext, TextDocument, Uri, window, workspace, WorkspaceFolder } from 'vscode';\nimport {\n  ExecutableOptions,\n  LanguageClient,\n  LanguageClientOptions,\n  Logger,\n  RevealOutputChannelOn,\n  ServerOptions,\n} from 'vscode-languageclient/node';\nimport * as constants from './commands/constants';\nimport * as DocsBrowser from './docsBrowser';\nimport { HlsError, MissingToolError, NoMatchingHls } from './errors';\nimport { findHaskellLanguageServer, HlsExecutable, IEnvVars, fetchConfig } from './hlsBinaries';\nimport { addPathToProcessPath, comparePVP, callAsync } from './utils';\nimport { Config, initConfig, initLoggerFromConfig, logConfig } from './config';\nimport { HaskellStatusBar } from './statusBar';\n\n/**\n * Global information about the running clients.\n */\ntype Client = {\n  client: LanguageClient;\n  config: Config;\n};\n\n// The current map of documents & folders to language servers.\n// It may be null to indicate that we are in the process of launching a server,\n// in which case don't try to launch another one for that uri\nconst clients: Map<string, Client | null> = new Map();\n\n// This is the entrypoint to our extension\nexport async function activate(context: ExtensionContext) {\n  const statusBar = new HaskellStatusBar(context.extension.packageJSON.version as string | undefined);\n  context.subscriptions.push(statusBar);\n\n  // (Possibly) launch the language server every time a document is opened, so\n  // it works across multiple workspace folders. Eventually, haskell-lsp should\n  // just support\n  // https://microsoft.github.io/language-server-protocol/specifications/specification-3-15/#workspace_workspaceFolders\n  // and then we can just launch one server\n  workspace.onDidOpenTextDocument(async (document: TextDocument) => await activateServer(context, document));\n  for (const document of workspace.textDocuments) {\n    await activateServer(context, document);\n  }\n\n  // Stop the server from any workspace folders that are removed.\n  workspace.onDidChangeWorkspaceFolders(async (event) => {\n    for (const folder of event.removed) {\n      const client = clients.get(folder.uri.toString());\n      if (client) {\n        const uri = folder.uri.toString();\n        client.client.info(`Deleting folder for clients: ${uri}`);\n        clients.delete(uri);\n        client.client.info('Stopping the server');\n        await client.client.stop();\n      }\n    }\n  });\n\n  // Register editor commands for HIE, but only register the commands once at activation.\n  const restartCmd = commands.registerCommand(constants.RestartServerCommandName, async () => {\n    for (const langClient of clients.values()) {\n      langClient?.client.info('Stopping the server');\n      await langClient?.client.stop();\n      langClient?.client.info('Starting the server');\n      await langClient?.client.start();\n    }\n  });\n\n  context.subscriptions.push(restartCmd);\n\n  const openLogsCmd = commands.registerCommand(constants.OpenLogsCommandName, () => {\n    for (const langClient of clients.values()) {\n      langClient?.config.outputChannel.show();\n    }\n  });\n\n  context.subscriptions.push(openLogsCmd);\n\n  const restartExtensionCmd = commands.registerCommand(constants.RestartExtensionCommandName, async () => {\n    for (const langClient of clients.values()) {\n      langClient?.client.info('Stopping the server');\n      await langClient?.client.stop();\n    }\n    clients.clear();\n    fetchConfig();\n\n    for (const document of workspace.textDocuments) {\n      await activateServer(context, document);\n    }\n  });\n\n  context.subscriptions.push(restartExtensionCmd);\n\n  const showVersionsCmd = commands.registerCommand(constants.ShowExtensionVersions, () => {\n    void window.showInformationMessage(`Extension Version: ${context.extension.packageJSON.version ?? '<unknown>'}`);\n  });\n\n  context.subscriptions.push(showVersionsCmd);\n\n  const stopCmd = commands.registerCommand(constants.StopServerCommandName, async () => {\n    for (const langClient of clients.values()) {\n      langClient?.client.info('Stopping the server');\n      await langClient?.client.stop();\n      langClient?.client.info('Server stopped');\n    }\n  });\n\n  context.subscriptions.push(stopCmd);\n\n  const startCmd = commands.registerCommand(constants.StartServerCommandName, async () => {\n    for (const langClient of clients.values()) {\n      langClient?.client.info('Starting the server');\n      await langClient?.client.start();\n      langClient?.client.info('Server started');\n    }\n  });\n\n  context.subscriptions.push(startCmd);\n\n  // Set up the documentation browser.\n  const docsDisposable = DocsBrowser.registerDocsBrowser();\n  context.subscriptions.push(docsDisposable);\n\n  const openOnHackageDisposable = DocsBrowser.registerDocsOpenOnHackage();\n  context.subscriptions.push(openOnHackageDisposable);\n\n  statusBar.refresh();\n  statusBar.show();\n}\n\nasync function activateServer(context: ExtensionContext, document: TextDocument) {\n  // We are only interested in Haskell files.\n  if (\n    (document.languageId !== 'haskell' &&\n      document.languageId !== 'cabal' &&\n      document.languageId !== 'literate haskell') ||\n    (document.uri.scheme !== 'file' && document.uri.scheme !== 'untitled')\n  ) {\n    return;\n  }\n\n  const uri = document.uri;\n  const folder = workspace.getWorkspaceFolder(uri);\n\n  await activateServerForFolder(context, uri, folder);\n}\n\nasync function activateServerForFolder(context: ExtensionContext, uri: Uri, folder?: WorkspaceFolder) {\n  const clientsKey = folder ? folder.uri.toString() : uri.toString();\n  // If the client already has an LSP server for this uri/folder, then don't start a new one.\n  if (clients.has(clientsKey)) {\n    return;\n  }\n  // Set the key to null to prevent multiple servers being launched at once\n  clients.set(clientsKey, null);\n\n  const config = initConfig(workspace.getConfiguration('haskell', uri), uri, folder);\n  const logger: Logger = initLoggerFromConfig(config);\n\n  logConfig(logger, config);\n\n  let hlsExecutable: HlsExecutable;\n  try {\n    hlsExecutable = await findHaskellLanguageServer(context, logger, config.ghcupConfig, config.workingDir, folder);\n  } catch (e) {\n    await handleInitializationError(e, logger);\n    // Make sure to release the key again.\n    clients.delete(clientsKey);\n    return;\n  }\n\n  const serverEnvironment: IEnvVars = initServerEnvironment(config, hlsExecutable);\n  const exeOptions: ExecutableOptions = {\n    cwd: config.workingDir,\n    env: { ...process.env, ...serverEnvironment },\n  };\n\n  // For our intents and purposes, the server should be launched the same way in\n  // both debug and run mode.\n  const serverOptions: ServerOptions = {\n    run: { command: hlsExecutable.location, args: config.serverArgs, options: exeOptions },\n    debug: { command: hlsExecutable.location, args: config.serverArgs, options: exeOptions },\n  };\n\n  // If we're operating on a standalone file (i.e. not in a folder) then we need\n  // to launch the server in a reasonable current directory. Otherwise the cradle\n  // guessing logic in hie-bios will be wrong!\n  let cwdMsg = `Activating the language server in working dir: ${config.workingDir}`;\n  if (folder) {\n    cwdMsg += ' (the workspace folder)';\n  } else {\n    cwdMsg += ` (parent dir of loaded file ${uri.fsPath})`;\n  }\n  logger.info(cwdMsg);\n\n  logger.info(`run command: ${hlsExecutable.location} ${config.serverArgs.join(' ')}`);\n  logger.info(`debug command: ${hlsExecutable.location} ${config.serverArgs.join(' ')}`);\n  if (exeOptions.cwd) {\n    logger.info(`server cwd: ${exeOptions.cwd}`);\n  }\n  if (serverEnvironment) {\n    logger.info('server environment variables:');\n    Object.entries(serverEnvironment).forEach(([key, val]: [string, string | undefined]) => {\n      logger.info(`  ${key}=${val}`);\n    });\n  }\n\n  const pat = folder ? `${folder.uri.fsPath}/**/*` : '**/*';\n  logger.log(`document selector patten: ${pat}`);\n\n  const cabalDocumentSelector = { scheme: 'file', language: 'cabal', pattern: pat };\n  const haskellDocumentSelector = [\n    { scheme: 'file', language: 'haskell', pattern: pat },\n    { scheme: 'file', language: 'literate haskell', pattern: pat },\n  ];\n\n  const documentSelector = [...haskellDocumentSelector];\n\n  const cabalFileSupport: 'automatic' | 'enable' | 'disable' = workspace.getConfiguration(\n    'haskell',\n    uri,\n  ).supportCabalFiles;\n  logger.info(`Support for '.cabal' files: ${cabalFileSupport}`);\n\n  switch (cabalFileSupport) {\n    case 'automatic': {\n      const hlsVersion = await callAsync(\n        hlsExecutable.location,\n        ['--numeric-version'],\n        logger,\n        config.workingDir,\n        undefined /* this command is very fast, don't show anything */,\n        false,\n        serverEnvironment,\n      );\n      if (comparePVP(hlsVersion, '1.9.0.0') >= 0) {\n        // If hlsVersion is >= '1.9.0.0'\n        documentSelector.push(cabalDocumentSelector);\n      }\n      break;\n    }\n    case 'enable':\n      documentSelector.push(cabalDocumentSelector);\n      break;\n    case 'disable':\n      break;\n    default:\n      break;\n  }\n\n  const clientOptions: LanguageClientOptions = {\n    // Use the document selector to only notify the LSP on files inside the folder\n    // path for the specific workspace.\n    documentSelector: [...documentSelector],\n    synchronize: {\n      // Synchronize the setting section 'haskell' to the server.\n      configurationSection: 'haskell',\n    },\n    diagnosticCollectionName: config.langName,\n    revealOutputChannelOn: RevealOutputChannelOn.Never,\n    outputChannel: config.outputChannel,\n    outputChannelName: config.langName,\n    middleware: {\n      provideHover: DocsBrowser.hoverLinksMiddlewareHook,\n      provideCompletionItem: DocsBrowser.completionLinksMiddlewareHook,\n    },\n    // Launch the server in the directory of the workspace folder.\n    workspaceFolder: folder,\n  };\n\n  // Create the LSP client.\n  const langClient = new LanguageClient('haskell', config.langName, serverOptions, clientOptions);\n\n  // Register ClientCapabilities for stuff like window/progress\n  langClient.registerProposedFeatures();\n\n  // Finally start the client and add it to the list of clients.\n  logger.info('Starting language server');\n  clients.set(clientsKey, {\n    client: langClient,\n    config,\n  });\n  await langClient.start();\n}\n\n/**\n * Handle errors the extension may throw. Errors are expected to be fatal.\n *\n * @param e Error thrown during the extension initialization.\n * @param logger\n */\nasync function handleInitializationError(e: unknown, logger: Logger) {\n  if (e instanceof MissingToolError) {\n    const link = e.installLink();\n    if (link) {\n      if (await window.showErrorMessage(e.message, `Install ${e.tool}`)) {\n        env.openExternal(link);\n      }\n    } else {\n      await window.showErrorMessage(e.message);\n    }\n  } else if (e instanceof HlsError) {\n    logger.error(`General HlsError: ${e.message}`);\n    window.showErrorMessage(e.message);\n  } else if (e instanceof NoMatchingHls) {\n    const link = e.docLink();\n    logger.error(`${e.message}`);\n    if (await window.showErrorMessage(e.message, 'Open documentation')) {\n      env.openExternal(link);\n    }\n  } else if (e instanceof Error) {\n    logger.error(`Internal Error: ${e.message}`);\n    window.showErrorMessage(e.message);\n  }\n  if (e instanceof Error) {\n    // general stack trace printing\n    if (e.stack) {\n      logger.error(`${e.stack}`);\n    }\n  }\n}\n\nfunction initServerEnvironment(config: Config, hlsExecutable: HlsExecutable) {\n  let serverEnvironment: IEnvVars = config.serverEnvironment;\n  if (hlsExecutable.tag === 'ghcup') {\n    const newPath = addPathToProcessPath(hlsExecutable.binaryDirectory);\n    serverEnvironment = {\n      ...serverEnvironment,\n      ...{ PATH: newPath },\n    };\n  }\n  return serverEnvironment;\n}\n\n/*\n * Deactivate each of the LSP servers.\n */\nexport async function deactivate() {\n  const promises: Thenable<void>[] = [];\n  for (const client of clients.values()) {\n    if (client) {\n      promises.push(client.client.stop());\n    }\n  }\n  await Promise.all(promises);\n}\n"
  },
  {
    "path": "src/ghcup.ts",
    "content": "import * as path from 'path';\nimport * as os from 'os';\nimport * as process from 'process';\nimport { WorkspaceFolder } from 'vscode';\nimport { Logger } from 'vscode-languageclient';\nimport { MissingToolError } from './errors';\nimport { resolvePathPlaceHolders, executableExists, callAsync, ProcessCallback, IEnvVars } from './utils';\nimport { match } from 'ts-pattern';\n\nexport type Tool = 'hls' | 'ghc' | 'cabal' | 'stack';\n\nexport type ToolConfig = Map<Tool, string | null>;\n\nexport function initDefaultGHCup(config: GHCupConfig, logger: Logger, folder?: WorkspaceFolder): GHCup {\n  const ghcupLoc = findGHCup(logger, config.executablePath, folder);\n  return new GHCup(logger, ghcupLoc, config, {\n    // omit colourful output because the logs are uglier\n    NO_COLOR: '1',\n  });\n}\n\nexport type GHCupConfig = {\n  metadataUrl?: string;\n  upgradeGHCup: boolean;\n  executablePath?: string;\n};\n\nexport type ToolInfo = {\n  tool: Tool;\n  version: string;\n  tags: string[];\n};\n\nexport class GHCup {\n  constructor(\n    readonly logger: Logger,\n    readonly location: string,\n    readonly config: GHCupConfig,\n    readonly environment: IEnvVars,\n  ) {}\n\n  /**\n   * Most generic way to run the `ghcup` binary.\n   * @param args Arguments to run the `ghcup` binary with.\n   * @param title Displayed to the user for long-running tasks.\n   * @param cancellable Whether this invocation can be cancelled by the user.\n   * @param callback Handle success or failures.\n   * @returns The output of the `ghcup` invocation. If no {@link callback} is given, this is the stdout. Otherwise, whatever {@link callback} produces.\n   */\n  public async call(\n    args: string[],\n    title?: string,\n    cancellable?: boolean,\n    callback?: ProcessCallback,\n  ): Promise<string> {\n    const metadataUrl = this.config.metadataUrl; // ;\n    return await callAsync(\n      this.location,\n      ['--no-verbose'].concat(metadataUrl ? ['-s', metadataUrl] : []).concat(args),\n      this.logger,\n      undefined,\n      title,\n      cancellable,\n      this.environment,\n      callback,\n    );\n  }\n\n  /**\n   * Upgrade the `ghcup` binary unless this option was disabled by the user.\n   */\n  public async upgrade(): Promise<void> {\n    const upgrade = this.config.upgradeGHCup;\n    if (upgrade) {\n      await this.call(['upgrade'], 'Upgrading ghcup', true);\n    }\n  }\n\n  /**\n   * Find the `set` version of a {@link Tool} in GHCup.\n   * If no version is set, return null.\n   * @param tool Tool you want to know the latest version of.\n   * @returns The latest installed or generally available version of the {@link tool}\n   */\n  public async getSetVersion(tool: Tool): Promise<ToolInfo | null> {\n    // these might be custom/stray/compiled, so we try first\n    const installedVersions = await this.listTool(tool, 'set');\n    const latestInstalled = installedVersions.pop();\n    if (latestInstalled) {\n      return latestInstalled;\n    } else {\n      return null;\n    }\n  }\n\n  /**\n   * Find the latest version of a {@link Tool} that we can find in GHCup.\n   * Prefer already installed versions, but fall back to all available versions, if there aren't any.\n   * @param tool Tool you want to know the latest version of.\n   * @returns The latest installed or generally available version of the {@link tool}\n   */\n  public async getAnyLatestVersion(tool: Tool): Promise<ToolInfo | null> {\n    // these might be custom/stray/compiled, so we try first\n    const installedVersions = await this.listTool(tool, 'installed');\n    const latestInstalled = installedVersions.pop();\n    if (latestInstalled) {\n      return latestInstalled;\n    } else {\n      return this.getLatestAvailableVersion(tool);\n    }\n  }\n\n  /**\n   * Find the latest available version that we can find in GHCup with a certain {@link tag}.\n   * Corresponds to the `ghcup list -t <tool> -c available -r` command.\n   * The tag can be used to further filter the list of versions, for example you can provide\n   * @param tool Tool you want to know the latest version of.\n   * @param tag The tag to filter the available versions with. By default `\"latest\"`.\n   * @returns The latest available version filtered by {@link tag}.\n   */\n  public async getLatestAvailableVersion(tool: Tool, tag: string = 'latest'): Promise<ToolInfo> {\n    // fall back to installable versions\n    const availableVersions = await this.listTool(tool, 'available');\n\n    let latestAvailable: ToolInfo | null = null;\n    availableVersions.forEach((toolInfo) => {\n      if (toolInfo.tags.includes(tag)) {\n        latestAvailable = toolInfo;\n      }\n    });\n    if (!latestAvailable) {\n      throw new Error(`Unable to find ${tag} tool ${tool}`);\n    } else {\n      return latestAvailable;\n    }\n  }\n\n  private async listTool(tool: Tool, category: string): Promise<ToolInfo[]> {\n    // fall back to installable versions\n    const availableVersions = await this.call(['list', '-t', tool, '-c', category, '-r'], undefined, false).then((s) =>\n      s.split(/\\r?\\n/),\n    );\n\n    return availableVersions.map((toolString) => {\n      const toolParts = toolString.split(/\\s+/);\n      return {\n        tool: tool,\n        version: toolParts[1],\n        tags: toolParts[2]?.split(',') ?? [],\n      };\n    });\n  }\n\n  public async findLatestUserInstalledTool(tool: Tool): Promise<ToolInfo> {\n    let toolInfo = null;\n    toolInfo = await this.getSetVersion(tool);\n    if (toolInfo) return toolInfo;\n    toolInfo = await this.getAnyLatestVersion(tool);\n    if (toolInfo) return toolInfo;\n    throw new Error(`Unable to find a version for tool ${tool}`);\n  }\n}\n\nfunction findGHCup(logger: Logger, exePath?: string, folder?: WorkspaceFolder): string {\n  logger.info('Checking for ghcup installation');\n  if (exePath) {\n    logger.info(`Trying to find the ghcup executable in: ${exePath}`);\n    exePath = resolvePathPlaceHolders(exePath, folder);\n    logger.log(`Location after path variables substitution: ${exePath}`);\n    if (executableExists(exePath)) {\n      return exePath;\n    } else {\n      throw new Error(`Could not find a ghcup binary at ${exePath}!`);\n    }\n  } else {\n    const localGHCup = ['ghcup'].find(executableExists);\n    if (!localGHCup) {\n      logger.info(`probing for GHCup binary`);\n      const ghcupExe: string | null = match(process.platform)\n        .with('win32', () => {\n          const ghcupPrefix = process.env.GHCUP_INSTALL_BASE_PREFIX;\n          if (ghcupPrefix) {\n            return path.join(ghcupPrefix, 'ghcup', 'bin', 'ghcup.exe');\n          } else {\n            return path.join('C:\\\\', 'ghcup', 'bin', 'ghcup.exe');\n          }\n        })\n        .otherwise(() => {\n          const useXDG = process.env.GHCUP_USE_XDG_DIRS;\n          if (useXDG) {\n            const xdgBin = process.env.XDG_BIN_HOME;\n            if (xdgBin) {\n              return path.join(xdgBin, 'ghcup');\n            } else {\n              return path.join(os.homedir(), '.local', 'bin', 'ghcup');\n            }\n          } else {\n            const ghcupPrefix = process.env.GHCUP_INSTALL_BASE_PREFIX;\n            if (ghcupPrefix) {\n              return path.join(ghcupPrefix, '.ghcup', 'bin', 'ghcup');\n            } else {\n              return path.join(os.homedir(), '.ghcup', 'bin', 'ghcup');\n            }\n          }\n        });\n      if (ghcupExe !== null && executableExists(ghcupExe)) {\n        return ghcupExe;\n      } else {\n        logger.warn(`ghcup at ${ghcupExe} does not exist`);\n        throw new MissingToolError('ghcup');\n      }\n    } else {\n      logger.info(`found ghcup at ${localGHCup}`);\n      return localGHCup;\n    }\n  }\n}\n"
  },
  {
    "path": "src/hlsBinaries.ts",
    "content": "import * as fs from 'fs';\nimport * as path from 'path';\nimport { ConfigurationTarget, ExtensionContext, window, workspace, WorkspaceFolder } from 'vscode';\nimport { Logger } from 'vscode-languageclient';\nimport { HlsError, MissingToolError, NoMatchingHls } from './errors';\nimport {\n  addPathToProcessPath,\n  callAsync,\n  comparePVP,\n  executableExists,\n  IEnvVars,\n  resolvePathPlaceHolders,\n} from './utils';\nimport { ToolConfig, Tool, initDefaultGHCup, GHCup, GHCupConfig } from './ghcup';\nimport { getHlsMetadata } from './metadata';\nexport { IEnvVars, fetchConfig };\n\nexport type Context = {\n  manageHls: ManageHLS;\n  storagePath: string;\n  serverExecutable?: HlsExecutable;\n  logger: Logger;\n};\n\n/**\n * Global configuration for this extension.\n */\nlet haskellConfig = workspace.getConfiguration('haskell');\n\n/**\n * On Windows the executable needs to be stored somewhere with an .exe extension\n */\nconst exeExt = process.platform === 'win32' ? '.exe' : '';\n\ntype ManageHLS = 'GHCup' | 'PATH';\nlet manageHLS = haskellConfig.get('manageHLS') as ManageHLS;\n\nfunction fetchConfig() {\n  haskellConfig = workspace.getConfiguration('haskell');\n  manageHLS = haskellConfig.get('manageHLS') as ManageHLS;\n}\n\n/**\n * Gets serverExecutablePath and fails if it's not set.\n * @param logger Log progress.\n * @param folder Workspace folder. Used for resolving variables in the `serverExecutablePath`.\n * @returns Path to an HLS executable binary.\n */\nfunction findServerExecutable(logger: Logger, folder?: WorkspaceFolder): string {\n  const rawExePath = haskellConfig.get('serverExecutablePath') as string;\n  logger.info(`Trying to find the server executable in: ${rawExePath}`);\n  const resolvedExePath = resolvePathPlaceHolders(rawExePath, folder);\n  logger.log(`Location after path variables substitution: ${resolvedExePath}`);\n  if (executableExists(resolvedExePath)) {\n    return resolvedExePath;\n  } else {\n    const msg = `Could not find a HLS binary at ${resolvedExePath}! Consider installing HLS via ghcup or change \"haskell.manageHLS\" in your settings.`;\n    throw new HlsError(msg);\n  }\n}\n\n/**\n * Searches the `PATH` for `haskell-language-server` or `haskell-language-server-wrapper` binary.\n * Fails if nothing is found.\n * @param logger Log all the stuff!\n * @returns Location of the `haskell-language-server` or `haskell-language-server-wrapper` binary if found.\n */\nfunction findHlsInPath(logger: Logger): string {\n  // try PATH\n  const exes: string[] = ['haskell-language-server-wrapper', 'haskell-language-server'];\n  logger.info(`Searching for server executables ${exes.join(',')} in $PATH`);\n  logger.info(`$PATH environment variable: ${process.env.PATH}`);\n  for (const exe of exes) {\n    if (executableExists(exe)) {\n      logger.info(`Found server executable in $PATH: ${exe}`);\n      return exe;\n    }\n  }\n  throw new MissingToolError('hls');\n}\n\nexport type HlsExecutable = HlsOnPath | HlsViaVSCodeConfig | HlsViaGhcup;\n\nexport type HlsOnPath = {\n  location: string;\n  tag: 'path';\n};\n\nexport type HlsViaVSCodeConfig = {\n  location: string;\n  tag: 'config';\n};\n\nexport type HlsViaGhcup = {\n  location: string;\n  /**\n   * if we download HLS, add that bin dir to PATH\n   */\n  binaryDirectory: string;\n  tag: 'ghcup';\n};\n\n/**\n * Find and setup the Haskell Language Server.\n *\n * We support three ways of finding the HLS binary:\n *\n * 1. Let the user provide a location via `haskell.serverExecutablePath` option.\n * 2. Find a `haskell-language-server` binary on the `$PATH` if the user wants to do that.\n * 3. Use GHCup to install and locate HLS and other required tools, such as cabal, stack and ghc.\n *\n * @param context Context of the extension, required for metadata.\n * @param logger Logger for progress updates.\n * @param workingDir Working directory in VSCode.\n * @param folder Optional workspace folder. If given, will be preferred over {@link workingDir} for finding configuration entries.\n * @returns Path to haskell-language-server, paired with additional data required for setting up.\n */\nexport async function findHaskellLanguageServer(\n  context: ExtensionContext,\n  logger: Logger,\n  ghcupConfig: GHCupConfig,\n  workingDir: string,\n  folder?: WorkspaceFolder,\n): Promise<HlsExecutable> {\n  logger.info('Finding haskell-language-server');\n\n  const hasConfigForExecutable = haskellConfig.get('serverExecutablePath') as string;\n  if (hasConfigForExecutable) {\n    const exe = findServerExecutable(logger, folder);\n    return {\n      location: exe,\n      tag: 'config',\n    };\n  }\n\n  const storagePath: string = getStoragePath(context);\n  if (!fs.existsSync(storagePath)) {\n    fs.mkdirSync(storagePath);\n  }\n\n  // first extension initialization\n  manageHLS = await promptUserForManagingHls(context, manageHLS);\n\n  // based on the user-decision\n  if (manageHLS === 'PATH') {\n    const exe = findHlsInPath(logger);\n    return {\n      location: exe,\n      tag: 'path',\n    };\n  } else {\n    // we manage HLS, make sure ghcup is installed/available\n    const ghcup = initDefaultGHCup(ghcupConfig, logger, folder);\n    await ghcup.upgrade();\n\n    // boring init\n    let latestHLS: string | undefined | null;\n    let latestCabal: string | undefined | null;\n    let latestStack: string | undefined | null;\n    let recGHC: string | undefined | null = 'recommended';\n    let projectHls: string | undefined | null;\n    let projectGhc: string | undefined | null;\n\n    // support explicit toolchain config\n    const toolchainConfig = new Map(Object.entries(haskellConfig.get('toolchain') as ToolConfig)) as ToolConfig;\n    if (toolchainConfig) {\n      latestHLS = toolchainConfig.get('hls');\n      latestCabal = toolchainConfig.get('cabal');\n      latestStack = toolchainConfig.get('stack');\n      recGHC = toolchainConfig.get('ghc');\n\n      projectHls = latestHLS;\n      projectGhc = recGHC;\n    }\n\n    // get a preliminary toolchain for finding the correct project GHC version\n    // (we need HLS and cabal/stack and ghc as fallback),\n    // later we may install a different toolchain that's more project-specific\n    if (latestHLS === undefined) {\n      latestHLS = await ghcup.getAnyLatestVersion('hls').then((tool) => tool?.version);\n    }\n    if (latestCabal === undefined) {\n      latestCabal = (await ghcup.findLatestUserInstalledTool('cabal')).version;\n    }\n    if (latestStack === undefined) {\n      latestStack = (await ghcup.findLatestUserInstalledTool('stack')).version;\n    }\n    if (recGHC === undefined) {\n      recGHC = !executableExists('ghc') ? (await ghcup.getLatestAvailableVersion('ghc', 'recommended')).version : null;\n    }\n\n    // download popups\n    const promptBeforeDownloads = haskellConfig.get('promptBeforeDownloads') as boolean;\n    if (promptBeforeDownloads) {\n      const hlsInstalled = latestHLS ? await installationStatusOfGhcupTool(ghcup, 'hls', latestHLS) : undefined;\n      const cabalInstalled = latestCabal ? await installationStatusOfGhcupTool(ghcup, 'cabal', latestCabal) : undefined;\n      const stackInstalled = latestStack ? await installationStatusOfGhcupTool(ghcup, 'stack', latestStack) : undefined;\n      const ghcInstalled = executableExists('ghc')\n        ? new ToolStatus(\n            'ghc',\n            await callAsync(`ghc${exeExt}`, ['--numeric-version'], logger, undefined, undefined, false),\n          )\n        : // if recGHC is null, that means user disabled automatic handling,\n          recGHC !== null\n          ? await installationStatusOfGhcupTool(ghcup, 'ghc', recGHC)\n          : undefined;\n      const toInstall: ToolStatus[] = [hlsInstalled, cabalInstalled, stackInstalled, ghcInstalled].filter(\n        (tool) => tool && !tool.installed,\n      ) as ToolStatus[];\n      if (toInstall.length > 0) {\n        const decision = await window.showInformationMessage(\n          `Need to download ${toInstall.map((t) => t.nameWithVersion).join(', ')}, continue?`,\n          'Yes',\n          'No',\n          \"Yes, don't ask again\",\n        );\n        if (decision === 'Yes') {\n          logger.info(`User accepted download for ${toInstall.map((t) => t.nameWithVersion).join(', ')}.`);\n        } else if (decision === \"Yes, don't ask again\") {\n          logger.info(\n            `User accepted download for ${toInstall.map((t) => t.nameWithVersion).join(', ')} and won't be asked again.`,\n          );\n          haskellConfig.update('promptBeforeDownloads', false);\n        } else {\n          toInstall.forEach((tool) => {\n            if (tool !== undefined && !tool.installed) {\n              if (tool.name === 'hls') {\n                throw new MissingToolError('hls');\n              } else if (tool.name === 'cabal') {\n                latestCabal = null;\n              } else if (tool.name === 'stack') {\n                latestStack = null;\n              } else if (tool.name === 'ghc') {\n                recGHC = null;\n              }\n            }\n          });\n        }\n      }\n    }\n\n    // our preliminary toolchain\n    const latestToolchainBindir = await ghcup.call(\n      [\n        'run',\n        ...(latestHLS ? ['--hls', latestHLS] : []),\n        ...(latestCabal ? ['--cabal', latestCabal] : []),\n        ...(latestStack ? ['--stack', latestStack] : []),\n        ...(recGHC ? ['--ghc', recGHC] : []),\n        '--install',\n      ],\n      'Installing latest toolchain for bootstrap',\n      true,\n      (err, stdout, _stderr, resolve, reject) => {\n        if (err) {\n          reject(\"Couldn't install latest toolchain\");\n        } else {\n          resolve(stdout?.trim());\n        }\n      },\n    );\n\n    // now figure out the actual project GHC version and the latest supported HLS version\n    // we need for it (e.g. this might in fact be a downgrade for old GHCs)\n    if (projectHls === undefined || projectGhc === undefined) {\n      const res = await getLatestProjectHls(ghcup, logger, storagePath, workingDir, latestToolchainBindir);\n      if (projectHls === undefined) {\n        projectHls = res[0];\n      }\n      if (projectGhc === undefined) {\n        projectGhc = res[1];\n      }\n    }\n\n    // more download popups\n    if (promptBeforeDownloads) {\n      const hlsInstalled = projectHls ? await installationStatusOfGhcupTool(ghcup, 'hls', projectHls) : undefined;\n      const ghcInstalled = projectGhc ? await installationStatusOfGhcupTool(ghcup, 'ghc', projectGhc) : undefined;\n      const toInstall: ToolStatus[] = [hlsInstalled, ghcInstalled].filter(\n        (tool) => tool && !tool.installed,\n      ) as ToolStatus[];\n      if (toInstall.length > 0) {\n        const decision = await window.showInformationMessage(\n          `Need to download ${toInstall.map((t) => t.nameWithVersion).join(', ')}, continue?`,\n          { modal: true },\n          'Yes',\n          'No',\n          \"Yes, don't ask again\",\n        );\n        if (decision === 'Yes') {\n          logger.info(`User accepted download for ${toInstall.map((t) => t.nameWithVersion).join(', ')}.`);\n        } else if (decision === \"Yes, don't ask again\") {\n          logger.info(\n            `User accepted download for ${toInstall.map((t) => t.nameWithVersion).join(', ')} and won't be asked again.`,\n          );\n          haskellConfig.update('promptBeforeDownloads', false);\n        } else {\n          toInstall.forEach((tool) => {\n            if (!tool.installed) {\n              if (tool.name === 'hls') {\n                throw new MissingToolError('hls');\n              } else if (tool.name === 'ghc') {\n                projectGhc = null;\n              }\n            }\n          });\n        }\n      }\n    }\n\n    // now install the proper versions\n    const hlsBinDir = await ghcup.call(\n      [\n        'run',\n        ...(projectHls ? ['--hls', projectHls] : []),\n        ...(latestCabal ? ['--cabal', latestCabal] : []),\n        ...(latestStack ? ['--stack', latestStack] : []),\n        ...(projectGhc ? ['--ghc', projectGhc] : []),\n        '--install',\n      ],\n      `Installing project specific toolchain: ${[\n        ['hls', projectHls],\n        ['GHC', projectGhc],\n        ['cabal', latestCabal],\n        ['stack', latestStack],\n      ]\n        .filter((t) => t[1])\n        .map((t) => `${t[0]}-${t[1]}`)\n        .join(', ')}`,\n      true,\n    );\n\n    if (projectHls) {\n      return {\n        binaryDirectory: hlsBinDir,\n        location: path.join(hlsBinDir, `haskell-language-server-wrapper${exeExt}`),\n        tag: 'ghcup',\n      };\n    } else {\n      return {\n        binaryDirectory: hlsBinDir,\n        location: findHlsInPath(logger),\n        tag: 'ghcup',\n      };\n    }\n  }\n}\n\nasync function promptUserForManagingHls(context: ExtensionContext, manageHlsSetting: ManageHLS): Promise<ManageHLS> {\n  if (manageHlsSetting !== 'GHCup' && (!context.globalState.get('pluginInitialized') as boolean | null)) {\n    const promptMessage = `How do you want the extension to manage/discover HLS and the relevant toolchain?\n\n    Choose \"Automatically\" if you're in doubt.\n    `;\n\n    const popup = window.showInformationMessage(\n      promptMessage,\n      { modal: true },\n      'Automatically via GHCup',\n      'Manually via PATH',\n    );\n\n    const decision = (await popup) || null;\n    let howToManage: ManageHLS;\n    if (decision === 'Automatically via GHCup') {\n      howToManage = 'GHCup';\n    } else if (decision === 'Manually via PATH') {\n      howToManage = 'PATH';\n    } else {\n      window.showWarningMessage(\n        \"Choosing default PATH method for HLS discovery. You can change this via 'haskell.manageHLS' in the settings.\",\n      );\n      howToManage = 'PATH';\n    }\n    haskellConfig.update('manageHLS', howToManage, ConfigurationTarget.Global);\n    context.globalState.update('pluginInitialized', true);\n    return howToManage;\n  } else {\n    return manageHlsSetting;\n  }\n}\n\nasync function getLatestProjectHls(\n  ghcup: GHCup,\n  logger: Logger,\n  storagePath: string,\n  workingDir: string,\n  toolchainBindir: string,\n): Promise<[string, string]> {\n  // get project GHC version, but fallback to system ghc if necessary.\n  const projectGhc = toolchainBindir\n    ? await getProjectGhcVersion(toolchainBindir, workingDir, logger).catch(async (e) => {\n        logger.error(`${e}`);\n        window.showWarningMessage(\n          `I had trouble figuring out the exact GHC version for the project. Falling back to using 'ghc${exeExt}'.`,\n        );\n        return await callAsync(`ghc${exeExt}`, ['--numeric-version'], logger, undefined, undefined, false);\n      })\n    : await callAsync(`ghc${exeExt}`, ['--numeric-version'], logger, undefined, undefined, false);\n\n  // first we get supported GHC versions from available HLS bindists (whether installed or not)\n  const metadataMap = (await getHlsMetadata(storagePath, logger)) || new Map<string, string[]>();\n  // then we get supported GHC versions from currently installed HLS versions\n  const ghcupMap = (await findAvailableHlsBinariesFromGHCup(ghcup)) || new Map<string, string[]>();\n  // since installed HLS versions may support a different set of GHC versions than the bindists\n  // (e.g. because the user ran 'ghcup compile hls'), we need to merge both maps, preferring\n  // values from already installed HLSes\n  const merged = new Map<string, string[]>([...metadataMap, ...ghcupMap]); // right-biased\n  // now sort and get the latest suitable version\n  const latest = [...merged]\n    .filter(([_k, v]) => v.some((x) => x === projectGhc))\n    .sort(([k1, _v1], [k2, _v2]) => comparePVP(k1, k2))\n    .pop();\n\n  if (!latest) {\n    throw new NoMatchingHls(projectGhc);\n  } else {\n    return [latest[0], projectGhc];\n  }\n}\n\n/**\n * Obtain the project ghc version from the HLS - Wrapper (which must be in PATH now).\n * Also, serves as a sanity check.\n * @param toolchainBindir Path to the toolchain bin directory (added to PATH)\n * @param workingDir Directory to run the process, usually the root of the workspace.\n * @param logger Logger for feedback.\n * @returns The GHC version, or fail with an `Error`.\n */\nexport async function getProjectGhcVersion(\n  toolchainBindir: string,\n  workingDir: string,\n  logger: Logger,\n): Promise<string> {\n  const title = 'Working out the project GHC version. This might take a while...';\n  logger.info(title);\n\n  const args = ['--project-ghc-version'];\n\n  const newPath = addPathToProcessPath(toolchainBindir);\n  const environmentNew: IEnvVars = {\n    PATH: newPath,\n  };\n\n  return callAsync(\n    'haskell-language-server-wrapper',\n    args,\n    logger,\n    workingDir,\n    title,\n    false,\n    environmentNew,\n    (err, stdout, stderr, resolve, reject) => {\n      if (err) {\n        // Error message emitted by HLS-wrapper\n        const regex =\n          /Cradle requires (.+) but couldn't find it|The program '(.+)' version .* is required but the version of.*could.*not be determined|Cannot find the program '(.+)'\\. User-specified/;\n        const res = regex.exec(stderr);\n        if (res) {\n          for (let i = 1; i < res.length; i++) {\n            if (res[i]) {\n              reject(new MissingToolError(res[i]));\n            }\n          }\n          reject(new MissingToolError('unknown'));\n        }\n        reject(\n          Error(\n            `haskell-language-server --project-ghc-version exited with exit code ${err.code}:\\n${stdout}\\n${stderr}`,\n          ),\n        );\n      } else {\n        logger.info(`The GHC version for the project or file: ${stdout?.trim()}`);\n        resolve(stdout?.trim());\n      }\n    },\n  );\n}\n\n/**\n * Find the storage path for the extension.\n * If no custom location was given\n *\n * @param context Extension context for the 'Storage Path'.\n * @returns\n */\nexport function getStoragePath(context: ExtensionContext): string {\n  let storagePath: string | undefined = haskellConfig.get('releasesDownloadStoragePath');\n\n  if (!storagePath) {\n    storagePath = context.globalStorageUri.fsPath;\n  } else {\n    storagePath = resolvePathPlaceHolders(storagePath);\n  }\n\n  return storagePath;\n}\n\n/**\n *\n * Complements {@link getReleaseMetadata}, by checking possibly locally compiled\n * HLS in ghcup\n * If 'targetGhc' is omitted, picks the latest 'haskell-language-server-wrapper',\n * otherwise ensures the specified GHC is supported.\n *\n * @param ghcup GHCup wrapper.\n * @returns A Map of the locally installed HLS versions and with which `GHC` versions they are compatible.\n */\n\nasync function findAvailableHlsBinariesFromGHCup(ghcup: GHCup): Promise<Map<string, string[]> | null> {\n  const hlsVersions = await ghcup.call(['list', '-t', 'hls', '-c', 'installed', '-r'], undefined, false);\n\n  const bindir = await ghcup.call(['whereis', 'bindir'], undefined, false);\n  const files = fs.readdirSync(bindir).filter((e) => {\n    const stat = fs.statSync(path.join(bindir, e));\n    return stat.isFile();\n  });\n\n  const installed = hlsVersions.split(/\\r?\\n/).map((e) => e.split(/\\s+/)[1]);\n  if (installed?.length) {\n    const myMap = new Map<string, string[]>();\n    installed.forEach((hls) => {\n      const ghcs = files\n        .filter((f) => f.endsWith(`~${hls}${exeExt}`) && f.startsWith('haskell-language-server-'))\n        .map((f) => {\n          const rmPrefix = f.substring('haskell-language-server-'.length);\n          return rmPrefix.substring(0, rmPrefix.length - `~${hls}${exeExt}`.length);\n        });\n      myMap.set(hls, ghcs);\n    });\n    return myMap;\n  } else {\n    return null;\n  }\n}\n\nasync function installationStatusOfGhcupTool(ghcup: GHCup, tool: Tool, version: string): Promise<ToolStatus> {\n  const b = await ghcup\n    .call(['whereis', tool, version], undefined, false)\n    .then(() => true)\n    .catch(() => false);\n  return new ToolStatus(tool, version, b);\n}\n\n/**\n * Tracks the name, version and installation state of tools we need.\n */\nclass ToolStatus {\n  /**\n   * \"\\<name\\>-\\<version\\>\" of the installed Tool.\n   */\n  readonly nameWithVersion: string = '';\n\n  /**\n   * Initialize an installed tool entry.\n   *\n   * If optional parameters are omitted, we assume the tool is installed.\n   *\n   * @param name Name of the tool.\n   * @param version Version of the tool, expected to be either SemVer or PVP versioned.\n   * @param installed Is this tool currently installed?\n   */\n  public constructor(\n    readonly name: string,\n    readonly version: string,\n    readonly installed: boolean = true,\n  ) {\n    this.nameWithVersion = `${name}-${version}`;\n  }\n}\n"
  },
  {
    "path": "src/logger.ts",
    "content": "import { OutputChannel } from 'vscode';\nimport { Logger } from 'vscode-languageclient';\nimport * as fs from 'fs';\n\nenum LogLevel {\n  Off,\n  Error,\n  Warn,\n  Info,\n  Debug,\n}\nexport class ExtensionLogger implements Logger {\n  public readonly name: string;\n  public readonly level: LogLevel;\n  public readonly channel: OutputChannel;\n  public readonly logFile: string | undefined;\n\n  constructor(name: string, level: string, channel: OutputChannel, logFile: string | undefined) {\n    this.name = name;\n    this.level = this.getLogLevel(level);\n    this.channel = channel;\n    this.logFile = logFile;\n  }\n  public warn(message: string): void {\n    this.logLevel(LogLevel.Warn, message);\n  }\n\n  public info(message: string): void {\n    this.logLevel(LogLevel.Info, message);\n  }\n\n  public error(message: string) {\n    this.logLevel(LogLevel.Error, message);\n  }\n\n  public log(message: string) {\n    this.logLevel(LogLevel.Debug, message);\n  }\n\n  private write(msg: string) {\n    let now = new Date();\n    // Ugly hack to make js date iso format similar to hls one\n    const offset = now.getTimezoneOffset();\n    now = new Date(now.getTime() - offset * 60 * 1000);\n    const timedMsg = `${new Date().toISOString().replace('T', ' ').replace('Z', '0000')} ${msg}`;\n    this.channel.appendLine(timedMsg);\n    if (this.logFile) {\n      fs.appendFileSync(this.logFile, timedMsg + '\\n');\n    }\n  }\n\n  private logLevel(level: LogLevel, msg: string) {\n    if (level <= this.level) {\n      this.write(`[${this.name}] ${LogLevel[level].toUpperCase()} ${msg}`);\n    }\n  }\n\n  private getLogLevel(level: string) {\n    switch (level) {\n      case 'off':\n        return LogLevel.Off;\n      case 'error':\n        return LogLevel.Error;\n      case 'debug':\n        return LogLevel.Debug;\n      default:\n        return LogLevel.Info;\n    }\n  }\n}\n"
  },
  {
    "path": "src/metadata.ts",
    "content": "import * as fs from 'fs';\nimport * as https from 'https';\nimport * as path from 'path';\nimport { match } from 'ts-pattern';\nimport { promisify } from 'util';\nimport { window, workspace } from 'vscode';\nimport { Logger } from 'vscode-languageclient';\nimport { httpsGetSilently } from './utils';\n\n/**\n * Metadata of release information.\n *\n * Example of the expected format:\n *\n * ```\n * {\n *  \"1.6.1.0\": {\n *     \"A_64\": {\n *       \"Darwin\": [\n *         \"8.10.6\",\n *       ],\n *       \"Linux_Alpine\": [\n *         \"8.10.7\",\n *         \"8.8.4\",\n *       ],\n *     },\n *     \"A_ARM\": {\n *       \"Linux_UnknownLinux\": [\n *         \"8.10.7\"\n *       ]\n *     },\n *     \"A_ARM64\": {\n *       \"Darwin\": [\n *         \"8.10.7\"\n *       ],\n *       \"Linux_UnknownLinux\": [\n *         \"8.10.7\"\n *       ]\n *     }\n *   }\n * }\n * ```\n *\n * consult [ghcup metadata repo](https://github.com/haskell/ghcup-metadata/) for details.\n */\nexport type ReleaseMetadata = Map<string, Map<string, Map<string, string[]>>>;\n\nexport type Platform = 'Darwin' | 'Linux_UnknownLinux' | 'Windows' | 'FreeBSD';\n\nexport type Arch = 'A_ARM' | 'A_ARM64' | 'A_32' | 'A_64';\n\n/**\n * Compute Map of supported HLS versions for this platform.\n * Fetches HLS metadata information.\n *\n * @param storagePath Path to put in binary files and caches.\n * @param logger Logger for feedback\n * @returns Map of supported HLS versions or null if metadata could not be fetched.\n */\nexport async function getHlsMetadata(storagePath: string, logger: Logger): Promise<Map<string, string[]> | null> {\n  const metadata = await getReleaseMetadata(storagePath, logger).catch(() => null);\n  if (!metadata) {\n    window.showErrorMessage('Could not get release metadata');\n    return null;\n  }\n  const plat: Platform | null = match(process.platform)\n    .with('darwin', () => 'Darwin' as Platform)\n    .with('linux', () => 'Linux_UnknownLinux' as Platform)\n    .with('win32', () => 'Windows' as Platform)\n    .with('freebsd', () => 'FreeBSD' as Platform)\n    .otherwise(() => null);\n  if (plat === null) {\n    throw new Error(`Unknown platform ${process.platform}`);\n  }\n  const arch: Arch | null = match(process.arch)\n    .with('arm', () => 'A_ARM' as Arch)\n    .with('arm64', () => 'A_ARM64' as Arch)\n    .with('ia32', () => 'A_32' as Arch)\n    .with('x64', () => 'A_64' as Arch)\n    .otherwise(() => null);\n  if (arch === null) {\n    throw new Error(`Unknown architecture ${process.arch}`);\n  }\n\n  return findSupportedHlsPerGhc(plat, arch, metadata, logger);\n}\n/**\n * Find all supported GHC versions per HLS version supported on the given\n * platform and architecture.\n * @param platform Platform of the host.\n * @param arch Arch of the host.\n * @param metadata HLS Metadata information.\n * @param logger Logger.\n * @returns Map from HLS version to GHC versions that are supported.\n */\nexport function findSupportedHlsPerGhc(\n  platform: Platform,\n  arch: Arch,\n  metadata: ReleaseMetadata,\n  logger: Logger,\n): Map<string, string[]> {\n  logger.info(`Platform constants: ${platform}, ${arch}`);\n  const newMap = new Map<string, string[]>();\n  metadata.forEach((supportedArch, hlsVersion) => {\n    const supportedOs = supportedArch.get(arch);\n    if (supportedOs) {\n      const ghcSupportedOnOs = supportedOs.get(platform);\n      if (ghcSupportedOnOs) {\n        logger.log(`HLS ${hlsVersion} compatible with GHC Versions: ${ghcSupportedOnOs.join(',')}`);\n        // copy supported ghc versions to avoid unintended modifications\n        newMap.set(hlsVersion, [...ghcSupportedOnOs]);\n      }\n    }\n  });\n\n  return newMap;\n}\n\n/**\n * Download GHCUP metadata.\n *\n * @param storagePath Path to put in binary files and caches.\n * @param logger Logger for feedback.\n * @returns Metadata of releases, or null if the cache can not be found.\n */\nasync function getReleaseMetadata(storagePath: string, logger: Logger): Promise<ReleaseMetadata | null> {\n  const releasesUrl = workspace.getConfiguration('haskell').releasesURL\n    ? new URL(workspace.getConfiguration('haskell').releasesURL as string)\n    : undefined;\n  const opts: https.RequestOptions = releasesUrl\n    ? {\n        host: releasesUrl.host,\n        path: releasesUrl.pathname,\n      }\n    : {\n        host: 'raw.githubusercontent.com',\n        path: '/haskell/ghcup-metadata/master/hls-metadata-0.0.1.json',\n      };\n\n  const offlineCache = path.join(storagePath, 'ghcupReleases.cache.json');\n\n  /**\n   * Convert a json value to ReleaseMetadata.\n   * Assumes the json is well-formed and a valid Release-Metadata.\n   * @param someObj Release Metadata without any typing information but well-formed.\n   * @returns Typed ReleaseMetadata.\n   */\n  const objectToMetadata = (someObj: any): ReleaseMetadata => {\n    const obj = someObj as [string: [string: [string: string[]]]];\n    const hlsMetaEntries = Object.entries(obj).map(([hlsVersion, archMap]) => {\n      const archMetaEntries = Object.entries(archMap).map(([arch, supportedGhcVersionsPerOs]) => {\n        return [arch, new Map(Object.entries(supportedGhcVersionsPerOs))] as [string, Map<string, string[]>];\n      });\n      return [hlsVersion, new Map(archMetaEntries)] as [string, Map<string, Map<string, string[]>>];\n    });\n    return new Map(hlsMetaEntries);\n  };\n\n  async function readCachedReleaseData(): Promise<ReleaseMetadata | null> {\n    try {\n      logger.info(`Reading cached release data at ${offlineCache}`);\n      const cachedInfo = await promisify(fs.readFile)(offlineCache, { encoding: 'utf-8' });\n      // export type ReleaseMetadata = Map<string, Map<string, Map<string, string[]>>>;\n      const value: any = JSON.parse(cachedInfo);\n      return objectToMetadata(value);\n    } catch (err: any) {\n      // If file doesn't exist, return null, otherwise consider it a failure\n      if (err.code === 'ENOENT') {\n        logger.warn(`No cached release data found at ${offlineCache}`);\n        return null;\n      }\n      throw err;\n    }\n  }\n\n  try {\n    const releaseInfo = await httpsGetSilently(opts);\n    const releaseInfoParsed = JSON.parse(releaseInfo);\n\n    // Cache the latest successfully fetched release information\n    await promisify(fs.writeFile)(offlineCache, JSON.stringify(releaseInfoParsed), { encoding: 'utf-8' });\n    return objectToMetadata(releaseInfoParsed);\n  } catch (githubError: any) {\n    // Attempt to read from the latest cached file\n    try {\n      const cachedInfoParsed = await readCachedReleaseData();\n\n      window.showWarningMessage(\n        \"Couldn't get the latest haskell-language-server releases from GitHub, used local cache instead: \" +\n          githubError.message,\n      );\n      return cachedInfoParsed;\n    } catch (_fileError) {\n      throw new Error(\"Couldn't get the latest haskell-language-server releases from GitHub: \" + githubError.message);\n    }\n  }\n}\n"
  },
  {
    "path": "src/statusBar.ts",
    "content": "import * as vscode from 'vscode';\nimport * as constants from './commands/constants';\n\nexport class HaskellStatusBar {\n  readonly item: vscode.StatusBarItem;\n  constructor(readonly version?: string) {\n    // Set up the status bar item.\n    this.item = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left);\n  }\n\n  refresh(): void {\n    const version = this.version ?? '<unknown>';\n    this.item.text = `Haskell`;\n\n    this.item.command = constants.OpenLogsCommandName;\n    this.item.tooltip = new vscode.MarkdownString('', true);\n    this.item.tooltip.isTrusted = true;\n    this.item.tooltip.appendMarkdown(\n      `[Extension Info](command:${constants.ShowExtensionVersions} \"Show Extension Version\"): Version ${version}\\n\\n` +\n        `---\\n\\n` +\n        `[$(terminal) Open Logs](command:${constants.OpenLogsCommandName} \"Open the logs of the Server and Extension\")\\n\\n` +\n        `[$(debug-restart) Restart Server](command:${constants.RestartServerCommandName} \"Restart Haskell Language Server\")\\n\\n` +\n        `[$(refresh) Restart Extension](command:${constants.RestartExtensionCommandName} \"Restart vscode-haskell Extension\")\\n\\n`,\n    );\n  }\n\n  show() {\n    this.item.show();\n  }\n\n  hide() {\n    this.item.hide();\n  }\n\n  dispose() {\n    this.item.dispose();\n  }\n}\n"
  },
  {
    "path": "src/utils.ts",
    "content": "import * as child_process from 'child_process';\nimport * as fs from 'fs';\nimport * as https from 'https';\nimport * as os from 'os';\nimport * as process from 'process';\nimport { ProgressLocation, window, workspace, WorkspaceFolder } from 'vscode';\nimport { Logger } from 'vscode-languageclient';\nimport * as which from 'which';\nimport { HlsError } from './errors';\n\n// Used for environment variables later on\nexport type IEnvVars = {\n  [key: string]: string;\n};\n\n/**\n * Callback invoked on process termination.\n */\nexport type ProcessCallback = (\n  error: child_process.ExecFileException | null,\n  stdout: string,\n  stderr: string,\n  resolve: (value: string | PromiseLike<string>) => void,\n  reject: (reason?: HlsError | Error | string) => void,\n) => void;\n\n/**\n * Call a process asynchronously.\n * While doing so, update the windows with progress information.\n * If you need to run a process, consider preferring this over running\n * the command directly.\n *\n * @param binary Name of the binary to invoke.\n * @param args Arguments passed directly to the binary.\n * @param dir Directory in which the process shall be executed.\n * @param logger Logger for progress updates.\n * @param title Title of the action, shown to users if available.\n * @param cancellable Can the user cancel this process invocation?\n * @param envAdd Extra environment variables for this process only.\n * @param callback Upon process termination, execute this callback. If given, must resolve promise. On error, stderr and stdout are logged regardless of whether the callback has been specified.\n * @returns Stdout of the process invocation, trimmed off newlines, or whatever the `callback` resolved to.\n */\nexport function callAsync(\n  binary: string,\n  args: string[],\n  logger: Logger,\n  dir?: string,\n  title?: string,\n  cancellable?: boolean,\n  envAdd?: IEnvVars,\n  callback?: ProcessCallback,\n): Thenable<string> {\n  let newEnv: IEnvVars = resolveServerEnvironmentPATH(\n    workspace.getConfiguration('haskell').get('serverEnvironment') || {},\n  );\n  newEnv = { ...(process.env as IEnvVars), ...newEnv, ...(envAdd || {}) };\n  return window.withProgress(\n    {\n      location: ProgressLocation.Notification,\n      title,\n      cancellable,\n    },\n    async (_, token) => {\n      return new Promise<string>((resolve, reject) => {\n        const command: string = binary + ' ' + args.join(' ');\n        logger.info(`Executing '${command}' in cwd '${dir ? dir : process.cwd()}'`);\n        token.onCancellationRequested(() => {\n          logger.warn(`User canceled the execution of '${command}'`);\n        });\n        // Need to set the encoding to 'utf8' in order to get back a string\n        // We execute the command in a shell for windows, to allow use .cmd or .bat scripts\n        const childProcess = child_process\n          .execFile(\n            process.platform === 'win32' ? `\"${binary}\"` : binary,\n            args,\n            { encoding: 'utf8', cwd: dir, shell: process.platform === 'win32', env: newEnv },\n            (err, stdout, stderr) => {\n              if (err) {\n                logger.error(`Error executing '${command}' with error code ${err.code}`);\n                logger.error(`stderr: ${stderr}`);\n                if (stdout) {\n                  logger.error(`stdout: ${stdout}`);\n                }\n              }\n              if (callback) {\n                callback(err, stdout, stderr, resolve, reject);\n              } else {\n                if (err) {\n                  reject(\n                    Error(`\\`${command}\\` exited with exit code ${err.code}.\n                              Consult the [Extensions Output](https://github.com/haskell/vscode-haskell#investigating-and-reporting-problems)\n                              for details.`),\n                  );\n                } else {\n                  resolve(stdout?.trim());\n                }\n              }\n            },\n          )\n          .on('exit', (code, signal) => {\n            const msg =\n              `Execution of '${command}' terminated with code ${code}` + (signal ? `and signal ${signal}` : '');\n            logger.log(msg);\n          })\n          .on('error', (err) => {\n            if (err) {\n              logger.error(`Error executing '${command}': name = ${err.name}, message = ${err.message}`);\n              reject(err);\n            }\n          });\n        token.onCancellationRequested(() => childProcess.kill());\n      });\n    },\n  );\n}\n\n/**\n * Compare the PVP versions of two strings.\n * Details: https://github.com/haskell/pvp/\n *\n * @param l First version\n * @param r second version\n * @returns `1` if l is newer than r, `0` if they are equal and `-1` otherwise.\n */\nexport function comparePVP(l: string, r: string): number {\n  const al = l.split('.');\n  const ar = r.split('.');\n\n  let eq = 0;\n\n  for (let i = 0; i < Math.max(al.length, ar.length); i++) {\n    const el = parseInt(al[i], 10) || undefined;\n    const er = parseInt(ar[i], 10) || undefined;\n\n    if (el === undefined && er === undefined) {\n      break;\n    } else if (el !== undefined && er === undefined) {\n      eq = 1;\n      break;\n    } else if (el === undefined && er !== undefined) {\n      eq = -1;\n      break;\n    } else if (el !== undefined && er !== undefined && el > er) {\n      eq = 1;\n      break;\n    } else if (el !== undefined && er !== undefined && el < er) {\n      eq = -1;\n      break;\n    }\n  }\n  return eq;\n}\n\n/** When making http requests to github.com, use this header otherwise\n * the server will close the request\n */\nconst userAgentHeader = { 'User-Agent': 'vscode-haskell' };\n\nexport async function httpsGetSilently(options: https.RequestOptions): Promise<string> {\n  const opts: https.RequestOptions = {\n    ...options,\n    headers: {\n      ...(options.headers ?? {}),\n      ...userAgentHeader,\n    },\n  };\n\n  return new Promise((resolve, reject) => {\n    let data = '';\n    https\n      .get(opts, (res) => {\n        if (res.statusCode === 301 || res.statusCode === 302) {\n          if (!res.headers.location) {\n            reject(new Error('301/302 without a location header'));\n            return;\n          }\n          https.get(res.headers.location, (resAfterRedirect) => {\n            resAfterRedirect.on('data', (d) => (data += d));\n            resAfterRedirect.on('error', reject);\n            resAfterRedirect.on('close', () => {\n              resolve(data);\n            });\n          });\n        } else if (!res.statusCode || res.statusCode >= 400) {\n          reject(new Error(`Unexpected status code: ${res.statusCode}`));\n        } else {\n          res.on('data', (d) => (data += d));\n          res.on('error', reject);\n          res.on('close', () => {\n            resolve(data);\n          });\n        }\n      })\n      .on('error', reject);\n  });\n}\n\n/**\n * Checks if the executable is on the PATH\n * @param exe Name of the executable to find. Caller must ensure '.exe' extension is included on windows.\n */\nexport function executableExists(exe: string): boolean {\n  const isWindows = process.platform === 'win32';\n  let newEnv: IEnvVars = resolveServerEnvironmentPATH(\n    workspace.getConfiguration('haskell').get('serverEnvironment') || {},\n  );\n  newEnv = { ...(process.env as IEnvVars), ...newEnv };\n  const cmd: string = isWindows ? 'where' : 'which';\n  const out = child_process.spawnSync(cmd, [exe], { env: newEnv });\n  return out.status === 0 || (which.sync(exe, { nothrow: true, path: newEnv.PATH }) ?? '') !== '';\n}\n\nexport function directoryExists(path: string): boolean {\n  return fs.existsSync(path) && fs.lstatSync(path).isDirectory();\n}\n\nexport function expandHomeDir(path: string): string {\n  if (path.startsWith('~')) {\n    return path.replace('~', os.homedir);\n  }\n  return path;\n}\n\nexport function resolvePathPlaceHolders(path: string, folder?: WorkspaceFolder) {\n  path = path.replace('${HOME}', os.homedir).replace('${home}', os.homedir).replace(/^~/, os.homedir);\n  if (folder) {\n    path = path.replace('${workspaceFolder}', folder.uri.path).replace('${workspaceRoot}', folder.uri.path);\n  }\n  return path;\n}\n\nexport function resolvePATHPlaceHolders(path: string) {\n  return path\n    .replace('${HOME}', os.homedir)\n    .replace('${home}', os.homedir)\n    .replace('$PATH', process.env.PATH ?? '$PATH')\n    .replace('${PATH}', process.env.PATH ?? '${PATH}');\n}\n\n// also honours serverEnvironment.PATH\nexport function addPathToProcessPath(extraPath: string): string {\n  const pathSep = process.platform === 'win32' ? ';' : ':';\n  const serverEnvironment: IEnvVars = workspace.getConfiguration('haskell').get('serverEnvironment') || {};\n  const path: string[] = serverEnvironment.PATH\n    ? serverEnvironment.PATH.split(pathSep).map((p) => resolvePATHPlaceHolders(p))\n    : (process.env.PATH?.split(pathSep) ?? []);\n  path.unshift(extraPath);\n  return path.join(pathSep);\n}\n\nexport function resolveServerEnvironmentPATH(serverEnv: IEnvVars): IEnvVars {\n  const pathSep = process.platform === 'win32' ? ';' : ':';\n  const path: string[] | null = serverEnv.PATH\n    ? serverEnv.PATH.split(pathSep).map((p) => resolvePATHPlaceHolders(p))\n    : null;\n  return {\n    ...serverEnv,\n    ...(path ? { PATH: path.join(pathSep) } : {}),\n  };\n}\n"
  },
  {
    "path": "test/suite/extension.test.ts",
    "content": "// We have the following testing targets:\n// 1. Test if the extension is present\n// 2. Test if the extension can be activated\n// 3. Test if the extension can create the extension log file\n// 4. Test if the extension log contains server output (currently we use this to ensure the server is activated successfully)\n// 5. Test if the server inherit environment variables defined in the settings\n\nimport * as vscode from 'vscode';\nimport * as assert from 'assert';\nimport * as path from 'path';\nimport * as fs from 'fs';\nimport { StopServerCommandName } from '../../src/commands/constants';\n\nconst LOG = 'hls.log';\nconst CACHE = 'cache-test';\nconst BIN = 'bin';\ntype AllowedKeys = typeof LOG | typeof CACHE;\n\nsuite('Extension Test Suite', () => {\n  const extension: vscode.Extension<unknown> | undefined = vscode.extensions.getExtension('haskell.haskell');\n  const haskellConfig = vscode.workspace.getConfiguration('haskell');\n\n  suiteSetup(async () => {\n    await haskellConfig.update('promptBeforeDownloads', false, vscode.ConfigurationTarget.Global);\n    await haskellConfig.update('manageHLS', 'GHCup');\n    await haskellConfig.update('logFile', LOG);\n    await haskellConfig.update('trace.server', 'messages');\n    await haskellConfig.update('releasesDownloadStoragePath', path.normalize(getWorkspaceFile(BIN).fsPath));\n    await haskellConfig.update('serverEnvironment', {\n      XDG_CACHE_HOME: path.normalize(getWorkspaceFile(CACHE).fsPath),\n    });\n\n    const contents = new TextEncoder().encode('main = putStrLn \"hi vscode tests\"');\n    await vscode.workspace.fs.writeFile(getWorkspaceFile('Main.hs'), contents);\n  });\n\n  test('1. Extension should be present', () => {\n    assert.ok(extension);\n  });\n\n  test('2. Extension can be activated', async () => {\n    assert.ok(await extension?.activate().then(() => true));\n  });\n\n  test('3. Extension should create the extension log file', async () => {\n    // Open the document to trigger the extension\n    vscode.workspace.openTextDocument(getWorkspaceFile('Main.hs'));\n    assert.ok(await runWithIntervalAndTimeout(() => workspaceFileExist(LOG), 1, 60));\n  });\n\n  test('4. Extension log should have server output', async () => {\n    vscode.workspace.openTextDocument(getWorkspaceFile('Main.hs'));\n    const checkServerLog = () => {\n      const logContents = getExtensionLogContent();\n      if (logContents) {\n        return logContents.match(/Registering IDE configuration/i) !== null;\n      }\n      return false;\n    };\n    assert.ok(await runWithIntervalAndTimeout(checkServerLog, 1, 60), 'Extension log file has no expected hls output');\n  });\n\n  test('5. Server should inherit environment variables defined in the settings', async () => {\n    vscode.workspace.openTextDocument(getWorkspaceFile('Main.hs'));\n    assert.ok(\n      await runWithIntervalAndTimeout(() => workspaceFileExist(CACHE), 1, 30),\n      'Server did not inherit XDG_CACHE_DIR from environment variables set in the settings',\n    );\n  });\n\n  suiteTeardown(async () => {\n    console.log('Stopping the lsp server');\n    await vscode.commands.executeCommand(StopServerCommandName);\n\n    console.log('Contents of the extension log:');\n    const logContents = getExtensionLogContent();\n    if (logContents) {\n      console.log(logContents);\n    }\n  });\n});\n\n//////////////////////////\n// Helper functions BEGIN\n//////////////////////////\n\nfunction getWorkspaceRoot(): vscode.WorkspaceFolder {\n  const folders = vscode.workspace.workspaceFolders;\n  if (folders) {\n    return folders[0];\n  } else {\n    throw Error('workspaceFolders is empty');\n  }\n}\n\nfunction getWorkspaceFile(name: string): vscode.Uri {\n  const wsroot = getWorkspaceRoot().uri;\n  return wsroot.with({ path: path.posix.join(wsroot.path, name) });\n}\n\n/**\n * Check if the given file exists in the workspace.\n * @param key The key name\n * @returns `True` if exists, otherwise `False`\n */\nfunction workspaceFileExist(key: AllowedKeys): boolean {\n  const folder = getWorkspaceRoot();\n  const targetPath = path.join(folder.uri.fsPath, key);\n\n  return fs.existsSync(targetPath);\n}\n\n/**\n * Run a function by given interval and timeout.\n * @param fn The function to run, which has the signature `() => boolean`\n * @param interval Interval in seconds\n * @param timeout Interval in seconds\n * @returns `true` if `fn` returns `true` before the `timeout`, otherwise `false`\n */\nasync function runWithIntervalAndTimeout(fn: () => boolean, interval: number, timeout: number): Promise<boolean> {\n  const startTime = Date.now();\n  const intervalMs = interval * 1000;\n  const timeoutMs = timeout * 1000;\n  const endTime = startTime + timeoutMs;\n  const wait = (ms: number) => new Promise((r) => setTimeout(r, ms));\n\n  while (Date.now() <= endTime) {\n    if (fn()) {\n      return true;\n    }\n    await wait(intervalMs);\n  }\n\n  return false;\n}\n\nfunction getExtensionLogContent(): string | undefined {\n  const extLog = getWorkspaceFile(LOG).fsPath;\n  if (fs.existsSync(extLog)) {\n    const logContents = fs.readFileSync(extLog);\n    return logContents.toString();\n  } else {\n    console.log(`${extLog} does not exist!`);\n    return undefined;\n  }\n}\n\n//////////////////////////\n// Helper functions END\n//////////////////////////\n"
  },
  {
    "path": "test/suite/index.ts",
    "content": "import * as glob from 'glob';\nimport * as Mocha from 'mocha';\nimport * as path from 'path';\n\nexport async function run(): Promise<void> {\n  // Create the mocha test\n  const mocha = new Mocha({\n    ui: 'tdd',\n    timeout: 210_000, // 3.5 mins\n    color: true,\n  });\n\n  const testsRoot = path.resolve(__dirname, '..');\n\n  return new Promise((c, e) => {\n    glob\n      .glob('**/**.test.js', { cwd: testsRoot })\n      .then((files) => {\n        // Add files to the test suite\n        files.forEach((f) => mocha.addFile(path.resolve(testsRoot, f)));\n\n        try {\n          // Run the mocha test\n          mocha.run((failures) => {\n            if (failures > 0) {\n              e(new Error(`${failures} tests failed.`));\n            } else {\n              c();\n            }\n          });\n        } catch (err) {\n          console.error(err);\n          e(err);\n        }\n      })\n      .catch((err) => e(err));\n  });\n}\n"
  },
  {
    "path": "test-workspace/.gitkeep",
    "content": ""
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"module\": \"CommonJS\",\n    \"target\": \"ES2024\",\n    \"outDir\": \"out\",\n    \"lib\": [\"ES2024\"],\n    \"sourceMap\": true,\n    \"rootDir\": \".\",\n    \"noUnusedLocals\": true,\n    \"strict\": true,\n    \"noImplicitAny\": true,\n    \"noImplicitReturns\": true,\n    \"noFallthroughCasesInSwitch\": true,\n    \"strictNullChecks\": true,\n    \"strictBuiltinIteratorReturn\": false\n  },\n  \"include\": [\"./src/**/*.ts\", \"./test/**/*.ts\", \"webpack.config.js\"],\n  \"exclude\": [\"node_modules\", \".vscode\", \".vscode-test\"]\n}\n"
  },
  {
    "path": "webpack.config.js",
    "content": "/*---------------------------------------------------------------------------------------------\n *  Minimal webpack config for VS Code extensions\n *  Uses ES Modules (compatible with Yarn 4, npm 11, and modern Node.js)\n *--------------------------------------------------------------------------------------------*/\n\nimport path from 'path';\nimport { fileURLToPath } from 'url';\nimport ESLintPlugin from 'eslint-webpack-plugin';\n\n// Recreate __dirname for ES Modules (not available in ESM by default)\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\n/** @type {import('webpack').Configuration} */\nexport default {\n  // VS Code extensions run in a Node.js environment, not a browser\n  target: 'node',\n\n  // 'none' mode disables default optimizations (VS Code handles this)\n  mode: 'none',\n\n  // Entry point: where webpack starts bundling your extension\n  entry: './src/extension.ts',\n\n  output: {\n    // Output directory for the bundled extension\n    path: path.resolve(__dirname, 'dist'),\n    // Final bundle filename (must match 'main' in package.json)\n    filename: 'extension.js',\n    // Required format for VS Code extensions (CommonJS)\n    libraryTarget: 'commonjs2'\n  },\n\n  // Generate source maps for debugging (maps bundled code back to original TypeScript)\n  devtool: 'source-map',\n\n  externals: {\n    // 'vscode' module is provided by VS Code at runtime — don't bundle it\n    vscode: 'commonjs vscode'\n    // Add other native modules here if needed (e.g., 'fsevents': 'commonjs fsevents')\n  },\n\n  resolve: {\n    // File extensions webpack will look for (in order)\n    extensions: ['.ts', '.js']\n  },\n\n  module: {\n    rules: [\n      {\n        // Match all TypeScript files\n        test: /\\.ts$/,\n        // Skip node_modules (already compiled)\n        exclude: /node_modules/,\n        use: {\n          loader: 'ts-loader',\n          // Explicitly point to your tsconfig.json (fixes ESM resolution issues)\n          options: { configFile: path.resolve(__dirname, 'tsconfig.json') }\n        }\n      }\n    ]\n  },\n\n  plugins: [\n    // Lint TypeScript files during build\n    new ESLintPlugin({\n      extensions: ['.ts'],\n      exclude: ['node_modules', 'dist']\n    })\n  ],\n\n  // Suppress known harmless warnings from vscode-languageserver-types UMD build\n  ignoreWarnings: [\n    {\n      module: /vscode-languageserver-types/,\n      message: /Critical dependency: require function is used in a way/\n    }\n  ]\n};\n"
  }
]