├── .config └── dotnet-tools.json ├── .devcontainer ├── Dockerfile └── devcontainer.json ├── .envrc ├── .github └── workflows │ ├── publish.yml │ └── testing.yml ├── .gitignore ├── .paket └── Paket.Restore.targets ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── .vscodeignore ├── Icon512.png ├── LICENSE.md ├── Notes └── Tooltips.md ├── NuGet.Config ├── README.md ├── TODO.md ├── Understanding FSAC.md ├── appveyor.yml ├── changelog.md ├── client └── extension.ts ├── fsharp-language-server.sln ├── package.json ├── paket.dependencies ├── paket.lock ├── sample ├── BadProject │ └── BadProject.fsproj ├── CSharpProject.AssemblyName │ ├── CSharpProject.AssemblyName.csproj │ └── Class1.cs ├── CSharpProject │ ├── CSharpProject.csproj │ └── Class1.cs ├── DependsOn │ ├── DependsOn.fsproj │ └── MyLibrary.fs ├── EmptyProject │ └── EmptyProject.fsproj ├── FSharpKoans.Core │ └── FSharpKoans.Core.fsproj ├── HasLocalDll │ ├── HasLocalDll.fsproj │ └── Program.fs ├── HasPackageReference │ ├── HasPackageReference.fsproj │ └── Library.fs ├── HasTests │ ├── HasTests.fsproj │ └── MyTests.fs ├── IndirectDep │ ├── IndirectDep.fsproj │ └── IndirectLibrary.fs ├── Issue28 │ ├── Issue28.fsproj │ └── main.fs ├── MainProject │ ├── BreakParentReference.fs │ ├── BreakParentTarget.fs │ ├── CompleteInString.fs │ ├── Completions.fs │ ├── CreateTypeError.fs │ ├── DeclareSymbol.fs │ ├── Hover.fs │ ├── InterfaceInModule.fs │ ├── MainProject.fsproj │ ├── NotInFsproj.fs │ ├── Reference.fs │ ├── ReferenceDependsOn.fs │ ├── ReferenceIndirectDep.fs │ ├── RenameReference.fs │ ├── RenameTarget.fs │ ├── SignatureHelp.fs │ ├── Target.fs │ ├── UseSymbol.fs │ └── WrongType.fs ├── Net5Console │ ├── Net5Console.fsproj │ └── main.fs ├── Net6Console │ ├── Net6Console.fsproj │ └── main.fs ├── Net6Windows │ ├── Net6Windows.fsproj │ └── Program.fs ├── NetCoreApp3 │ ├── NetCoreApp3.fsproj │ └── main.fs ├── NotBuilt │ ├── NotBuilt.fs │ └── NotBuilt.fsproj ├── ReferenceCSharp.AssemblyName │ ├── Library.fs │ └── ReferenceCSharp.AssemblyName.fsproj ├── ReferenceCSharp │ ├── Library.fs │ └── ReferenceCSharp.fsproj ├── Script │ ├── LoadedByScript.fs │ └── MainScript.fsx ├── Signature │ ├── HasSignature.fs │ ├── HasSignature.fsi │ └── Signature.fsproj ├── SlnReferences │ ├── Common.fs │ ├── Main.fs │ ├── OrphanProject.fsproj │ ├── ReferencedProject.fsproj │ └── SlnReferences.sln └── TemplateParams │ └── TemplateParams.fsproj ├── scripts ├── build.ps1 ├── build.sh ├── debug.sh ├── install.sh ├── paketActions.sh ├── restore.sh └── test.sh ├── shell.nix ├── spacemacs └── fsharp2 │ ├── config.el │ ├── funcs.el │ └── packages.el ├── src ├── FSharpLanguageServer │ ├── Conversions.fs │ ├── DebounceCheck.fs │ ├── FSAC │ │ ├── Keywordlist.fs │ │ ├── Licensing.md │ │ ├── Semantic.fs │ │ └── ToolTips │ │ │ ├── Format.fs │ │ │ ├── ToolTip.fs │ │ │ └── XmlDoc.fs │ ├── FSharpLanguageServer.fsproj │ ├── Goto.fs │ ├── Navigation.fs │ ├── Program.fs │ ├── ProgressBar.fs │ ├── ProjectManager │ │ ├── FileCache.fs │ │ ├── ProjectManager.fs │ │ └── Types.fs │ ├── SourceLink.fs │ ├── SyntaxTreeOps.fs │ ├── TipFormatter.fs │ ├── UnusedDeclarations.fs │ ├── bin │ │ └── Release │ │ │ └── netcoreapp2.0 │ │ │ └── assembly │ │ │ └── README.md │ └── paket.references ├── LSP │ ├── DocumentStore.fs │ ├── LSP.fsproj │ ├── LanguageServer.fs │ ├── Log.fs │ ├── Parser.fs │ ├── Ser.fs │ ├── Tokenizer.fs │ ├── Types │ │ ├── BaseTypes.fs │ │ ├── SemanticToken.fs │ │ └── Types.fs │ ├── Utils.fs │ └── paket.references └── ProjInfo │ ├── Library.fs │ ├── ProjInfo.fsproj │ └── paket.references ├── syntaxes ├── LICENSE.md ├── README.md ├── fsharp.configuration.json ├── fsharp.fsi.json ├── fsharp.fsx.json └── fsharp.json ├── tests ├── Expecto │ ├── Common.fs │ ├── FSharpLanguageServer.Expecto.fsproj │ ├── FsharpLanguageServer │ │ ├── FormattingTests.fs │ │ ├── ProjectManagerTests.fs │ │ └── ServerTests.fs │ ├── LSP │ │ ├── DocumentStoreTests.fs │ │ ├── JsonTests.fs │ │ ├── LanguageServerTests.fs │ │ ├── ParserTests.fs │ │ └── TokenizerTests.fs │ ├── Program.fs │ ├── ProjectCracker │ │ └── Cracker.fs │ └── paket.references ├── ProjectInfo │ ├── Program.cs │ └── ProjectInfo.csproj └── Scratch │ ├── Program.fs │ ├── Scratch.fsproj │ └── paket.references ├── tsconfig.json ├── videos ├── Autocomplete.mov.gif ├── DebugTest.mov.gif ├── DocumentSymbols.mov.gif ├── EmacsLspMode.gif ├── FindReferences.mov.gif ├── GoToDefinition.mov.gif ├── Hover.mov.gif ├── LSP-vs-Ionide-Warm.gif ├── RenameSymbol.mov.gif ├── ShowErrors.mov.gif ├── SignatureHelp.mov.gif ├── VimDeoplete.mov.gif └── WorkspaceSymbols.mov.gif └── yarn.lock /.config/dotnet-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "isRoot": true, 4 | "tools": { 5 | "paket": { 6 | "version": "7.1.5", 7 | "commands": [ 8 | "paket" 9 | ] 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | # See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.205.2/containers/dotnet/.devcontainer/base.Dockerfile 2 | 3 | # [Choice] .NET version: 6.0, 5.0, 3.1, 6.0-bullseye, 5.0-bullseye, 3.1-bullseye, 6.0-focal, 5.0-focal, 3.1-focal 4 | ARG VARIANT="6.0-bullseye-slim" 5 | FROM mcr.microsoft.com/vscode/devcontainers/dotnet:0-${VARIANT} 6 | 7 | # [Choice] Node.js version: none, lts/*, 16, 14, 12, 10 8 | ARG NODE_VERSION="none" 9 | RUN if [ "${NODE_VERSION}" != "none" ]; then su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi 10 | 11 | # [Optional] Uncomment this section to install additional OS packages. 12 | # RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 13 | # && apt-get -y install --no-install-recommends 14 | 15 | # [Optional] Uncomment this line to install global node packages. 16 | # RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g " 2>&1 -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: 2 | // https://github.com/microsoft/vscode-dev-containers/tree/v0.205.2/containers/dotnet 3 | { 4 | "name": "C# (.NET)", 5 | "build": { 6 | "dockerfile": "Dockerfile", 7 | "args": { 8 | // Update 'VARIANT' to pick a .NET Core version: 3.1, 5.0, 6.0 9 | // Append -bullseye or -focal to pin to an OS version. 10 | "VARIANT": "6.0", 11 | // Options 12 | "NODE_VERSION": "lts/*" 13 | } 14 | }, 15 | 16 | // Set *default* container specific settings.json values on container create. 17 | "settings": {}, 18 | 19 | // Add the IDs of extensions you want installed when the container is created. 20 | "extensions": [ 21 | "ms-dotnettools.csharp" 22 | ], 23 | 24 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 25 | // "forwardPorts": [5000, 5001], 26 | 27 | // [Optional] To reuse of your local HTTPS dev cert: 28 | // 29 | // 1. Export it locally using this command: 30 | // * Windows PowerShell: 31 | // dotnet dev-certs https --trust; dotnet dev-certs https -ep "$env:USERPROFILE/.aspnet/https/aspnetapp.pfx" -p "SecurePwdGoesHere" 32 | // * macOS/Linux terminal: 33 | // dotnet dev-certs https --trust; dotnet dev-certs https -ep "${HOME}/.aspnet/https/aspnetapp.pfx" -p "SecurePwdGoesHere" 34 | // 35 | // 2. Uncomment these 'remoteEnv' lines: 36 | // "remoteEnv": { 37 | // "ASPNETCORE_Kestrel__Certificates__Default__Password": "SecurePwdGoesHere", 38 | // "ASPNETCORE_Kestrel__Certificates__Default__Path": "/home/vscode/.aspnet/https/aspnetapp.pfx", 39 | // }, 40 | // 41 | // 3. Do one of the following depending on your scenario: 42 | // * When using GitHub Codespaces and/or Remote - Containers: 43 | // 1. Start the container 44 | // 2. Drag ~/.aspnet/https/aspnetapp.pfx into the root of the file explorer 45 | // 3. Open a terminal in VS Code and run "mkdir -p /home/vscode/.aspnet/https && mv aspnetapp.pfx /home/vscode/.aspnet/https" 46 | // 47 | // * If only using Remote - Containers with a local container, uncomment this line instead: 48 | // "mounts": [ "source=${env:HOME}${env:USERPROFILE}/.aspnet/https,target=/home/vscode/.aspnet/https,type=bind" ], 49 | 50 | // Use 'postCreateCommand' to run commands after the container is created. 51 | // "postCreateCommand": "dotnet restore", 52 | 53 | // Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. 54 | "remoteUser": "vscode" 55 | } 56 | -------------------------------------------------------------------------------- /.envrc: -------------------------------------------------------------------------------- 1 | eval "$(lorri direnv)" 2 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: publish-extension 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | name: 7 | description: 'test' 8 | required: false 9 | default: 'hi' 10 | 11 | jobs: 12 | pubBinaries: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Setupdotnet 18 | uses: actions/setup-dotnet@v1 19 | with: 20 | dotnet-version: 6.0.x 21 | - uses: actions/cache@v2 22 | with: 23 | path: ./paket 24 | key: ${{ runner.os }}-${{ hashFiles('**/paket.lock','**/paket.dependencies') }} 25 | - run: bash ./scripts/paketActions.sh 26 | - name: Restore dependencies 27 | run: dotnet tool restore 28 | - name: paket install 29 | run: dotnet paket install 30 | - name: publish 31 | run: dotnet publish -c Release src/FSharpLanguageServer 32 | - uses: actions/upload-artifact@v2 33 | # Upload the artifact so the MacOS runner do something with it 34 | with: 35 | name: CompiledProject 36 | path: src/FSharpLanguageServer/bin/Release/net6.0/publish 37 | #I think i need to do something wiith these to make the be accessable to the next action(upload artifact i think) 38 | publish: 39 | runs-on: ubuntu-latest 40 | needs: pubBinaries 41 | if: success() 42 | steps: 43 | - uses: actions/checkout@v2 44 | - uses: actions/setup-node@v2 45 | with: 46 | node-version: '14' 47 | 48 | - run: yarn install 49 | - uses: actions/download-artifact@master 50 | with: 51 | name: CompiledProject 52 | path: src/FSharpLanguageServer/bin/Release/net6.0/publish 53 | - run: npx vsce package 54 | - uses: actions/download-artifact@v2 55 | - run: npx vsce publish --packagePath $(find . -iname *.vsix) 56 | env: 57 | VSCE_PAT: ${{ secrets.VSCE_PAT }} 58 | -------------------------------------------------------------------------------- /.github/workflows/testing.yml: -------------------------------------------------------------------------------- 1 | name: testing 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | jobs: 9 | build-test: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v2 14 | - name: Setup .NET 15 | uses: actions/setup-dotnet@v1 16 | with: 17 | dotnet-version: | 18 | 6.0.x 19 | 5.0.x 20 | 3.1.x 21 | - uses: actions/cache@v2 22 | with: 23 | path: ./paket 24 | key: ${{ runner.os }}-${{ hashFiles('**/paket.lock','**/paket.dependencies') }} 25 | - run: bash ./scripts/paketActions.sh 26 | - name: Restore dependencies 27 | run: dotnet tool restore 28 | - name: paket setup 29 | run: dotnet paket install 30 | - name: Restore 31 | run: bash ./scripts/restore.sh 32 | - name: Test 33 | run: dotnet test --verbosity normal ./tests/Expecto/ 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | obj/ 3 | out/ 4 | packages/ 5 | node_modules/ 6 | .idea/ 7 | build.vsix 8 | Scratch.fsx 9 | Scratch.ts 10 | fsharp-language-server.sln.DotSettings.user 11 | .vs 12 | 13 | .fake 14 | .ionide 15 | *.vsix 16 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "name": ".net expecto tests", 10 | "type": "coreclr", 11 | "request": "launch", 12 | "preLaunchTask": "build-expecto", 13 | "program": "${workspaceFolder}/tests/expecto/bin/Debug/net6.0/FSharpLanguageServer.Expecto.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}", 16 | "stopAtEntry": false, 17 | "console": "internalConsole" 18 | }, 19 | { 20 | "name": ".NET Core Attach", 21 | "type": "coreclr", 22 | "request": "attach", 23 | "processId": "${command:pickProcess}", 24 | "requireExactSource": false, 25 | "sourceLinkOptions": { 26 | "*": { 27 | "enabled": true 28 | } 29 | }, 30 | }, 31 | // { 32 | // "name": ".NET Core Attach", 33 | // "type": "coreclr", 34 | // "request": "attach", 35 | // "processId": "${command:pickProcess}" 36 | // }, 37 | { 38 | "name": "Extension", 39 | "type": "extensionHost", 40 | "request": "launch", 41 | "runtimeExecutable": "${execPath}", 42 | "args": [ 43 | "--extensionDevelopmentPath=${workspaceFolder}" 44 | ], 45 | "outFiles": [ 46 | "${workspaceFolder}/out/**/*.js" 47 | ], 48 | "preLaunchTask": "yarn: compile" 49 | }, 50 | { 51 | "name": "Extension Tests", 52 | "type": "extensionHost", 53 | "request": "launch", 54 | "runtimeExecutable": "${execPath}", 55 | "args": [ 56 | "--extensionDevelopmentPath=${workspaceFolder}", 57 | "--extensionTestsPath=${workspaceFolder}/out/test" 58 | ], 59 | "outFiles": [ 60 | "${workspaceFolder}/out/test/**/*.js" 61 | ], 62 | "preLaunchTask": "yarn: watch" 63 | } 64 | ] 65 | } 66 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.exclude": { 3 | ".git/": true, 4 | ".idea/": true, 5 | "node_modules/": true, 6 | "out/": true, 7 | "**/bin/": false, 8 | "**/obj/": true 9 | }, 10 | "files.trimTrailingWhitespace": false, 11 | "nixEnvSelector.suggestion": false, 12 | "nixEnvSelector.nixFile": "${workspaceRoot}/shell.nix", 13 | //"fsharp.useSystemDotnet": true 14 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "build-expecto", 8 | "command": "dotnet", 9 | "type": "shell", 10 | "args": [ 11 | "build", 12 | "./tests/expecto/FSharpLanguageServer.Expecto.fsproj", 13 | 14 | // Ask dotnet build to generate full paths for file names. 15 | "/property:GenerateFullPaths=true", 16 | // Do not generate summary otherwise it leads to duplicate errors in Problems panel 17 | "/consoleloggerparameters:NoSummary" 18 | ], 19 | "group": "test", 20 | "presentation": { 21 | "reveal": "silent" 22 | }, 23 | "problemMatcher": "$msCompile" 24 | } 25 | ] 26 | } -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | 2 | * 3 | ** 4 | 5 | !.vsixmanifest 6 | !node_modules 7 | !out 8 | !Icon512.png 9 | !LICENSE.md 10 | !package.json 11 | !changeog.md 12 | !syntaxes/** 13 | !src/FSharpLanguageServer/bin/Release/net6.0/publish/** 14 | !src/FSharpLanguageServer/bin/Release/net6.0/assembly/README.md -------------------------------------------------------------------------------- /Icon512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsprojects/fsharp-language-server/471439c562c484f2fe57adfd54485e7c1ee4ca29/Icon512.png -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) [F# Language Server Contributors](https://github.com/fsprojects/fsharp-language-server/graphs/contributors) 4 | All rights reserved. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | ----- 25 | 26 | The contents of the spacemacs directory are derived from the fsharp layer in [Spacemacs](https://github.com/syl20bnr/spacemacs), and are licensed under the GPLv3. 27 | -------------------------------------------------------------------------------- /Notes/Tooltips.md: -------------------------------------------------------------------------------- 1 | # Some Notes on tooltips 2 | ## Problems 3 | ### The tooltip doesn't have highlight markdown correctly. 4 | - Sometimes becuase of the way the xml is parsed and converted to markdown for display there are edge cases where markdown strings can end up being malformed and you might get say a big section in bold or some such. -------------------------------------------------------------------------------- /NuGet.Config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # F# Language Server 2 | # IMPORTANT 3 | ##Ending support 4 | This is not supported past .net 6.0. My(@faldor20) complaints with ionide have been resloved and the main fsharp project has plans for making an official language server. I see no reason to continue development on this. 5 | ##Vscode Extension 6 | Untill I can sort out access to publishing new versions of the original extension, this is the working version of this extension [F# Language Server updated](https://marketplace.visualstudio.com/items?itemName=faldor20.fsharp-language-server-updated) 7 | ## Recent Changes 8 | Now being actively maintained by @faldor20 9 | 10 | For changes see the [Changelog](./changelog.md) 11 | 12 | Key features i have added: 13 | Updated to support fcs 41 and .NET 6.0. 14 | 15 | I have used code from [FSharpAutoComplete](https://github.com/fsharp/FsAutoComplete) and some adjustments to the original to add some features: 16 | 17 | - Better hover docs 18 | - Working documentation for system types 19 | - Semantic tokenization 20 | - Support semantic tokens 21 | - Have pretty multiline function signatures in hover docs 22 | 23 | 24 | I may at some point work on supporting the vim and emacs versions of these extensions but as i use neither, I have not at this time. 25 | 26 | My work here is done specifically in response to this issue with Ionide [here](https://github.com/fsharp/FsAutoComplete/issues/805), which makes it unusuable for me and potentially many others. 27 | 28 | Almost all credit for this should go to @georgewfraser(original author) and the guys of at FSAC(where sections of the code and much inspiration comes from) 29 | 30 | ## Main description 31 | 32 | This project is an implementation of the [language server protocol](https://microsoft.github.io/language-server-protocol/) using the [F# Compiler Service](https://fsharp.github.io/FSharp.Compiler.Service/). 33 | 34 | ![https://ci.appveyor.com/api/projects/status/github/fsprojects/fsharp-language-server?svg=true](https://ci.appveyor.com/api/projects/status/github/fsprojects/fsharp-language-server?svg=true) 35 | 36 | ## Features 37 | 38 | ### Hover 39 | ![Hover](videos/Hover.mov.gif) 40 | 41 | ### Autocomplete 42 | ![Autocomplete](videos/Autocomplete.mov.gif) 43 | 44 | ### Method signature help 45 | ![Signature help](videos/SignatureHelp.mov.gif) 46 | 47 | ### Find symbols in document 48 | ![Document symbols](videos/DocumentSymbols.mov.gif) 49 | 50 | ### Find symbols in workspace 51 | ![Workspace symbols](videos/WorkspaceSymbols.mov.gif) 52 | 53 | ### Go-to-definition 54 | ![Go to definition](videos/GoToDefinition.mov.gif) 55 | 56 | ### Find references 57 | ![Find references](videos/FindReferences.mov.gif) 58 | 59 | ### Rename symbol 60 | ![Rename symbol](videos/RenameSymbol.mov.gif) 61 | 62 | ### Show errors on save 63 | ![Show errors](videos/ShowErrors.mov.gif) 64 | 65 | ### Run & Debug tests 66 | ![Debug test](videos/DebugTest.mov.gif) 67 | 68 | ## Code structure 69 | 70 | The language server protocol (LSP) is very similar to the API defined by the F# compiler service (FCS); most of the implementation is devoted to translating between the types used by FCS and the JSON representation of LSP. 71 | 72 | - client/extension.ts: Client-side VSCode launcher 73 | - sample: Example projects used by tests 74 | - scripts: Scripts for building and testing 75 | - src/LSP: Server-side implementation of [language server protocol](https://microsoft.github.io/language-server-protocol/specification) 76 | - src/ProjectCracker: Figures out [F# compiler options](https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/compiler-options) using [Buildalyzer](https://github.com/daveaglick/Buildalyzer) and the MSBuild API. 77 | - src/FSharpLanguageServer: F# language server 78 | - tests/LSP.Tests 79 | - tests/ProjectCracker.Tests 80 | - tests/FSharpLanguageServer.Tests 81 | - videos: Animated GIFs on this page 82 | - 83 | ## Installation 84 | 85 | ### VSCode 86 | 87 | [Install from the VSCode extension marketplace](https://marketplace.visualstudio.com/items?itemName=faldor20.fsharp-language-server-updated) 88 | 89 | ### Neovim and Vim 90 | 91 | Clone this repo to your system and build it: 92 | 93 | ``` 94 | yarn install 95 | dotnet build -c Release 96 | ``` 97 | 98 | If using a distribution based on Arch Linux, you can also install it from the [AUR](https://aur.archlinux.org/packages/fsharp-language-server/) 99 | (Still installs the old version) 100 | Install [LanguageClient-neovim](https://github.com/autozimu/LanguageClient-neovim) 101 | 102 | Update your vim config to point LanguageClient-neovim to the FSharp Language Server for fsharp filetypes: 103 | ``` 104 | let g:LanguageClient_serverCommands = { 105 | \ 'fsharp': ['dotnet', '/Users/name/code/fsharp-language-server/src/FSharpLanguageServer/bin/Release/netcoreapp3.0/target/FSharpLanguageServer.dll'] 106 | \ } 107 | ``` 108 | Open an fsharp file, move the cursor, and call functions. Mappings are up to you: 109 | * Hover `call LanguageClient#textDocument_hover()` 110 | * Rename: `call LanguageClient#textDocument_rename()` 111 | * Definition: `call LanguageClient#textDocument_definition()` 112 | * etc... 113 | 114 | Neovim with Deoplete completion:\ 115 | ![VimDeoplete](videos/VimDeoplete.mov.gif) 116 | 117 | (alternatively there is another vim language server plugin [vim-lsp](https://github.com/prabirshrestha/vim-lsp) but this one hasn't been tried. 118 | 119 | ### Emacs 120 | 121 | #### Spacemacs 122 | 123 | Clone this repo to your system and build it: 124 | 125 | ``` 126 | yarn install 127 | 128 | # Pick the appropriate target based upon your OS 129 | dotnet publish -c Release 130 | dotnet publish -c Release 131 | dotnet publish -c Release 132 | ``` 133 | 134 | Make sure that the FSharpLanguageServer (in `src/FSharpLanguageServer/bin/Release/net6.0/publish`) is in your PATH. Alternatively, you can set the path to the server executable manually within your .spacemacs user-config: 135 | 136 | ``` 137 | (setq fsharp2-lsp-executable "/path/to/FSharpLanguageServer") 138 | ``` 139 | 140 | Since the stock fsharp layer does not currently include LSP support, you will need to use the fsharp2 layer (a fork of fsharp) which does. To use fsharp2, copy the custom layer into your Spacemacs private layers directory. In order for this layer to work, you must be on the Spacemacs **develop** branch, since the LSP layer is not yet available in Spacemacs master. 141 | 142 | ``` 143 | cp -r spacemacs/fsharp2 ~/.emacs.d/private 144 | ``` 145 | 146 | Finally, make sure that you have these layers enabled in your dotspacemacs-configuration-layers. You will need to remove the fsharp layer if you have it, since fsharp2 conflicts with it. 147 | 148 | - lsp 149 | - fsharp2 150 | - syntax-checking 151 | - auto-completion 152 | 153 | ![EmacsLspMode](videos/EmacsLspMode.gif) 154 | 155 | ## How is this project different than [Ionide](https://github.com/ionide)? 156 | Ionide is a suite of F# plugins for VSCode; F# language server is analagous to the [FSAC](https://github.com/fsharp/FsAutoComplete) component. 157 | 158 | The implementation is a thin wrapper around [F# Compiler Service](https://fsharp.github.io/FSharp.Compiler.Service/) and is heavily focused on performance. For example, autocompleting in medium-sized file in F# Language Server (left) and Ionide (right): 159 | 160 | ![Autocomplete warm](videos/LSP-vs-Ionide-Warm.gif) 161 | 162 | # Contributing 163 | 164 | Please do! 165 | 166 | Any help is very much appreciated, issues, PR's, even just asking a question about how something works. I'm happy to help and be helped. 167 | 168 | ## Building 169 | 170 | Run : 171 | 172 | - ``yarn install`` to setup node deps (not needed unless you plan to build the vsix extension package) 173 | 174 | - ``dotnet tool restore`` to install paket 175 | 176 | - ``dotnet paket install`` to install all dependencies 177 | 178 | Then refer to the build scripts. 179 | 180 | Essentially you just publish the language server with ``dotnet publish -c Release src/FSharpLanguageServer`` then run ``vsce package -o build.vsix`` to package it up 181 | 182 | If you want to try your newly created extension run ``code --install-extension build.vsix`` 183 | 184 | ## Debugging 185 | Set the Fsharp.debug and fsharp.CustomDll path settings in vscode. 186 | fsharp.debug: Stops execution of the langserver until you attach the vscode debugger to the dotnet instance. 187 | fsharp.customDllPath: allows you to specify a dll to run instead of the packaged version of fslsp. 188 | #### Live project: 189 | - Open two instances of vscode one in a testing project, one in the fsharp-language-server project 190 | - Make changes to the test project and then run ``dotnet publish`` in src\FSharpLanguageServer. 191 | - use the workspace settings in the test project to set fsharp.debug to true and fsharp.customDllPath to the path of the dll you just published 192 | - Reload the other instance of vscode and attach the debugger to monitor the changes. 193 | 194 | #### Tests: 195 | - Write a test for your problem 196 | - Change "test" to "ftest" 197 | - In the vscode debugger dropdown select ".net expecto tests" 198 | - Debug your test 199 | 200 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # Highlighting 2 | - UnionConstructor { ... } is highlighted like seq { ... } 3 | - ``some-name`` is highlighted wrong at - 4 | - ``name`` is highlighted like a function name regardless of context 5 | - "string format %d" doesn't format %d 6 | - @"\w" doesn't highlight regexes 7 | - Missing keywords: 8 | - to 9 | - downto 10 | - type 11 | - not 12 | - done 13 | 14 | # Cleanup 15 | 16 | # Bugs 17 | - Autocompleting in strings and comments 18 | - Crack FCS 19 | - Reload options when .fsx is saved 20 | - Invalidate check results when referenced .dlls are modified 21 | - Projects targeting netstandard2.0 show fake errors 22 | - Save upstream file not triggering re-lint 23 | - Unused-open is sometimes wrong??? See ProgressBar.fs 24 | - Concurrency errors; use a single thread for everything except FSharpCompilerService ops 25 | - Set --framework in test command 26 | 27 | # Optimizations 28 | - Add analyze-incrementally operation to F# compiler 29 | 30 | # Features 31 | - Allow emitting obj/FscArgs.txt as a project-cracker backup 32 | - fsharp.task.test problem-matchers 33 | - Emit .trx files from tests, and use them to highlight failed tests 34 | - Show "you need to press play" popup the first time the user debugs something 35 | - Popup to do restore, like C# 36 | - Prompt the user to build projects when no obj/ directory is present -------------------------------------------------------------------------------- /Understanding FSAC.md: -------------------------------------------------------------------------------- 1 | Goto definiton: 2 | Tries to find the assembly and filename using getDelaration 3 | if it is within the same project all good return location and go there 4 | if it is in a refernced libararry use the assembly and try to get the sourceLink file -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | 2 | image: 3 | - Visual Studio 2019 4 | - Ubuntu 5 | before_build: 6 | # Display .NET Core version 7 | - dotnet --list-sdks 8 | - dotnet --version 9 | # Restore deps 10 | - dotnet tool restore 11 | - dotnet restore 12 | build_script: 13 | - dotnet build 14 | before_test: 15 | - bash scripts/restore.sh 16 | test_script: 17 | - dotnet test tests/Expecto 18 | -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | ## 0.1.82 2 | 3 | ### Issues fixed 4 | - Hovering not working in various scenarios 5 | - All but the last char of class members 6 | - Keywords 7 | - DU's where the name doesn't have a space between it and the pipe. eg: '|MyDu of string' 8 | - Renaming and goto definition not working when the cursor is next to the end of the word. 9 | - Renaming not working for DU's when the name touches the pipe 10 | - Not rebuilding the cache when fsproj has changed but assets.json has not 11 | ### Internal 12 | - Switched to yarn 13 | 14 | ## 0.1.81 15 | - Added keywords to autocomplete 16 | - fixed a few bugs in the path normalization and caching 17 | - Added keywords to autocomplete 18 | - fixed a few bugs in the path normalization and caching 19 | ## 0.1.80 20 | - Using Ionide.projInfo for project cracking which should fix all unessicary rechecks due to building the project 21 | - Added a project cracking caching feature so that projects now load way faster after being opened ap least once. 22 | It's a small list but this update was a pretty big undertaking and has made huge strides in project opening speed. 23 | ## 0.1.70 24 | ### User facing 25 | - Fixed a problem that meant it was never possible to use recent typechecks. This should massively improve autocomplete and hover speed consistency and reduce rechecking of files. 26 | - Fixed a problem with summary being duplicated in hover docs that was introduced in 0.1.6 27 | - Added support for net-windows, net-macos etc versions of the sdk 28 | ### Internal 29 | - Total overhaul of testing, now using expecto, debugging is very easy, CI is working. 30 | - Moved buildalyzer location yet again. Now it is inside /obj 31 | - fixed an occasional bug that would cause some tests to fail because of running in parallel 32 | 33 | ## 0.1.60 34 | - Added support for any text inside a /// comment appearing in hover tooltips. 35 | - Fixed bug that inserted annoying ** into empty tooltips 36 | - Renamed Buildalyzer artifacts location 37 | ## 0.1.51 38 | - New and improved logging 39 | - MangleMaxine added improved grammars 40 | - Fixed Buildalyzer deleting build artifacts 41 | - Added paket 42 | ## 0.1.5 43 | 44 | Improved signature help and hover for methods in classes. Both now include parameter information and possible exceptions 45 | ## 0.1.41 46 | Fixed bug with finding dotnet executable on windows 47 | ## 0.1.40 48 | Switched from using binaries to publishing a netcore dependant dll. 49 | Massively reduces extension size and also reduces problems with running binaries on strange operating systems or not having certain dependencies 50 | 51 | 52 | ## 0.1.32 53 | fixed a few minor tooltip issues, including issue #1 54 | trying out publishing from linux to fix permissions problems 55 | 56 | 57 | ## 0.1.31 58 | Just little maintenance changes to readmes and icons and stuff to differentiate form fsharp language server 59 | -------------------------------------------------------------------------------- /client/extension.ts: -------------------------------------------------------------------------------- 1 | /* -------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | * ------------------------------------------------------------------------------------------ */ 5 | 'use strict'; 6 | 7 | import * as path from 'path'; 8 | import * as fs from "fs"; 9 | import * as cp from 'child_process'; 10 | import { window, workspace, ExtensionContext, Range, commands, tasks, Task, TaskExecution, ShellExecution, Uri, TaskDefinition, debug } from 'vscode'; 11 | // import { NotificationType } from 'vscode-languageclient'; 12 | import { 13 | LanguageClient, 14 | LanguageClientOptions, 15 | ServerOptions, 16 | TransportKind 17 | } from 'vscode-languageclient/node'; 18 | //import { env } from 'process'; 19 | // Run using `dotnet` instead of self-contained executable 20 | let client: LanguageClient; 21 | 22 | export function activate(context: ExtensionContext) { 23 | //let FSLangServerFolder = Uri.joinPath(workspace.workspaceFolders[0].uri, ('src/FSharpLanguageServer')); 24 | const debugMode = workspace.getConfiguration().get("fsharp.debug.enable", false); 25 | 26 | const customCommand: string = workspace.getConfiguration().get("fsharp.customCommand", null); 27 | 28 | const customCommandArgs: string[] = workspace.getConfiguration().get("fsharp.customCommandArgs", null); 29 | const customDllPath: string = workspace.getConfiguration().get("fsharp.customDllPath", null); 30 | let customDllArgs = null 31 | 32 | if (customDllPath != null && customDllPath != "") customDllArgs = [customDllPath]; 33 | 34 | let args: string[] = customCommandArgs ?? (customDllArgs ?? [binName()]) 35 | if (debugMode) { 36 | args.push("--attach-debugger") 37 | } 38 | //This always needs to be just a single command with no args. If not it will cause an error. 39 | let serverMain = customCommand ?? findInPath('dotnet') ?? 'dotnet'; 40 | 41 | // The server is packaged as a standalone command 42 | 43 | 44 | console.log("Going to start server with command ", serverMain, args); 45 | 46 | // If the extension is launched in debug mode then the debug server options are used 47 | // Otherwise the run options are used 48 | let serverOptions: ServerOptions = { 49 | command: serverMain, 50 | args: args, 51 | transport: TransportKind.stdio, 52 | options: { 53 | cwd: context.extensionPath, 54 | env: { 55 | ...process.env, 56 | } 57 | 58 | } 59 | } 60 | 61 | // Options to control the language client 62 | let clientOptions: LanguageClientOptions = { 63 | // Register the server for F# documents 64 | documentSelector: [{ scheme: 'file', language: 'fsharp' }], 65 | synchronize: { 66 | // Synchronize the setting section 'languageServerExample' to the server 67 | configurationSection: 'fsharp', 68 | // Notify the server about file changes to F# project files contain in the workspace 69 | // TODO: is there a way to configure this via the language server protocol? 70 | fileEvents: [ 71 | workspace.createFileSystemWatcher('**/*.fsproj'), 72 | workspace.createFileSystemWatcher('**/*.fsx'), 73 | workspace.createFileSystemWatcher('**/project.assets.json') 74 | ] 75 | } 76 | } 77 | 78 | // Create the language client and start the client. 79 | client = new LanguageClient('fsharp', 'F# Language Server', serverOptions, clientOptions); 80 | client.start(); 81 | 82 | // Push the disposable to the context's subscriptions so that the 83 | // client can be deactivated on extension deactivation 84 | 85 | // When the language client activates, register a progress-listener 86 | 87 | // Register test-runner 88 | commands.registerCommand('fsharp.command.test.run', runTest); 89 | commands.registerCommand('fsharp.command.test.debug', debugTest); 90 | commands.registerCommand('fsharp.command.goto', goto); 91 | } 92 | 93 | export function deactivate(): Thenable | undefined { 94 | if (!client) { 95 | return undefined; 96 | } 97 | return client.stop(); 98 | } 99 | 100 | function goto(file: string, startLine: number, startColumn: number, _endLine: number, _endColumn: number) { 101 | let selection = new Range(startLine, startColumn, startLine, startColumn); 102 | workspace.openTextDocument(file).then(doc => window.showTextDocument(doc, { selection })); 103 | } 104 | 105 | interface FSharpTestTask extends TaskDefinition { 106 | projectPath: string 107 | fullyQualifiedName: string 108 | } 109 | 110 | function runTest(projectPath: string, fullyQualifiedName: string): Thenable { 111 | let args = ['test', projectPath, '--filter', `FullyQualifiedName=${fullyQualifiedName}`] 112 | let kind: FSharpTestTask = { 113 | type: 'fsharp.task.test', 114 | projectPath: projectPath, 115 | fullyQualifiedName: fullyQualifiedName 116 | } 117 | let shell = new ShellExecution('dotnet', args) 118 | let workspaceFolder = workspace.getWorkspaceFolder(Uri.file(projectPath)) 119 | let task = new Task(kind, workspaceFolder, 'F# Test', 'F# Language Server', shell) 120 | return tasks.executeTask(task) 121 | } 122 | 123 | const outputChannel = window.createOutputChannel('F# Debug Tests'); 124 | 125 | function debugTest(projectPath: string, fullyQualifiedName: string): Promise { 126 | return new Promise((resolve, _reject) => { 127 | // TODO replace this with the tasks API once stdout is available 128 | // https://code.visualstudio.com/docs/extensionAPI/vscode-api#_tasks 129 | // https://github.com/Microsoft/vscode/issues/45980 130 | let cmd = 'dotnet' 131 | let args = ['test', projectPath, '--filter', `FullyQualifiedName=${fullyQualifiedName}`] 132 | let child = cp.spawn(cmd, args, { 133 | env: { 134 | ...process.env, 135 | 'VSTEST_HOST_DEBUG': '1' 136 | } 137 | }) 138 | 139 | outputChannel.clear() 140 | outputChannel.show() 141 | outputChannel.appendLine(`${cmd} ${args.join(' ')}...`) 142 | 143 | var isWaitingForDebugger = false 144 | function onStdoutLine(line: string) { 145 | if (line.trim() == 'Waiting for debugger attach...') { 146 | isWaitingForDebugger = true 147 | } 148 | if (isWaitingForDebugger) { 149 | let pattern = /^Process Id: (\d+)/ 150 | let match = line.match(pattern) 151 | if (match) { 152 | let pid = Number.parseInt(match[1]) 153 | let workspaceFolder = workspace.getWorkspaceFolder(Uri.file(projectPath)) 154 | let config = { 155 | "name": "F# Test", 156 | "type": "coreclr", 157 | "request": "attach", 158 | "processId": pid 159 | } 160 | outputChannel.appendLine(`Attaching debugger to process ${pid}...`) 161 | debug.startDebugging(workspaceFolder, config) 162 | 163 | isWaitingForDebugger = false 164 | } 165 | } 166 | } 167 | 168 | var stdoutBuffer = '' 169 | function onStdoutChunk(chunk: string | Buffer) { 170 | // Append to output channel 171 | let string = chunk.toString() 172 | outputChannel.append(string) 173 | // Send each line to onStdoutLine 174 | stdoutBuffer += string 175 | var newline = stdoutBuffer.indexOf('\n') 176 | while (newline != -1) { 177 | let line = stdoutBuffer.substring(0, newline) 178 | onStdoutLine(line) 179 | stdoutBuffer = stdoutBuffer.substring(newline + 1) 180 | newline = stdoutBuffer.indexOf('\n') 181 | } 182 | } 183 | 184 | child.stdout.on('data', onStdoutChunk); 185 | child.stderr.on('data', chunk => outputChannel.append(chunk.toString())); 186 | child.on('close', (code, _signal) => resolve(code)) 187 | }) 188 | } 189 | 190 | function binName() { 191 | var baseParts = ['src', 'FSharpLanguageServer', 'bin', 'Release', 'net6.0']; 192 | var pathParts = getPathParts(); 193 | var fullParts = baseParts.concat(pathParts); 194 | 195 | return path.join(...fullParts); 196 | } 197 | 198 | 199 | 200 | function getPathParts(): string[] { 201 | /* switch (platform) { 202 | case 'win32': 203 | return ['win10-x64', 'publish', 'FSharpLanguageServer.exe']; 204 | 205 | case 'linux': 206 | return ['linux-x64', 'publish', 'FSharpLanguageServer']; 207 | 208 | case 'darwin': 209 | return ['osx.10.11-x64', 'publish', 'FSharpLanguageServer']; 210 | } 211 | 212 | throw `unsupported platform: ${platform}`; */ 213 | return ['publish', 'FSharpLanguageServer.dll']; 214 | } 215 | 216 | function findInPath(binname: string) { 217 | let pathparts = process.env['PATH'].split(path.delimiter); 218 | for (let i = 0; i < pathparts.length; i++) { 219 | let binpath = path.join(pathparts[i], binname); 220 | if (fs.existsSync(binpath)) { 221 | return binpath; 222 | } 223 | } 224 | return null; 225 | } 226 | -------------------------------------------------------------------------------- /fsharp-language-server.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26124.0 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".paket", ".paket", "{A85B8490-3570-4135-9F62-0C30DF2FB72C}" 7 | ProjectSection(SolutionItems) = preProject 8 | paket.dependencies = paket.dependencies 9 | EndProjectSection 10 | EndProject 11 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{C12C1289-6B1C-44FA-A500-F70B9530EA27}" 12 | EndProject 13 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharpLanguageServer", "src\FSharpLanguageServer\FSharpLanguageServer.fsproj", "{56790B4C-96F9-4A15-99B3-D643EACEEB7B}" 14 | EndProject 15 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "LSP", "src\LSP\LSP.fsproj", "{0D048A71-6AA3-4F50-8E27-FB6E34B4537B}" 16 | EndProject 17 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{6A478F23-67F9-4F5B-9159-589A05EBBFB5}" 18 | EndProject 19 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProjectInfo", "tests\ProjectInfo\ProjectInfo.csproj", "{EB22E80A-4C8C-4B5D-B114-BC0CDA57207F}" 20 | EndProject 21 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharpLanguageServer.Expecto", "tests\Expecto\FSharpLanguageServer.Expecto.fsproj", "{081054CB-6515-47CC-9DA1-7095523F3794}" 22 | EndProject 23 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Scratch", "tests\Scratch\Scratch.fsproj", "{A8BDFA87-1F0A-4EDF-A869-DCB53BD7EC28}" 24 | EndProject 25 | Global 26 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 27 | Debug|Any CPU = Debug|Any CPU 28 | Debug|x64 = Debug|x64 29 | Debug|x86 = Debug|x86 30 | Release|Any CPU = Release|Any CPU 31 | Release|x64 = Release|x64 32 | Release|x86 = Release|x86 33 | EndGlobalSection 34 | GlobalSection(SolutionProperties) = preSolution 35 | HideSolutionNode = FALSE 36 | EndGlobalSection 37 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 38 | {56790B4C-96F9-4A15-99B3-D643EACEEB7B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {56790B4C-96F9-4A15-99B3-D643EACEEB7B}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {56790B4C-96F9-4A15-99B3-D643EACEEB7B}.Debug|x64.ActiveCfg = Debug|x64 41 | {56790B4C-96F9-4A15-99B3-D643EACEEB7B}.Debug|x64.Build.0 = Debug|x64 42 | {56790B4C-96F9-4A15-99B3-D643EACEEB7B}.Debug|x86.ActiveCfg = Debug|x86 43 | {56790B4C-96F9-4A15-99B3-D643EACEEB7B}.Debug|x86.Build.0 = Debug|x86 44 | {56790B4C-96F9-4A15-99B3-D643EACEEB7B}.Release|Any CPU.ActiveCfg = Release|Any CPU 45 | {56790B4C-96F9-4A15-99B3-D643EACEEB7B}.Release|Any CPU.Build.0 = Release|Any CPU 46 | {56790B4C-96F9-4A15-99B3-D643EACEEB7B}.Release|x64.ActiveCfg = Release|x64 47 | {56790B4C-96F9-4A15-99B3-D643EACEEB7B}.Release|x64.Build.0 = Release|x64 48 | {56790B4C-96F9-4A15-99B3-D643EACEEB7B}.Release|x86.ActiveCfg = Release|x86 49 | {56790B4C-96F9-4A15-99B3-D643EACEEB7B}.Release|x86.Build.0 = Release|x86 50 | {0D048A71-6AA3-4F50-8E27-FB6E34B4537B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 51 | {0D048A71-6AA3-4F50-8E27-FB6E34B4537B}.Debug|Any CPU.Build.0 = Debug|Any CPU 52 | {0D048A71-6AA3-4F50-8E27-FB6E34B4537B}.Debug|x64.ActiveCfg = Debug|x64 53 | {0D048A71-6AA3-4F50-8E27-FB6E34B4537B}.Debug|x64.Build.0 = Debug|x64 54 | {0D048A71-6AA3-4F50-8E27-FB6E34B4537B}.Debug|x86.ActiveCfg = Debug|x86 55 | {0D048A71-6AA3-4F50-8E27-FB6E34B4537B}.Debug|x86.Build.0 = Debug|x86 56 | {0D048A71-6AA3-4F50-8E27-FB6E34B4537B}.Release|Any CPU.ActiveCfg = Release|Any CPU 57 | {0D048A71-6AA3-4F50-8E27-FB6E34B4537B}.Release|Any CPU.Build.0 = Release|Any CPU 58 | {0D048A71-6AA3-4F50-8E27-FB6E34B4537B}.Release|x64.ActiveCfg = Release|x64 59 | {0D048A71-6AA3-4F50-8E27-FB6E34B4537B}.Release|x64.Build.0 = Release|x64 60 | {0D048A71-6AA3-4F50-8E27-FB6E34B4537B}.Release|x86.ActiveCfg = Release|x86 61 | {0D048A71-6AA3-4F50-8E27-FB6E34B4537B}.Release|x86.Build.0 = Release|x86 62 | {7C5994F9-641C-4A79-ADB6-6A0412EB885B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 63 | {7C5994F9-641C-4A79-ADB6-6A0412EB885B}.Debug|Any CPU.Build.0 = Debug|Any CPU 64 | {7C5994F9-641C-4A79-ADB6-6A0412EB885B}.Debug|x64.ActiveCfg = Debug|x64 65 | {7C5994F9-641C-4A79-ADB6-6A0412EB885B}.Debug|x64.Build.0 = Debug|x64 66 | {7C5994F9-641C-4A79-ADB6-6A0412EB885B}.Debug|x86.ActiveCfg = Debug|x86 67 | {7C5994F9-641C-4A79-ADB6-6A0412EB885B}.Debug|x86.Build.0 = Debug|x86 68 | {7C5994F9-641C-4A79-ADB6-6A0412EB885B}.Release|Any CPU.ActiveCfg = Release|Any CPU 69 | {7C5994F9-641C-4A79-ADB6-6A0412EB885B}.Release|Any CPU.Build.0 = Release|Any CPU 70 | {7C5994F9-641C-4A79-ADB6-6A0412EB885B}.Release|x64.ActiveCfg = Release|x64 71 | {7C5994F9-641C-4A79-ADB6-6A0412EB885B}.Release|x64.Build.0 = Release|x64 72 | {7C5994F9-641C-4A79-ADB6-6A0412EB885B}.Release|x86.ActiveCfg = Release|x86 73 | {7C5994F9-641C-4A79-ADB6-6A0412EB885B}.Release|x86.Build.0 = Release|x86 74 | {1D0BB852-97FA-4933-8595-A2B579B3768F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 75 | {1D0BB852-97FA-4933-8595-A2B579B3768F}.Debug|Any CPU.Build.0 = Debug|Any CPU 76 | {1D0BB852-97FA-4933-8595-A2B579B3768F}.Debug|x64.ActiveCfg = Debug|x64 77 | {1D0BB852-97FA-4933-8595-A2B579B3768F}.Debug|x64.Build.0 = Debug|x64 78 | {1D0BB852-97FA-4933-8595-A2B579B3768F}.Debug|x86.ActiveCfg = Debug|x86 79 | {1D0BB852-97FA-4933-8595-A2B579B3768F}.Debug|x86.Build.0 = Debug|x86 80 | {1D0BB852-97FA-4933-8595-A2B579B3768F}.Release|Any CPU.ActiveCfg = Release|Any CPU 81 | {1D0BB852-97FA-4933-8595-A2B579B3768F}.Release|Any CPU.Build.0 = Release|Any CPU 82 | {1D0BB852-97FA-4933-8595-A2B579B3768F}.Release|x64.ActiveCfg = Release|x64 83 | {1D0BB852-97FA-4933-8595-A2B579B3768F}.Release|x64.Build.0 = Release|x64 84 | {1D0BB852-97FA-4933-8595-A2B579B3768F}.Release|x86.ActiveCfg = Release|x86 85 | {1D0BB852-97FA-4933-8595-A2B579B3768F}.Release|x86.Build.0 = Release|x86 86 | {12BD685B-EFDB-4991-A9FE-2CBB28944383}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 87 | {12BD685B-EFDB-4991-A9FE-2CBB28944383}.Debug|Any CPU.Build.0 = Debug|Any CPU 88 | {12BD685B-EFDB-4991-A9FE-2CBB28944383}.Debug|x64.ActiveCfg = Debug|x64 89 | {12BD685B-EFDB-4991-A9FE-2CBB28944383}.Debug|x64.Build.0 = Debug|x64 90 | {12BD685B-EFDB-4991-A9FE-2CBB28944383}.Debug|x86.ActiveCfg = Debug|x86 91 | {12BD685B-EFDB-4991-A9FE-2CBB28944383}.Debug|x86.Build.0 = Debug|x86 92 | {12BD685B-EFDB-4991-A9FE-2CBB28944383}.Release|Any CPU.ActiveCfg = Release|Any CPU 93 | {12BD685B-EFDB-4991-A9FE-2CBB28944383}.Release|Any CPU.Build.0 = Release|Any CPU 94 | {12BD685B-EFDB-4991-A9FE-2CBB28944383}.Release|x64.ActiveCfg = Release|x64 95 | {12BD685B-EFDB-4991-A9FE-2CBB28944383}.Release|x64.Build.0 = Release|x64 96 | {12BD685B-EFDB-4991-A9FE-2CBB28944383}.Release|x86.ActiveCfg = Release|x86 97 | {12BD685B-EFDB-4991-A9FE-2CBB28944383}.Release|x86.Build.0 = Release|x86 98 | {EB22E80A-4C8C-4B5D-B114-BC0CDA57207F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 99 | {EB22E80A-4C8C-4B5D-B114-BC0CDA57207F}.Debug|Any CPU.Build.0 = Debug|Any CPU 100 | {EB22E80A-4C8C-4B5D-B114-BC0CDA57207F}.Debug|x64.ActiveCfg = Debug|Any CPU 101 | {EB22E80A-4C8C-4B5D-B114-BC0CDA57207F}.Debug|x64.Build.0 = Debug|Any CPU 102 | {EB22E80A-4C8C-4B5D-B114-BC0CDA57207F}.Debug|x86.ActiveCfg = Debug|Any CPU 103 | {EB22E80A-4C8C-4B5D-B114-BC0CDA57207F}.Debug|x86.Build.0 = Debug|Any CPU 104 | {EB22E80A-4C8C-4B5D-B114-BC0CDA57207F}.Release|Any CPU.ActiveCfg = Release|Any CPU 105 | {EB22E80A-4C8C-4B5D-B114-BC0CDA57207F}.Release|Any CPU.Build.0 = Release|Any CPU 106 | {EB22E80A-4C8C-4B5D-B114-BC0CDA57207F}.Release|x64.ActiveCfg = Release|Any CPU 107 | {EB22E80A-4C8C-4B5D-B114-BC0CDA57207F}.Release|x64.Build.0 = Release|Any CPU 108 | {EB22E80A-4C8C-4B5D-B114-BC0CDA57207F}.Release|x86.ActiveCfg = Release|Any CPU 109 | {EB22E80A-4C8C-4B5D-B114-BC0CDA57207F}.Release|x86.Build.0 = Release|Any CPU 110 | {081054CB-6515-47CC-9DA1-7095523F3794}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 111 | {081054CB-6515-47CC-9DA1-7095523F3794}.Debug|Any CPU.Build.0 = Debug|Any CPU 112 | {081054CB-6515-47CC-9DA1-7095523F3794}.Debug|x64.ActiveCfg = Debug|Any CPU 113 | {081054CB-6515-47CC-9DA1-7095523F3794}.Debug|x64.Build.0 = Debug|Any CPU 114 | {081054CB-6515-47CC-9DA1-7095523F3794}.Debug|x86.ActiveCfg = Debug|Any CPU 115 | {081054CB-6515-47CC-9DA1-7095523F3794}.Debug|x86.Build.0 = Debug|Any CPU 116 | {081054CB-6515-47CC-9DA1-7095523F3794}.Release|Any CPU.ActiveCfg = Release|Any CPU 117 | {081054CB-6515-47CC-9DA1-7095523F3794}.Release|Any CPU.Build.0 = Release|Any CPU 118 | {081054CB-6515-47CC-9DA1-7095523F3794}.Release|x64.ActiveCfg = Release|Any CPU 119 | {081054CB-6515-47CC-9DA1-7095523F3794}.Release|x64.Build.0 = Release|Any CPU 120 | {081054CB-6515-47CC-9DA1-7095523F3794}.Release|x86.ActiveCfg = Release|Any CPU 121 | {081054CB-6515-47CC-9DA1-7095523F3794}.Release|x86.Build.0 = Release|Any CPU 122 | {A8BDFA87-1F0A-4EDF-A869-DCB53BD7EC28}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 123 | {A8BDFA87-1F0A-4EDF-A869-DCB53BD7EC28}.Debug|Any CPU.Build.0 = Debug|Any CPU 124 | {A8BDFA87-1F0A-4EDF-A869-DCB53BD7EC28}.Debug|x64.ActiveCfg = Debug|Any CPU 125 | {A8BDFA87-1F0A-4EDF-A869-DCB53BD7EC28}.Debug|x64.Build.0 = Debug|Any CPU 126 | {A8BDFA87-1F0A-4EDF-A869-DCB53BD7EC28}.Debug|x86.ActiveCfg = Debug|Any CPU 127 | {A8BDFA87-1F0A-4EDF-A869-DCB53BD7EC28}.Debug|x86.Build.0 = Debug|Any CPU 128 | {A8BDFA87-1F0A-4EDF-A869-DCB53BD7EC28}.Release|Any CPU.ActiveCfg = Release|Any CPU 129 | {A8BDFA87-1F0A-4EDF-A869-DCB53BD7EC28}.Release|Any CPU.Build.0 = Release|Any CPU 130 | {A8BDFA87-1F0A-4EDF-A869-DCB53BD7EC28}.Release|x64.ActiveCfg = Release|Any CPU 131 | {A8BDFA87-1F0A-4EDF-A869-DCB53BD7EC28}.Release|x64.Build.0 = Release|Any CPU 132 | {A8BDFA87-1F0A-4EDF-A869-DCB53BD7EC28}.Release|x86.ActiveCfg = Release|Any CPU 133 | {A8BDFA87-1F0A-4EDF-A869-DCB53BD7EC28}.Release|x86.Build.0 = Release|Any CPU 134 | EndGlobalSection 135 | GlobalSection(NestedProjects) = preSolution 136 | {56790B4C-96F9-4A15-99B3-D643EACEEB7B} = {C12C1289-6B1C-44FA-A500-F70B9530EA27} 137 | {0D048A71-6AA3-4F50-8E27-FB6E34B4537B} = {C12C1289-6B1C-44FA-A500-F70B9530EA27} 138 | {EB22E80A-4C8C-4B5D-B114-BC0CDA57207F} = {6A478F23-67F9-4F5B-9159-589A05EBBFB5} 139 | {081054CB-6515-47CC-9DA1-7095523F3794} = {6A478F23-67F9-4F5B-9159-589A05EBBFB5} 140 | {A8BDFA87-1F0A-4EDF-A869-DCB53BD7EC28} = {6A478F23-67F9-4F5B-9159-589A05EBBFB5} 141 | EndGlobalSection 142 | EndGlobal 143 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fsharp-language-server-updated", 3 | "displayName": "F# Language Server updated", 4 | "description": "F# Language Support using FSharp Compiler Services updated to support net6.0 and fcs 41", 5 | "author": "George Fraser and Eli Dowling", 6 | "license": "MIT", 7 | "icon": "Icon512.png", 8 | "version": "0.1.82", 9 | "preview": false, 10 | "publisher": "faldor20", 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/faldor20/fsharp-language-server" 14 | }, 15 | "engines": { 16 | "vscode": "^1.52.0" 17 | }, 18 | "categories": [ 19 | "Programming Languages", 20 | "Linters" 21 | ], 22 | "activationEvents": [ 23 | "onLanguage:fsharp" 24 | ], 25 | "main": "./out/client/extension", 26 | "files": [ 27 | "src/FSharpLanguageServer/bin/Release/net6.0/publish/" 28 | ], 29 | "contributes": { 30 | "languages": [ 31 | { 32 | "id": "fsharp", 33 | "aliases": [ 34 | "F#", 35 | "FSharp", 36 | "fsharp" 37 | ], 38 | "extensions": [ 39 | ".fs", 40 | ".fsx", 41 | ".fsi" 42 | ], 43 | "configuration": "./syntaxes/fsharp.configuration.json" 44 | } 45 | ], 46 | "grammars": [ 47 | { 48 | "language": "fsharp", 49 | "scopeName": "source.fsharp", 50 | "path": "./syntaxes/fsharp.json" 51 | }, 52 | { 53 | "language": "fsharp", 54 | "scopeName": "source.fsharp.fsx", 55 | "path": "./syntaxes/fsharp.fsx.json" 56 | }, 57 | { 58 | "language": "fsharp", 59 | "scopeName": "source.fsharp.fsi", 60 | "path": "./syntaxes/fsharp.fsi.json" 61 | } 62 | ], 63 | "configuration": { 64 | "type": "object", 65 | "title": "FSharp configuration", 66 | "properties": { 67 | "fsharp.trace.server": { 68 | "scope": "window", 69 | "type": "string", 70 | "enum": [ 71 | "off", 72 | "messages", 73 | "verbose" 74 | ], 75 | "default": "off", 76 | "description": "Traces the communication between VSCode and the language server." 77 | }, 78 | "fsharp.debug.enable": { 79 | "scope": "window", 80 | "type": "boolean", 81 | "default": false, 82 | "description": "Sets whether to use add the --attach-debugger arg to the launch ags, allowing you to debug the running langserver" 83 | }, 84 | "fsharp.customCommand": { 85 | "scope": "window", 86 | "type": "string", 87 | "default": null, 88 | "description": "Allows you to set a custom command to run the langserver, good for testing custom version \n This must be just the command to run, eg: 'dotnet' put any args in customCommandArgs" 89 | }, 90 | "fsharp.customCommandArgs": { 91 | "scope": "window", 92 | "type": "array", 93 | "default": null, 94 | "description": "Allows you to set a custom command args to run the langserver, good for testing custom version or passing special arguments" 95 | }, 96 | "fsharp.customDllPath": { 97 | "scope": "window", 98 | "type": "string", 99 | "default": null, 100 | "description": "Allows you to set a custom path for the FsharpLanguageServer.dll \n handy for testing the debug version you just built" 101 | } 102 | } 103 | }, 104 | "taskDefinitions": [ 105 | { 106 | "type": "fsharp.task.test", 107 | "required": [ 108 | "projectPath", 109 | "fullyQualifiedName" 110 | ], 111 | "properties": { 112 | "projectPath": { 113 | "type": "string" 114 | }, 115 | "fullyQualifiedName": { 116 | "type": "string" 117 | } 118 | } 119 | } 120 | ], 121 | "breakpoints": [ 122 | { 123 | "language": "fsharp" 124 | } 125 | ] 126 | }, 127 | "scripts": { 128 | "vscode:prepublish": "yarn run compile", 129 | "compile": "tsc -p ./ && dotnet publish -c Release src/FSharpLanguageServer", 130 | "watch": "tsc -watch -p ./", 131 | "test": "yarn run compile && node ./node_modules/vscode/bin/test" 132 | }, 133 | "extensionDependencies": [ 134 | "ms-dotnettools.csharp" 135 | ], 136 | "dependencies": { 137 | "vscode-languageclient": "^7.0.0" 138 | }, 139 | "devDependencies": { 140 | "tsc": "^2.0.4", 141 | "@types/node": "^16.11.7", 142 | "@types/vscode": "^1.52.0", 143 | "@vscode/test-electron": "^1.6.1", 144 | "typescript": "^4.4.4", 145 | "vsce": "^2.3.0" 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /paket.dependencies: -------------------------------------------------------------------------------- 1 | source https://api.nuget.org/v3/index.json 2 | storage: none 3 | framework: auto-detect 4 | nuget Expecto 9.0.4 5 | nuget FSharp.Compiler.Service 41.0.1 6 | nuget FSharp.Data 4.2.5 7 | nuget FSharp.UMX 8 | nuget HtmlAgilityPack 1.11.39 9 | nuget Ionide.ProjInfo 10 | nuget Ionide.ProjInfo.FCS 11 | nuget Ionide.ProjInfo.ProjectSystem 12 | nuget logary 4.2.1 13 | nuget Microsoft.NET.Sdk.Functions 14 | nuget Microsoft.NET.Test.Sdk 17.0.0 15 | nuget Serilog 2.10.0 16 | nuget Serilog.Sinks.Console 4.0.1 17 | nuget Serilog.Sinks.File 5.0.0 18 | nuget System.IO.FileSystem 4.3.0 19 | nuget System.IO.FileSystem.Primitives 4.3.0 20 | nuget System.Net.NameResolution 4.3.0 21 | nuget System.Security.Principal 4.3.0 22 | nuget System.Threading.ThreadPool 4.3.0 23 | nuget Thoth.Json.Net 8.0.0 24 | nuget YoloDev.Expecto.TestSdk 0.12.13 25 | nuget FSharp.SystemTextJson 26 | 27 | 28 | 29 | nuget Microsoft.Build.Locator 30 | 31 | 32 | # don't expose as a package reference 33 | nuget Microsoft.SourceLink.GitHub copy_local: true 34 | # don't copy runtime assets 35 | nuget Microsoft.Build.Framework copy_local: false 36 | nuget Microsoft.Build.Tasks.Core copy_local: false 37 | nuget Microsoft.Build.Utilities.Core copy_local:false #this needs to be copied or we get errors 38 | nuget Microsoft.Build copy_local: false 39 | 40 | nuget Newtonsoft.Json 13.0.1 -------------------------------------------------------------------------------- /sample/BadProject/BadProject.fsproj: -------------------------------------------------------------------------------- 1 | This is not actually a project file -------------------------------------------------------------------------------- /sample/CSharpProject.AssemblyName/CSharpProject.AssemblyName.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | CSharpProject.AssemblyName.Modified 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /sample/CSharpProject.AssemblyName/Class1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CSharpProject.AssemblyName 4 | { 5 | public class Class1 6 | { 7 | public static String name() { 8 | return "CSharp"; 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /sample/CSharpProject/CSharpProject.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /sample/CSharpProject/Class1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CSharpProject 4 | { 5 | public class Class1 6 | { 7 | public static String name() { 8 | return "CSharp"; 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /sample/DependsOn/DependsOn.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp2.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /sample/DependsOn/MyLibrary.fs: -------------------------------------------------------------------------------- 1 | module MyLibrary 2 | 3 | let myInt: int = 1 -------------------------------------------------------------------------------- /sample/EmptyProject/EmptyProject.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | netstandard2.0 4 | 5 | 6 | -------------------------------------------------------------------------------- /sample/FSharpKoans.Core/FSharpKoans.Core.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | netstandard2.0 4 | Library 5 | FSharpKoans.Core 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /sample/HasLocalDll/HasLocalDll.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp2.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /sample/HasLocalDll/Program.fs: -------------------------------------------------------------------------------- 1 | module HasLocalDll 2 | 3 | [] 4 | let main(argv) = 5 | printf "Hello, %d" IndirectLibrary.myInt 6 | 0 -------------------------------------------------------------------------------- /sample/HasPackageReference/HasPackageReference.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /sample/HasPackageReference/Library.fs: -------------------------------------------------------------------------------- 1 | namespace HasPackageReference 2 | 3 | module Say = 4 | let hello name = 5 | printfn "Hello %s" name 6 | -------------------------------------------------------------------------------- /sample/HasTests/HasTests.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netcoreapp2.0 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /sample/HasTests/MyTests.fs: -------------------------------------------------------------------------------- 1 | module MyTests 2 | 3 | open NUnit.Framework 4 | 5 | [] 6 | let ``my great test``() = 7 | () -------------------------------------------------------------------------------- /sample/IndirectDep/IndirectDep.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp2.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /sample/IndirectDep/IndirectLibrary.fs: -------------------------------------------------------------------------------- 1 | module IndirectLibrary 2 | 3 | let myInt: int = 1 -------------------------------------------------------------------------------- /sample/Issue28/Issue28.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /sample/Issue28/main.fs: -------------------------------------------------------------------------------- 1 | module Lib 2 | 3 | open System.Net 4 | 5 | ServicePointManager.DefaultConnectionLimit <- 20 -------------------------------------------------------------------------------- /sample/MainProject/BreakParentReference.fs: -------------------------------------------------------------------------------- 1 | module BreakParentReference 2 | 3 | let referenceInt: int = BreakParentTarget.publicInt -------------------------------------------------------------------------------- /sample/MainProject/BreakParentTarget.fs: -------------------------------------------------------------------------------- 1 | module BreakParentTarget 2 | 3 | let publicInt = 1 -------------------------------------------------------------------------------- /sample/MainProject/CompleteInString.fs: -------------------------------------------------------------------------------- 1 | module CompleteInString 2 | 3 | let x = "List." -------------------------------------------------------------------------------- /sample/MainProject/Completions.fs: -------------------------------------------------------------------------------- 1 | module Completions 2 | 3 | let private completeListModule() = 4 | List. 5 | 6 | let private completeParens() = 7 | Some("foo") 8 | 9 | let private ``name with space``() = 10 | "" 11 | 12 | let private completeSpace() = 13 | na 14 | let private completeKeyword() = 15 | mat -------------------------------------------------------------------------------- /sample/MainProject/CreateTypeError.fs: -------------------------------------------------------------------------------- 1 | module CreateTypeError 2 | 3 | let private createTypeError () = 4 | let x: int = 1 5 | x -------------------------------------------------------------------------------- /sample/MainProject/DeclareSymbol.fs: -------------------------------------------------------------------------------- 1 | module DeclareSymbol 2 | 3 | let x = 1 -------------------------------------------------------------------------------- /sample/MainProject/Hover.fs: -------------------------------------------------------------------------------- 1 | module Hover 2 | open System.Net 3 | let private myFun(): int = 1 4 | 5 | let private testFun() = 6 | eprintfn "%d" (myFun()) 7 | 8 | module private InternalHover = 9 | let internalFun(): int = 1 10 | 11 | let private testInternalFun() = 12 | eprintfn "%d" (InternalHover.internalFun()) 13 | 14 | let private systemFuncHover=List.fold 15 | 16 | type intFunc= int->int 17 | let multiply a b c = 18 | a*b*c 19 | let aliasedFunc:intFunc = (multiply 1 2) 20 | ///This function has documentation 21 | ///``a``: a thing 22 | ///``b``: b thing 23 | let docedFunction a b= 24 | a+b 25 | let methodTest=Authorization("a") 26 | 27 | type DUHover= 28 | |HoverCase of string 29 | 30 | type HoverTestType(state:int)= 31 | let mutable state= state 32 | ///This should have hover text 33 | member this.newState(newState)= state<-newState 34 | 35 | -------------------------------------------------------------------------------- /sample/MainProject/InterfaceInModule.fs: -------------------------------------------------------------------------------- 1 | module InterfaceInModule 2 | 3 | type IMyInterface = 4 | abstract member MyMethod: unit -> string -------------------------------------------------------------------------------- /sample/MainProject/MainProject.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp2.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /sample/MainProject/NotInFsproj.fs: -------------------------------------------------------------------------------- 1 | module NotInFsproj 2 | 3 | let myInt = 1 -------------------------------------------------------------------------------- /sample/MainProject/Reference.fs: -------------------------------------------------------------------------------- 1 | module Reference 2 | 3 | let targetX: string = Target.x -------------------------------------------------------------------------------- /sample/MainProject/ReferenceDependsOn.fs: -------------------------------------------------------------------------------- 1 | module ReferenceDependsOn 2 | 3 | let x: string = MyLibrary.myInt -------------------------------------------------------------------------------- /sample/MainProject/ReferenceIndirectDep.fs: -------------------------------------------------------------------------------- 1 | module ReferenceIndirectDep 2 | 3 | let x: string = IndirectLibrary.myInt -------------------------------------------------------------------------------- /sample/MainProject/RenameReference.fs: -------------------------------------------------------------------------------- 1 | module RenameReference 2 | 3 | let referenceToRenamedSymbol = RenameTarget.symbolToRename -------------------------------------------------------------------------------- /sample/MainProject/RenameTarget.fs: -------------------------------------------------------------------------------- 1 | module RenameTarget 2 | 3 | let symbolToRename = 1 -------------------------------------------------------------------------------- /sample/MainProject/SignatureHelp.fs: -------------------------------------------------------------------------------- 1 | module SignatureHelp 2 | 3 | let private signatureHelp() = "foo".Substring() -------------------------------------------------------------------------------- /sample/MainProject/Target.fs: -------------------------------------------------------------------------------- 1 | module Target 2 | 3 | let x: int = 1 -------------------------------------------------------------------------------- /sample/MainProject/UseSymbol.fs: -------------------------------------------------------------------------------- 1 | module UseSymbol 2 | 3 | let useX = DeclareSymbol.x -------------------------------------------------------------------------------- /sample/MainProject/WrongType.fs: -------------------------------------------------------------------------------- 1 | module WrongType 2 | 3 | let private wrongType () = 4 | let x: int = "1" 5 | x -------------------------------------------------------------------------------- /sample/Net5Console/Net5Console.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net5.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /sample/Net5Console/main.fs: -------------------------------------------------------------------------------- 1 | open System 2 | 3 | [] 4 | let main _ = 5 | 0 6 | -------------------------------------------------------------------------------- /sample/Net6Console/Net6Console.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net6.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /sample/Net6Console/main.fs: -------------------------------------------------------------------------------- 1 | open System 2 | 3 | [] 4 | let main _ = 5 | 0 6 | -------------------------------------------------------------------------------- /sample/Net6Windows/Net6Windows.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net6.0-windows 6 | Net6Windows 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /sample/Net6Windows/Program.fs: -------------------------------------------------------------------------------- 1 | // For more information see https://aka.ms/fsharp-console-apps 2 | open Elmish.WPF 3 | type Model = 4 | { Count: int 5 | StepSize: int } 6 | 7 | let init () = 8 | { Count = 0 9 | StepSize = 1 } 10 | type Msg = 11 | | Increment 12 | | Decrement 13 | | SetStepSize of int 14 | let update msg m = 15 | match msg with 16 | | Increment -> { m with Count = m.Count + m.StepSize } 17 | | Decrement -> { m with Count = m.Count - m.StepSize } 18 | | SetStepSize x -> { m with StepSize = x } 19 | 20 | let bindings () = 21 | [ 22 | "CounterValue" |> Binding.oneWay (fun m -> m.Count) 23 | "Increment" |> Binding.cmd (fun _ -> Increment) 24 | "Decrement" |> Binding.cmd (fun _ -> Decrement) 25 | "StepSize" |> Binding.twoWay( 26 | (fun m -> float m.StepSize), 27 | (fun newVal _ -> int newVal |> SetStepSize)) 28 | ] 29 | 30 | let main window = 31 | WpfProgram.mkSimple init update bindings 32 | |> WpfProgram.startElmishLoop window -------------------------------------------------------------------------------- /sample/NetCoreApp3/NetCoreApp3.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /sample/NetCoreApp3/main.fs: -------------------------------------------------------------------------------- 1 | open System 2 | 3 | [] 4 | let main _ = 5 | 0 6 | -------------------------------------------------------------------------------- /sample/NotBuilt/NotBuilt.fs: -------------------------------------------------------------------------------- 1 | module NotBuilt 2 | 3 | let x = 1 -------------------------------------------------------------------------------- /sample/NotBuilt/NotBuilt.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp2.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /sample/ReferenceCSharp.AssemblyName/Library.fs: -------------------------------------------------------------------------------- 1 | namespace ReferenceCSharp.AssemblyName 2 | 3 | module Say = 4 | let hello () = 5 | let csharp = CSharpProject.AssemblyName.Class1.name() 6 | printfn "Hello %s" csharp 7 | -------------------------------------------------------------------------------- /sample/ReferenceCSharp.AssemblyName/ReferenceCSharp.AssemblyName.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /sample/ReferenceCSharp/Library.fs: -------------------------------------------------------------------------------- 1 | namespace ReferenceCSharp 2 | 3 | module Say = 4 | let hello () = 5 | let csharp = CSharpProject.Class1.name() 6 | printfn "Hello %s" csharp 7 | -------------------------------------------------------------------------------- /sample/ReferenceCSharp/ReferenceCSharp.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /sample/Script/LoadedByScript.fs: -------------------------------------------------------------------------------- 1 | module LoadedByScript 2 | let x = 1 -------------------------------------------------------------------------------- /sample/Script/MainScript.fsx: -------------------------------------------------------------------------------- 1 | #load "LoadedByScript.fs" 2 | 3 | let _y = LoadedByScript.x + 1 -------------------------------------------------------------------------------- /sample/Signature/HasSignature.fs: -------------------------------------------------------------------------------- 1 | module Foo 2 | 3 | let bar() = "bar" 4 | 5 | module Nested = 6 | let nestedBar() = "nested!" 7 | 8 | type Class() = 9 | member this.overloadedMethod(i: int) = sprintf "%d" i 10 | member this.overloadedMethod(i: string) = i -------------------------------------------------------------------------------- /sample/Signature/HasSignature.fsi: -------------------------------------------------------------------------------- 1 | module Foo 2 | 3 | val bar: unit -> string 4 | 5 | module Nested = 6 | val nestedBar: unit -> string 7 | val missingImplementation: unit -> string 8 | 9 | [] 10 | type Class = 11 | member overloadedMethod: int -> string 12 | member overloadedMethod: string -> string -------------------------------------------------------------------------------- /sample/Signature/Signature.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp2.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /sample/SlnReferences/Common.fs: -------------------------------------------------------------------------------- 1 | module Common 2 | 3 | let referenced = 1 -------------------------------------------------------------------------------- /sample/SlnReferences/Main.fs: -------------------------------------------------------------------------------- 1 | module Main 2 | 3 | let reference = Common.referenced -------------------------------------------------------------------------------- /sample/SlnReferences/OrphanProject.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp2.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /sample/SlnReferences/ReferencedProject.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp2.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /sample/SlnReferences/SlnReferences.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26124.0 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "ReferencedProject", "ReferencedProject.fsproj", "{A8CA8A97-3C42-4BA9-AED1-39C9AB043EB9}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Debug|x64 = Debug|x64 12 | Debug|x86 = Debug|x86 13 | Release|Any CPU = Release|Any CPU 14 | Release|x64 = Release|x64 15 | Release|x86 = Release|x86 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 21 | {A8CA8A97-3C42-4BA9-AED1-39C9AB043EB9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 22 | {A8CA8A97-3C42-4BA9-AED1-39C9AB043EB9}.Debug|Any CPU.Build.0 = Debug|Any CPU 23 | {A8CA8A97-3C42-4BA9-AED1-39C9AB043EB9}.Debug|x64.ActiveCfg = Debug|Any CPU 24 | {A8CA8A97-3C42-4BA9-AED1-39C9AB043EB9}.Debug|x64.Build.0 = Debug|Any CPU 25 | {A8CA8A97-3C42-4BA9-AED1-39C9AB043EB9}.Debug|x86.ActiveCfg = Debug|Any CPU 26 | {A8CA8A97-3C42-4BA9-AED1-39C9AB043EB9}.Debug|x86.Build.0 = Debug|Any CPU 27 | {A8CA8A97-3C42-4BA9-AED1-39C9AB043EB9}.Release|Any CPU.ActiveCfg = Release|Any CPU 28 | {A8CA8A97-3C42-4BA9-AED1-39C9AB043EB9}.Release|Any CPU.Build.0 = Release|Any CPU 29 | {A8CA8A97-3C42-4BA9-AED1-39C9AB043EB9}.Release|x64.ActiveCfg = Release|Any CPU 30 | {A8CA8A97-3C42-4BA9-AED1-39C9AB043EB9}.Release|x64.Build.0 = Release|Any CPU 31 | {A8CA8A97-3C42-4BA9-AED1-39C9AB043EB9}.Release|x86.ActiveCfg = Release|Any CPU 32 | {A8CA8A97-3C42-4BA9-AED1-39C9AB043EB9}.Release|x86.Build.0 = Release|Any CPU 33 | EndGlobalSection 34 | EndGlobal 35 | -------------------------------------------------------------------------------- /sample/TemplateParams/TemplateParams.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net45;netstandard2.0 4 | 5 | 6 | 7 | 8 | $(MSBuildProjectDirectory)\..\..\src 9 | $(TargetFramework)\ 10 | 11 | 12 | 13 | 14 | 15 | Utilities/QueueList.fs 16 | 17 | 18 | ParserAndUntypedAST/pars.fs 19 | 20 | 21 | -------------------------------------------------------------------------------- /scripts/build.ps1: -------------------------------------------------------------------------------- 1 | yarn install 2 | paket install 3 | dotnet restore 4 | dotnet clean 5 | dotnet publish -c Release src/FSharpLanguageServer 6 | #dotnet publish -c Release -r osx.10.11-x64 src/FSharpLanguageServer 7 | #dotnet publish -c Release -r linux-x64 src/FSharpLanguageServer 8 | 9 | # Build vsix 10 | vsce package -o build.vsix 11 | code --install-extension build.vsix -------------------------------------------------------------------------------- /scripts/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Builds the plugin as build.vsix 3 | # You will need dotnet core and vsce to run this script 4 | set -e 5 | 6 | # Needed once 7 | yarn install 8 | 9 | # Build self-contained archives for windows, mac and linux 10 | dotnet clean 11 | dotnet publish -c Release src/FSharpLanguageServer 12 | 13 | # Build vsix 14 | vsce package -o build.vsix 15 | code --install-extension build.vsix 16 | -------------------------------------------------------------------------------- /scripts/debug.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | # Delete build outputs 5 | rm -rf src/*/bin src/*/obj tests/*/bin tests/*/obj node_modules 6 | 7 | # Build js 8 | yarn install 9 | yarn run-script compile 10 | 11 | # Build src/FSharpLanguageServer/bin/Release/netcoreapp3.0/osx.10.11-x64/publish/FSharpLanguageServer 12 | dotnet publish -c Release src/FSharpLanguageServer 13 | echo 'Press F5 to debug the new build of F# language server' 14 | -------------------------------------------------------------------------------- /scripts/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | artifact=build.vsix 6 | package=faldor20.fsharp-language-server 7 | 8 | if [ ! -z `code --list-extensions | grep $package` ]; then 9 | code --uninstall-extension $package 10 | fi 11 | 12 | code --install-extension $artifact 13 | -------------------------------------------------------------------------------- /scripts/paketActions.sh: -------------------------------------------------------------------------------- 1 | #used to make paket use local package cache for github actions so we can cache that folder 2 | sed '2 i storage:local' ./paket.dependencies -------------------------------------------------------------------------------- /scripts/restore.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Restore test projects 3 | 4 | echo 'Restoring sample projects...' 5 | dotnet tool restore 6 | dotnet restore sample/EmptyProject/EmptyProject.fsproj 7 | dotnet restore sample/FSharpKoans.Core/FSharpKoans.Core.fsproj 8 | dotnet restore sample/HasLocalDll/HasLocalDll.fsproj 9 | dotnet restore sample/HasPackageReference/HasPackageReference.fsproj 10 | dotnet restore sample/HasTests/HasTests.fsproj 11 | dotnet restore sample/Issue28/Issue28.fsproj 12 | dotnet restore sample/MainProject/MainProject.fsproj 13 | dotnet restore sample/ReferenceCSharp.AssemblyName/ReferenceCSharp.AssemblyName.fsproj 14 | dotnet restore sample/ReferenceCSharp/ReferenceCSharp.fsproj 15 | dotnet restore sample/Signature/Signature.fsproj 16 | dotnet restore sample/SlnReferences/ReferencedProject.fsproj 17 | dotnet restore sample/TemplateParams/TemplateParams.fsproj 18 | dotnet restore sample/NetCoreApp3/NetCoreApp3.fsproj 19 | dotnet restore sample/Net6Windows/Net6Windows.fsproj 20 | # These need to be built, not restored 21 | dotnet build sample/CSharpProject/CSharpProject.csproj 22 | dotnet build sample/CSharpProject.AssemblyName/CSharpProject.AssemblyName.csproj 23 | -------------------------------------------------------------------------------- /scripts/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Restore test projects and run all tests 3 | 4 | source scripts/restore.sh 5 | 6 | echo 'Running tests...' 7 | set -e 8 | dotnet test tests/Expecto -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import {} }: 2 | 3 | let 4 | buildDotnet = with pkgs.dotnetCorePackages; combinePackages [ 5 | sdk_6_0 6 | #required to make all tests pass 7 | #sdk_5_0 8 | #sdk_3_1 9 | ]; 10 | in 11 | pkgs.mkShell { 12 | buildInputs = [ 13 | pkgs.hello 14 | pkgs.openssl.dev 15 | pkgs.openssl 16 | pkgs.openssl.out 17 | pkgs.pkg-config 18 | # keep this line if you use bash 19 | pkgs.bashInteractive 20 | pkgs.nodejs 21 | pkgs.nodePackages.npm 22 | 23 | ]; 24 | nativeBuildInputs=[ 25 | buildDotnet 26 | pkgs.openssl 27 | pkgs.openssl.dev 28 | pkgs.openssl.out 29 | pkgs.pkg-config 30 | ]; 31 | } 32 | 33 | 34 | -------------------------------------------------------------------------------- /spacemacs/fsharp2/config.el: -------------------------------------------------------------------------------- 1 | ;;; config.el --- fsharp Layer config File for Spacemacs 2 | ;; 3 | ;; Copyright (c) 2012-2018 Sylvain Benner & Contributors 4 | ;; 5 | ;; Author: Chris Marchetti 6 | ;; URL: https://github.com/syl20bnr/spacemacs 7 | ;; 8 | ;; This file is not part of GNU Emacs. 9 | ;; 10 | ;;; License: GPLv3 11 | (spacemacs|define-jump-handlers fsharp-mode) 12 | 13 | (defvar fsharp2-backend 'lsp 14 | "The backend to use for IDE features. Possible values are `fsac' and `lsp'") 15 | 16 | (defvar fsharp2-lsp-executable "FSharpLanguageServer" 17 | "The location of the FSharpLanguageServer executable") 18 | -------------------------------------------------------------------------------- /spacemacs/fsharp2/funcs.el: -------------------------------------------------------------------------------- 1 | ;;; funcs.el --- fsharp2 Layer config File for Spacemacs 2 | ;; 3 | ;; Copyright (c) 2012-2018 Sylvain Benner & Contributors 4 | ;; 5 | ;; Author: Chris Marchetti 6 | ;; URL: https://github.com/syl20bnr/spacemacs 7 | ;; 8 | ;; This file is not part of GNU Emacs. 9 | ;; 10 | ;;; License: GPLv3 11 | 12 | (defun spacemacs//fsharp2-setup-intellisense () 13 | "Conditionally enable fsharp-mode's built-in intellisense" 14 | (pcase fsharp2-backend 15 | (`lsp 16 | (setq fsharp-ac-intellisense-enabled nil)) 17 | (`fsac 18 | (setq fsharp-ac-intellisense-enabled t)))) 19 | 20 | (defun spacemacs//fsharp2-setup-backend () 21 | "Conditionally setup fsharp backend" 22 | (pcase fsharp2-backend 23 | (`lsp 24 | (require 'lsp) 25 | ;; Required to avoid issues with lsp-mode's built-in F# client; even though 26 | ;; we're using our mode instead, lsp-mode can't build the LSP client 27 | ;; without this value defined 28 | (setq lsp-fsharp-server-path "") 29 | (lsp-register-client 30 | (make-lsp-client 31 | :new-connection (lsp-stdio-connection fsharp2-lsp-executable) 32 | :major-modes '(fsharp-mode) 33 | :server-id 'fsharp-lsp 34 | :notification-handlers (ht ("fsharp/startProgress" #'ignore) 35 | ("fsharp/incrementProgress" #'ignore) 36 | ("fsharp/endProgress" #'ignore)) 37 | :priority 1)) 38 | (lsp)))) 39 | 40 | (defun spacemacs//fsharp2-setup-bindings () 41 | "Conditionally setup fsharp bindings" 42 | (pcase fsharp2-backend 43 | (`fsac 44 | (spacemacs/declare-prefix-for-mode 'fsharp-mode "mf" "find") 45 | (spacemacs/declare-prefix-for-mode 'fsharp-mode "ms" "interpreter") 46 | (spacemacs/declare-prefix-for-mode 'fsharp-mode "mx" "executable") 47 | (spacemacs/declare-prefix-for-mode 'fsharp-mode "mc" "compile") 48 | (spacemacs/declare-prefix-for-mode 'fsharp-mode "mg" "goto") 49 | (spacemacs/declare-prefix-for-mode 'fsharp-mode "mh" "hint")))) 50 | 51 | (defun spacemacs//fsharp2-setup-company () 52 | "Conditionally setup company mode" 53 | (pcase fsharp2-backend 54 | (`lsp 55 | (spacemacs|add-company-backends 56 | :backends company-lsp 57 | :modes fsharp-mode 58 | :append-hooks nil 59 | :call-hooks t) 60 | (company-mode)))) 61 | -------------------------------------------------------------------------------- /spacemacs/fsharp2/packages.el: -------------------------------------------------------------------------------- 1 | ;;; packages.el --- F# Layer packages File for Spacemacs 2 | ;; 3 | ;; Copyright (c) 2012-2018 Sylvain Benner & Contributors 4 | ;; 5 | ;; Author: Sylvain Benner 6 | ;; URL: https://github.com/syl20bnr/spacemacs 7 | ;; 8 | ;; This file is not part of GNU Emacs. 9 | ;; 10 | ;;; License: GPLv3 11 | 12 | (setq fsharp2-packages 13 | '( 14 | fsharp-mode 15 | ggtags 16 | company 17 | counsel-gtags 18 | helm-gtags 19 | )) 20 | 21 | (defun fsharp2/init-fsharp-mode () 22 | (use-package fsharp-mode 23 | :defer t 24 | :init 25 | (progn 26 | (setq fsharp-doc-idle-delay .2) 27 | (spacemacs/register-repl 'fsharp-mode 'fsharp-show-subshell "F#") 28 | (spacemacs//fsharp2-setup-intellisense) 29 | (spacemacs/add-to-hook 'fsharp-mode-hook 30 | '(spacemacs//fsharp2-setup-backend)) 31 | :config 32 | (progn 33 | (defun spacemacs/fsharp-load-buffer-file-focus () 34 | "Send the current buffer to REPL and switch to the REPL in 35 | `insert state'." 36 | (interactive) 37 | (fsharp-load-buffer-file) 38 | (switch-to-buffer-other-window inferior-fsharp-buffer-name) 39 | (evil-insert-state)) 40 | 41 | (defun spacemacs/fsharp-eval-phrase-focus () 42 | "Send the current phrase to REPL and switch to the REPL in 43 | `insert state'." 44 | (interactive) 45 | (fsharp-eval-phrase) 46 | (switch-to-buffer-other-window inferior-fsharp-buffer-name) 47 | (evil-insert-state)) 48 | 49 | (defun spacemacs/fsharp-eval-region-focus (start end) 50 | "Send the current phrase to REPL and switch to the REPL in 51 | `insert state'." 52 | (interactive "r") 53 | (fsharp-eval-region start end) 54 | (switch-to-buffer-other-window inferior-fsharp-buffer-name) 55 | (evil-insert-state)) 56 | 57 | (spacemacs//fsharp2-setup-bindings) 58 | (spacemacs/set-leader-keys-for-major-mode 'fsharp-mode 59 | ;; Compile 60 | "cc" 'compile 61 | 62 | "fa" 'fsharp-find-alternate-file 63 | 64 | "ht" 'fsharp-ac/show-tooltip-at-point 65 | 66 | "'" 'fsharp-show-subshell 67 | "sb" 'fsharp-load-buffer-file 68 | "sB" 'spacemacs/fsharp-load-buffer-file-focus 69 | "si" 'fsharp-show-subshell 70 | "sp" 'fsharp-eval-phrase 71 | "sP" 'spacemacs/fsharp-eval-phrase-focus 72 | "sr" 'fsharp-eval-region 73 | "sR" 'spacemacs/fsharp-eval-region-focus 74 | "ss" 'fsharp-show-subshell 75 | 76 | "xf" 'fsharp-run-executable-file))))) 77 | 78 | (defun fsharp2/post-init-ggtags () 79 | (add-hook 'fsharp-mode-local-vars-hook #'spacemacs/ggtags-mode-enable)) 80 | 81 | (defun fsharp2/post-init-counsel-gtags () 82 | (spacemacs/counsel-gtags-define-keys-for-mode 'fsharp-mode)) 83 | 84 | (defun fsharp2/post-init-helm-gtags () 85 | (spacemacs/helm-gtags-define-keys-for-mode 'fsharp-mode)) 86 | 87 | (defun fsharp2/post-init-company () 88 | (spacemacs//fsharp2-setup-company)) 89 | -------------------------------------------------------------------------------- /src/FSharpLanguageServer/DebounceCheck.fs: -------------------------------------------------------------------------------- 1 | namespace FSharpLanguageServer 2 | 3 | open System.IO 4 | open System.Collections.Concurrent 5 | open System.Threading 6 | 7 | type DebounceCheck(check: FileInfo -> Async, delayMs: int) = 8 | let todo = new ConcurrentDictionary() 9 | let mutable cancel = new CancellationTokenSource() 10 | let doCheck(file: FileInfo) = 11 | async { 12 | do! check(file) 13 | todo.TryRemove(file.FullName) |> ignore 14 | } 15 | let doCheckAll() = 16 | async { 17 | for file in todo.Values do 18 | do! doCheck(file) 19 | } 20 | member this.CheckLater(file: FileInfo) = 21 | // Add this file to the todo list 22 | todo.TryAdd(file.FullName, file) |> ignore 23 | // Reset the check-countdown 24 | cancel.Cancel() 25 | cancel <- new CancellationTokenSource() 26 | // Start a new check-countdown 27 | Async.Start(async { 28 | do! Async.Sleep(delayMs) 29 | do! doCheckAll() 30 | }, cancel.Token) 31 | member this.CheckNow() = 32 | cancel.Cancel() 33 | doCheckAll() 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/FSharpLanguageServer/FSAC/Keywordlist.fs: -------------------------------------------------------------------------------- 1 | namespace FsAutoComplete 2 | open LSP.Types 3 | open LSP.BaseTypes 4 | open FSharp.Compiler.Text 5 | open FSharp.Compiler.Tokenization 6 | open FSharp.Compiler.EditorServices 7 | open FSharp.Compiler.Symbols 8 | 9 | module KeywordList = 10 | open FSharp.Data 11 | 12 | let keywordDescriptions = FSharpKeywords.KeywordsWithDescription |> dict 13 | 14 | let keywordTooltips = 15 | keywordDescriptions 16 | |> Seq.map (fun kv -> 17 | let lines = kv.Value.Replace("\r\n", "\n").Split('\n') 18 | 19 | let allLines = 20 | Array.concat [| [| "" |] 21 | lines 22 | [| "" |] |] 23 | 24 | let tip = 25 | ToolTipText [ ToolTipElement.Single( 26 | [| TaggedText.tagText kv.Key |], 27 | FSharpXmlDoc.FromXmlText(FSharp.Compiler.Xml.XmlDoc(allLines, Range.Zero)) 28 | ) ] 29 | 30 | kv.Key, tip) 31 | |> dict 32 | 33 | let hashDirectives = 34 | [ 35 | "r", "References an assembly" 36 | "load", "Reads a source file, compiles it, and runs it." 37 | "I", "Specifies an assembly search path in quotation marks." 38 | "light", "Enables or disables lightweight syntax, for compatibility with other versions of ML" 39 | "if", "Supports conditional compilation" 40 | "else", "Supports conditional compilation" 41 | "endif", "Supports conditional compilation" 42 | "nowarn", "Disables a compiler warning or warnings" 43 | "line", "Indicates the original source code line" 44 | ] 45 | |> dict 46 | 47 | let hashSymbolCompletionItems = 48 | hashDirectives 49 | |> Seq.map (fun kv -> 50 | { defaultCompletionItem with 51 | detail=Some kv.Key 52 | kind = Some CompletionItemKind.Keyword 53 | insertText = Some kv.Key 54 | filterText = Some kv.Key 55 | sortText = Some kv.Key 56 | documentation = Some( {kind=MarkupKind.Markdown;value=kv.Value}) 57 | label = "#" + kv.Key }) 58 | |> Seq.toArray 59 | 60 | let allKeywords: string list = 61 | keywordDescriptions 62 | |> Seq.map ((|KeyValue|) >> fst) 63 | |> Seq.toList 64 | 65 | let keywordCompletionItems = 66 | allKeywords 67 | |> List.mapi (fun id k -> 68 | {defaultCompletionItem with 69 | label = k 70 | detail= Some k 71 | data= JsonValue.Record [|"FullName", JsonValue.String(k)|] 72 | CompletionItem.kind = Some CompletionItemKind.Keyword 73 | documentation =Some ({kind=MarkupKind.Markdown;value=keywordDescriptions[k]}) 74 | sortText = None//Some(sprintf "1000000%d" id) 75 | filterText = Some k 76 | insertText = Some k 77 | }) 78 | 79 | -------------------------------------------------------------------------------- /src/FSharpLanguageServer/FSAC/Licensing.md: -------------------------------------------------------------------------------- 1 | All code in this folder comes from, is a modified version of or directly relates to code from https://github.com/fsharp/FsAutoComplete and is licensed under Apache License, Version 2.0 2 | -------------------------------------------------------------------------------- /src/FSharpLanguageServer/FSAC/Semantic.fs: -------------------------------------------------------------------------------- 1 | module FSharpLanguageServer.SemanticTokenization 2 | open System 3 | open LSP.SemanticToken 4 | open LSP 5 | open FSharp.Compiler.EditorServices 6 | 7 | open FSharp.Compiler.Text 8 | open FSharp.Compiler.CodeAnalysis 9 | open LSP.Types 10 | 11 | // See https://code.visualstudio.com/api/language-extensions/semantic-highlight-guide#semantic-token-scope-map for the built-in scopes 12 | // if new token-type strings are added here, make sure to update the 'legend' in any downstream consumers. 13 | let map (t: SemanticClassificationType) : string = 14 | match t with 15 | | SemanticClassificationType.Operator -> "operator" 16 | | SemanticClassificationType.ReferenceType 17 | | SemanticClassificationType.Type 18 | | SemanticClassificationType.TypeDef 19 | | SemanticClassificationType.ConstructorForReferenceType -> "type" 20 | | SemanticClassificationType.ValueType 21 | | SemanticClassificationType.ConstructorForValueType -> "struct" 22 | | SemanticClassificationType.UnionCase 23 | | SemanticClassificationType.UnionCaseField -> "enumMember" 24 | | SemanticClassificationType.Function 25 | | SemanticClassificationType.Method 26 | | SemanticClassificationType.ExtensionMethod -> "function" 27 | | SemanticClassificationType.Property -> "property" 28 | | SemanticClassificationType.MutableVar 29 | | SemanticClassificationType.MutableRecordField -> "mutable" 30 | | SemanticClassificationType.Module 31 | | SemanticClassificationType.Namespace -> "namespace" 32 | | SemanticClassificationType.Printf -> "regexp" 33 | | SemanticClassificationType.ComputationExpression -> "cexpr" 34 | | SemanticClassificationType.IntrinsicFunction -> "function" 35 | | SemanticClassificationType.Enumeration -> "enum" 36 | | SemanticClassificationType.Interface -> "interface" 37 | | SemanticClassificationType.TypeArgument -> "typeParameter" 38 | | SemanticClassificationType.DisposableTopLevelValue 39 | | SemanticClassificationType.DisposableLocalValue 40 | | SemanticClassificationType.DisposableType -> "disposable" 41 | | SemanticClassificationType.Literal -> "variable.readonly.defaultLibrary" 42 | | SemanticClassificationType.RecordField 43 | | SemanticClassificationType.RecordFieldAsFunction -> "property.readonly" 44 | | SemanticClassificationType.Exception 45 | | SemanticClassificationType.Field 46 | | SemanticClassificationType.Event 47 | | SemanticClassificationType.Delegate 48 | | SemanticClassificationType.NamedArgument -> "member" 49 | | SemanticClassificationType.Value 50 | | SemanticClassificationType.LocalValue -> "variable" 51 | | SemanticClassificationType.Plaintext -> "text" 52 | 53 | 54 | ///Converts FCS token information to LSP Token information 55 | let private convertToken (tokens:SemanticClassificationItem[] option)= 56 | match tokens with 57 | | None -> 58 | None 59 | | Some rangesAndHighlights -> 60 | let lspTypedRanges = 61 | rangesAndHighlights 62 | |> Array.map (fun {Range=fcsRange;Type= fcsTokenType} -> 63 | 64 | let ty, mods = SemanticToken.map fcsTokenType 65 | struct(Conversions.asRange fcsRange, ty, mods) 66 | ) 67 | match SemanticToken.encodeSemanticHighlightRanges lspTypedRanges with 68 | | None -> 69 | None 70 | | Some encoded -> 71 | (Some { data = encoded|>Array.toList; resultId = None }) // TODO: provide a resultId when we support delta ranges 72 | 73 | 74 | let posEq (p1: pos) (p2: pos) = p1 = p2 75 | //All taken from FSAC semnantic token code 76 | 77 | /// given an enveloping range and the sub-ranges it overlaps, split out the enveloping range into a 78 | /// set of range segments that are non-overlapping with the children 79 | let private segmentRanges (parentRange: Range) (childRanges: Range []): Range [] = 80 | let firstSegment = Range.mkRange parentRange.FileName parentRange.Start childRanges.[0].Start // from start of parent to start of first child 81 | let lastSegment = Range.mkRange parentRange.FileName (Array.last childRanges).End parentRange.End // from end of last child to end of parent 82 | // now we can go pairwise, emitting a new range for the area between each end and start 83 | let innerSegments = 84 | childRanges |> Array.pairwise |> Array.map (fun (left, right) -> Range.mkRange parentRange.FileName left.End right.Start) 85 | 86 | [| 87 | // note that the first and last segments can be zero-length. 88 | // in that case we should not emit them because it confuses the 89 | // encoding algorithm 90 | if posEq firstSegment.Start firstSegment.End then () else firstSegment 91 | yield! innerSegments 92 | if posEq lastSegment.Start lastSegment.End then () else lastSegment 93 | |] 94 | 95 | /// TODO: LSP technically does now know how to handle overlapping, nested and multiline ranges, but 96 | /// as of 3 February 2021 there are no good examples of this that I've found, so we still do this 97 | /// because LSP doesn't know how to handle overlapping/nested ranges, we have to dedupe them here 98 | let private scrubRanges (highlights: SemanticClassificationItem array): SemanticClassificationItem array = 99 | let startToken = fun( {Range=m}:SemanticClassificationItem) -> m.Start.Line, m.Start.Column 100 | highlights 101 | |> Array.sortBy startToken 102 | |> Array.groupBy (fun {Range=r} -> r.StartLine) 103 | |> Array.collect (fun (_, highlights) -> 104 | 105 | // split out any ranges that contain other ranges on this line into the non-overlapping portions of that range 106 | let expandParents ({Range=parentRange;Type=tokenType}:SemanticClassificationItem as p) = 107 | let children = 108 | highlights 109 | |> Array.except [p] 110 | |> Array.choose (fun {Range=childRange} -> if Range.rangeContainsRange parentRange childRange then Some childRange else None) 111 | match children with 112 | | [||] -> [| p |] 113 | | children -> 114 | let sortedChildren = children |> Array.sortBy (fun r -> r.Start.Line, r.Start.Column) 115 | segmentRanges parentRange sortedChildren 116 | |> Array.map (fun subRange -> SemanticClassificationItem((subRange,tokenType)) ) 117 | 118 | highlights 119 | |> Array.collect expandParents 120 | ) 121 | |> Array.sortBy startToken 122 | 123 | ///Gets the tokens for semantic tokenization 124 | ///This is done by getting typecheck data and then getting the individual semntic clasifcations of the tokens in that data 125 | ///The checking can be done only in a certain range using the Range attribute. I'm not sure if this is really much faster or not 126 | //TODO: benchmark this process to find out whether only doing a certian range is actually faster 127 | let getSemanticTokens range (typeCheckResults:Result<(FSharpParseFileResults * FSharpCheckFileResults),Diagnostic list>)= 128 | async{ 129 | let tokens= 130 | match typeCheckResults with 131 | |Ok(parse,check)-> 132 | let ranges=check.GetSemanticClassification range 133 | //If we don't do the crubbing heaps of the tokesn don't show up properly in the edditor 134 | let filteredRanges = scrubRanges ranges 135 | Some filteredRanges 136 | |_->None 137 | return convertToken tokens 138 | } -------------------------------------------------------------------------------- /src/FSharpLanguageServer/FSAC/ToolTips/Format.fs: -------------------------------------------------------------------------------- 1 | module private FSharpLanguageServer.ToolTips.Formatting 2 | 3 | open System 4 | open System.Text.RegularExpressions 5 | 6 | let inline nl<'T> = Environment.NewLine 7 | 8 | let tagPattern (tagName: string) = 9 | sprintf 10 | """(?'void_element'<%s(?'void_attributes'\s+[^\/>]+)?\/>)|(?'non_void_element'<%s(?'non_void_attributes'\s+[^>]+)?>(?'non_void_innerText'(?:(?!<%s>)(?!<\/%s>)[\s\S])*)<\/%s\s*>)""" 11 | tagName 12 | tagName 13 | tagName 14 | tagName 15 | tagName 16 | 17 | type TagInfo = 18 | | VoidElement of attributes: Map 19 | | NonVoidElement of innerText: string * attributes: Map 20 | 21 | type FormatterInfo = 22 | { TagName: string 23 | Formatter: TagInfo -> string option } 24 | 25 | let private extractTextFromQuote (quotedText: string) = 26 | quotedText.Substring(1, quotedText.Length - 2) 27 | 28 | 29 | let extractMemberText (text: string) = 30 | let pattern = 31 | "(?'member_type'[a-z]{1}:)?(?'member_text'.*)" 32 | 33 | let m = 34 | Regex.Match(text, pattern, RegexOptions.IgnoreCase) 35 | 36 | if m.Groups.["member_text"].Success then 37 | m.Groups.["member_text"].Value 38 | else 39 | text 40 | 41 | let getAttributes (attributes: Group) = 42 | if attributes.Success then 43 | let pattern = 44 | """(?'key'\S+)=(?'value''[^']*'|"[^"]*")""" 45 | 46 | Regex.Matches(attributes.Value, pattern, RegexOptions.IgnoreCase) 47 | |> Seq.cast 48 | |> Seq.map (fun m -> m.Groups.["key"].Value, extractTextFromQuote m.Groups.["value"].Value) 49 | |> Map.ofSeq 50 | else 51 | Map.empty 52 | 53 | type AttrLookup = Map -> Option 54 | 55 | let cref: AttrLookup = Map.tryFind "cref" 56 | let langword: AttrLookup = Map.tryFind "langword" 57 | let href: AttrLookup = Map.tryFind "href" 58 | let lang: AttrLookup = Map.tryFind "lang" 59 | let name: AttrLookup = Map.tryFind "name" 60 | 61 | let rec applyFormatter (info: FormatterInfo) text = 62 | let pattern = tagPattern info.TagName 63 | 64 | match Regex.Match(text, pattern, RegexOptions.IgnoreCase) with 65 | | m when m.Success -> 66 | if m.Groups.["void_element"].Success then 67 | let attributes = 68 | getAttributes m.Groups.["void_attributes"] 69 | 70 | let replacement = VoidElement attributes |> info.Formatter 71 | 72 | match replacement with 73 | | Some replacement -> 74 | text.Replace(m.Groups.["void_element"].Value, replacement) 75 | // Re-apply the formatter, because perhaps there is more 76 | // of the current tag to convert 77 | |> applyFormatter info 78 | 79 | | None -> 80 | // The formatter wasn't able to convert the tag 81 | // Return as it is and don't re-apply the formatter 82 | // otherwise it will create an infinity loop 83 | text 84 | 85 | else if m.Groups.["non_void_element"].Success then 86 | let innerText = m.Groups.["non_void_innerText"].Value 87 | 88 | let attributes = 89 | getAttributes m.Groups.["non_void_attributes"] 90 | 91 | let replacement = 92 | NonVoidElement(innerText, attributes) 93 | |> info.Formatter 94 | 95 | match replacement with 96 | | Some replacement -> 97 | // Re-apply the formatter, because perhaps there is more 98 | // of the current tag to convert 99 | text.Replace(m.Groups.["non_void_element"].Value, replacement) 100 | |> applyFormatter info 101 | 102 | | None -> 103 | // The formatter wasn't able to convert the tag 104 | // Return as it is and don't re-apply the formatter 105 | // otherwise it will create an infinity loop 106 | text 107 | else 108 | // Should not happend but like that we are sure to handle all possible cases 109 | text 110 | | _ -> text 111 | 112 | let codeBlock = 113 | { TagName = "code" 114 | Formatter = 115 | function 116 | | VoidElement _ -> None 117 | 118 | | NonVoidElement (innerText, attributes) -> 119 | let lang = 120 | match lang attributes with 121 | | Some lang -> lang 122 | 123 | | None -> "forceNoHighlight" 124 | 125 | let formattedText = 126 | if innerText.Contains("\n") then 127 | 128 | if innerText.StartsWith("\n") then 129 | 130 | sprintf "```%s%s\n```" lang innerText 131 | 132 | else 133 | sprintf "```%s\n%s\n```" lang innerText 134 | 135 | else 136 | sprintf "`%s`" innerText 137 | 138 | Some formattedText 139 | 140 | } 141 | |> applyFormatter 142 | 143 | let codeInline = 144 | { TagName = "c" 145 | Formatter = 146 | function 147 | | VoidElement _ -> None 148 | | NonVoidElement (innerText, _) -> "`" + innerText + "`" |> Some } 149 | |> applyFormatter 150 | 151 | let link text uri = $"[`%s{text}`](%s{uri})" 152 | let code text = $"`%s{text}`" 153 | 154 | let anchor = 155 | { TagName = "a" 156 | Formatter = 157 | function 158 | | VoidElement attributes -> 159 | match href attributes with 160 | | Some href -> Some(link href href) 161 | | None -> None 162 | 163 | | NonVoidElement (innerText, attributes) -> 164 | match href attributes with 165 | | Some href -> Some(link innerText href) 166 | | None -> Some(code innerText) } 167 | |> applyFormatter 168 | 169 | let paragraph = 170 | { TagName = "para" 171 | Formatter = 172 | function 173 | | VoidElement _ -> None 174 | 175 | | NonVoidElement (innerText, _) -> nl + innerText + nl |> Some } 176 | |> applyFormatter 177 | 178 | let block = 179 | { TagName = "block" 180 | Formatter = 181 | function 182 | | VoidElement _ -> None 183 | 184 | | NonVoidElement (innerText, _) -> nl + innerText + nl |> Some } 185 | |> applyFormatter 186 | 187 | let see = 188 | let formatFromAttributes (attrs: Map) = 189 | match cref attrs with 190 | // crefs can have backticks in them, which mess with formatting. 191 | // for safety we can just double-backtick and markdown is ok with that. 192 | | Some cref -> Some $"``{extractMemberText cref}``" 193 | | None -> 194 | match langword attrs with 195 | | Some langword -> Some(code langword) 196 | | None -> None 197 | 198 | { TagName = "see" 199 | Formatter = 200 | function 201 | | VoidElement attributes -> formatFromAttributes attributes 202 | | NonVoidElement (innerText, attributes) -> 203 | if String.IsNullOrWhiteSpace innerText then 204 | formatFromAttributes attributes 205 | else 206 | match href attributes with 207 | | Some externalUrl -> Some(link innerText externalUrl) 208 | | None -> Some $"`{innerText}`" } 209 | |> applyFormatter 210 | 211 | let xref = 212 | { TagName = "xref" 213 | Formatter = 214 | function 215 | | VoidElement attributes -> 216 | match href attributes with 217 | | Some href -> Some(link href href) 218 | | None -> None 219 | 220 | | NonVoidElement (innerText, attributes) -> 221 | if String.IsNullOrWhiteSpace innerText then 222 | match href attributes with 223 | | Some href -> Some(link innerText href) 224 | | None -> None 225 | else 226 | Some(code innerText) } 227 | |> applyFormatter 228 | 229 | let paramRef = 230 | { TagName = "paramref" 231 | Formatter = 232 | function 233 | | VoidElement attributes -> 234 | match name attributes with 235 | | Some name -> Some(code name) 236 | | None -> None 237 | 238 | | NonVoidElement (innerText, attributes) -> 239 | if String.IsNullOrWhiteSpace innerText then 240 | match name attributes with 241 | | Some name -> 242 | // TODO: Add config to generates command 243 | Some(code name) 244 | | None -> None 245 | else 246 | Some(code innerText) 247 | 248 | } 249 | |> applyFormatter 250 | 251 | let typeParamRef = 252 | { TagName = "typeparamref" 253 | Formatter = 254 | function 255 | | VoidElement attributes -> 256 | match name attributes with 257 | | Some name -> Some(code name) 258 | | None -> None 259 | 260 | | NonVoidElement (innerText, attributes) -> 261 | if String.IsNullOrWhiteSpace innerText then 262 | match name attributes with 263 | | Some name -> 264 | // TODO: Add config to generates command 265 | Some(code name) 266 | | None -> None 267 | else 268 | Some(code innerText) } 269 | |> applyFormatter 270 | 271 | let fixPortableClassLibrary (text: string) = 272 | text.Replace( 273 | "~/docs/standard/cross-platform/cross-platform-development-with-the-portable-class-library.md", 274 | "https://docs.microsoft.com/en-gb/dotnet/standard/cross-platform/cross-platform-development-with-the-portable-class-library" 275 | ) 276 | -------------------------------------------------------------------------------- /src/FSharpLanguageServer/FSharpLanguageServer.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | true 5 | Exe 6 | net6.0 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/FSharpLanguageServer/Goto.fs: -------------------------------------------------------------------------------- 1 | module FSharpLanguageServer.Goto 2 | let _TryFindIdentifierDeclaration (pos: Pos) (lineStr: LineStr) = 3 | match Lexer.findLongIdents(pos.Column, lineStr) with 4 | | None -> async.Return (ResultOrString.Error "Could not find ident at this location") 5 | | Some(col, identIsland) -> 6 | let identIsland = Array.toList identIsland 7 | let declarations = checkResults.GetDeclarationLocation(pos.Line, col, lineStr, identIsland, preferFlag = false) 8 | 9 | let decompile assembly externalSym = 10 | match Decompiler.tryFindExternalDeclaration checkResults (assembly, externalSym) with 11 | | Ok extDec -> ResultOrString.Ok (FindDeclarationResult.ExternalDeclaration extDec) 12 | | Error(Decompiler.FindExternalDeclarationError.ReferenceHasNoFileName assy) -> ResultOrString.Error (sprintf "External declaration assembly '%s' missing file name" assy.SimpleName) 13 | | Error(Decompiler.FindExternalDeclarationError.ReferenceNotFound assy) -> ResultOrString.Error (sprintf "External declaration assembly '%s' not found" assy) 14 | | Error(Decompiler.FindExternalDeclarationError.DecompileError (Decompiler.Exception(symbol, file, exn))) -> 15 | Error (sprintf "Error while decompiling symbol '%A' in file '%s': %s\n%s" symbol file exn.Message exn.StackTrace) 16 | 17 | /// these are all None because you can't easily get the source file from the external symbol information here. 18 | let tryGetSourceRangeForSymbol (sym: FSharpExternalSymbol): (string * Pos) option = 19 | match sym with 20 | | FSharpExternalSymbol.Type name -> None 21 | | FSharpExternalSymbol.Constructor(typeName, args) -> None 22 | | FSharpExternalSymbol.Method(typeName, name, paramSyms, genericArity) -> None 23 | | FSharpExternalSymbol.Field(typeName, name) -> None 24 | | FSharpExternalSymbol.Event(typeName, name) -> None 25 | | FSharpExternalSymbol.Property(typeName, name) -> None 26 | 27 | // attempts to manually discover symbol use and externalsymbol information for a range that doesn't exist in a local file 28 | // bugfix/workaround for FCS returning invalid declfound for f# members. 29 | let tryRecoverExternalSymbolForNonexistentDecl (rangeInNonexistentFile: Range): ResultOrString * string> = 30 | match Lexer.findLongIdents(pos.Column - 1, lineStr) with 31 | | None -> ResultOrString.Error (sprintf "Range for nonexistent file found, no ident found: %s" rangeInNonexistentFile.FileName) 32 | | Some (col, identIsland) -> 33 | let identIsland = Array.toList identIsland 34 | let symbolUse = checkResults.GetSymbolUseAtLocation(pos.Line, col, lineStr, identIsland) 35 | match symbolUse with 36 | | None -> ResultOrString.Error (sprintf "Range for nonexistent file found, no symboluse found: %s" rangeInNonexistentFile.FileName) 37 | | Some sym -> 38 | match sym.Symbol.Assembly.FileName with 39 | | Some fullFilePath -> 40 | Ok (UMX.tag fullFilePath, UMX.tag rangeInNonexistentFile.FileName) 41 | | None -> 42 | ResultOrString.Error (sprintf "Assembly '%s' declaring symbol '%s' has no location on disk" sym.Symbol.Assembly.QualifiedName sym.Symbol.DisplayName) 43 | 44 | async { 45 | match declarations with 46 | | FSharpFindDeclResult.DeclNotFound reason -> 47 | let elaboration = 48 | match reason with 49 | | FSharpFindDeclFailureReason.NoSourceCode -> "No source code was found for the declaration" 50 | | FSharpFindDeclFailureReason.ProvidedMember m -> sprintf "Go-to-declaration is not available for Type Provider-provided member %s" m 51 | | FSharpFindDeclFailureReason.ProvidedType t -> sprintf "Go-to-declaration is not available from Type Provider-provided type %s" t 52 | | FSharpFindDeclFailureReason.Unknown r -> r 53 | return ResultOrString.Error (sprintf "Could not find declaration. %s" elaboration) 54 | | FSharpFindDeclResult.DeclFound range when range.FileName.EndsWith(Range.rangeStartup.FileName) -> return ResultOrString.Error "Could not find declaration" 55 | | FSharpFindDeclResult.DeclFound range when System.IO.File.Exists range.FileName -> 56 | let rangeStr = range.ToString() 57 | logger.info (Log.setMessage "Got a declresult of {range} that supposedly exists" >> Log.addContextDestructured "range" rangeStr) 58 | return Ok (FindDeclarationResult.Range range) 59 | | FSharpFindDeclResult.DeclFound rangeInNonexistentFile -> 60 | let range = rangeInNonexistentFile.ToString() 61 | logger.warn (Log.setMessage "Got a declresult of {range} that doesn't exist" >> Log.addContextDestructured "range" range) 62 | match tryRecoverExternalSymbolForNonexistentDecl rangeInNonexistentFile with 63 | | Ok (assemblyFile, sourceFile) -> 64 | match! Sourcelink.tryFetchSourcelinkFile assemblyFile sourceFile with 65 | | Ok localFilePath -> 66 | return ResultOrString.Ok (FindDeclarationResult.ExternalDeclaration { File = UMX.untag localFilePath; Position = rangeInNonexistentFile.Start }) 67 | | Error reason -> 68 | return ResultOrString.Error (sprintf "%A" reason) 69 | | Error e -> return Error e 70 | | FSharpFindDeclResult.ExternalDecl (assembly, externalSym) -> 71 | // not enough info on external symbols to get a range-like thing :( 72 | match tryGetSourceRangeForSymbol externalSym with 73 | | Some (sourceFile, pos) -> 74 | match! Sourcelink.tryFetchSourcelinkFile (UMX.tag assembly) sourceFile with 75 | | Ok localFilePath -> 76 | return ResultOrString.Ok (FindDeclarationResult.ExternalDeclaration { File = UMX.untag localFilePath; Position = pos }) 77 | | Error reason -> 78 | logger.info (Log.setMessage "no sourcelink info for {assembly}, decompiling instead" >> Log.addContextDestructured "assembly" assembly) 79 | return decompile assembly externalSym 80 | | None -> 81 | return decompile assembly externalSym 82 | } -------------------------------------------------------------------------------- /src/FSharpLanguageServer/ProgressBar.fs: -------------------------------------------------------------------------------- 1 | namespace FSharpLanguageServer 2 | 3 | open System 4 | open System.IO 5 | open LSP.Types 6 | open FSharp.Data 7 | 8 | /// When we check a long series of files, create a progress bar 9 | type ProgressBar(nFiles: int, title: string, client: ILanguageClient, ?hide: bool) = 10 | let token=Guid.NewGuid().ToString() 11 | let hide = defaultArg hide false 12 | let mutable processed=0; 13 | let notifyProgress workDone= client.WorkDoneProgressNotification(token,workDone) 14 | do if not hide then 15 | client.CustomNotification(Random().Next(0,100),"window/workDoneProgress/create",JsonValue.Record([|"token",JsonValue.String(token)|])) 16 | workDoneProgressBegin(title,Some false,Some <|nFiles.ToString(),Some 0u)|>notifyProgress 17 | /// Increment the progress bar and change the displayed message to the current file name 18 | member this.Increment(sourceFile: FileInfo) = 19 | if not hide then 20 | let message = $"processed {sourceFile}. Remaining:{nFiles-processed}" 21 | let percent=(processed*100)/nFiles|>uint 22 | workDoneProgressReport(Some false,Some message,Some percent)|>notifyProgress 23 | processed<- processed+1 24 | 25 | /// Close the progress bar 26 | interface IDisposable with 27 | member this.Dispose() = 28 | if not hide then 29 | workDoneProgressEnd(Some "Done")|>notifyProgress 30 | 31 | -------------------------------------------------------------------------------- /src/FSharpLanguageServer/ProjectManager/FileCache.fs: -------------------------------------------------------------------------------- 1 | module FSharpLanguageServer.ProjectManager.FileCache 2 | open LSP.Log 3 | open LSP.Utils 4 | open System 5 | open System.IO 6 | open System.Collections.Generic 7 | open FSharp.Compiler.Text 8 | open Thoth.Json.Net 9 | open Newtonsoft.Json 10 | open Types 11 | 12 | type CacheData={ 13 | ///hash of the projects assets.json. This is used to see if the project has changed which would invalidate our hash. 14 | assetsHash :string 15 | fsprojHash :string 16 | Project:ResolvedProject 17 | ///Used to allow deleting of old cache data if we make significant changes 18 | version:string 19 | } 20 | let currentVersion="0.1.80" 21 | type FileInfoConverter() = 22 | inherit JsonConverter() 23 | override x.WriteJson( writer:JsonWriter, value:FileInfo, serializer:JsonSerializer)= 24 | writer.WriteValue(value.FullName); 25 | 26 | override x.ReadJson( reader:JsonReader, objectType:Type, existingValue:FileInfo, hasExistingValue:bool, serializer:JsonSerializer)= 27 | let s = reader.Value:?>string 28 | normedFileInfo(s) 29 | 30 | let private serialzeProjectData (projectCache: Dictionary)= 31 | let projectData=projectCache|> Seq.map(fun x->x.Key,x.Value.resolved.Value); 32 | System.Text.Json.JsonSerializer.Serialize(projectData) 33 | 34 | let private deserializeProjectData(json:string) :Dictionary = 35 | let projects=System.Text.Json.JsonSerializer.Deserialize<(string*ResolvedProject) list>(json) 36 | projects|>List.map(fun( name,proj)->KeyValuePair(name,{file=normedFileInfo(name); resolved= lazy(proj) }))|>Dictionary 37 | 38 | let private getCachePath (projectPath:string)=Path.Combine(Path.GetDirectoryName(projectPath),"obj","fslspCache.json") 39 | 40 | let getHash fileName= 41 | use md5 = System.Security.Cryptography.MD5.Create() 42 | use stream = File.OpenRead(fileName) 43 | md5.ComputeHash(stream) |>BitConverter.ToString 44 | 45 | let settings = 46 | JsonSerializerSettings( 47 | MissingMemberHandling=MissingMemberHandling.Error, 48 | ConstructorHandling=ConstructorHandling.AllowNonPublicDefaultConstructor, 49 | Converters=[|new FileInfoConverter()|] 50 | ) 51 | 52 | let extraEncoders= 53 | Extra.empty 54 | |>(Extra.withCustom 55 | (fun (x:FileInfo)->Encode.string x.FullName) 56 | (fun path value->Ok (normedFileInfo(value.ToString())))) 57 | |>Extra.withCustom 58 | (fun (x:Range)->Encode.string <|System.Text.Json.JsonSerializer.Serialize(x)) 59 | (fun path value->Ok (System.Text.Json.JsonSerializer.Deserialize(value.ToString()) )) 60 | 61 | ///Uses various methods to decide if the cache is still valid or if it needs to be discarded and replaced. 62 | let isCacheValid (fsprojPath:string) (cachePath:string) (cacheData:CacheData)= 63 | 64 | let assetsPath=Path.Combine(Path.GetDirectoryName(cachePath),"project.assets.json") 65 | let assetHash= getHash assetsPath 66 | let fsprojHash= getHash fsprojPath 67 | cacheData.assetsHash=assetHash && cacheData.fsprojHash=fsprojHash && cacheData.version=currentVersion 68 | 69 | ///**Attempts to get cached project data.** 70 | /// 71 | ///O returns the data if the project.assets.json files hash has not changed. A change would indicate that the cached data may no longer be valid. 72 | let tryGetCached (fsproj:FileInfo)= 73 | let cacheFilePath=getCachePath fsproj.FullName 74 | if File.Exists(cacheFilePath)then 75 | let cacheJson= File.ReadAllText(cacheFilePath) 76 | 77 | try 78 | let existingCacheData=match(Decode.Auto.fromString(cacheJson,extra=extraEncoders))with|Ok a->a|Error e->failwithf "error %A"e 79 | 80 | if isCacheValid fsproj.FullName cacheFilePath existingCacheData then Ok existingCacheData 81 | else 82 | File.Delete(cacheFilePath) 83 | lgInfo "Not using cached projOptions for '{proj}' because the project.assets.json hash has changed" fsproj.FullName 84 | Error "Hash had changed" 85 | with 86 | |e-> 87 | lgWarn2 "Cached projectOptions for '{proj}' could not be read, deleting it \nReson {exception}" fsproj e 88 | File.Delete(cacheFilePath) 89 | Error(sprintf "%A" e) 90 | else Error "no cache file exists" 91 | 92 | ///Saves the project data into a cahce file so it can be loaded later 93 | let saveCache (projectData:ResolvedProject) (fsproj:FileInfo) = 94 | let cachePath=getCachePath fsproj.FullName 95 | let assetsPath=Path.Combine(Path.GetDirectoryName(cachePath),"project.assets.json") 96 | let assetHash=getHash assetsPath 97 | let fsprojHash=getHash fsproj.FullName 98 | 99 | let data={assetsHash=assetHash; fsprojHash=fsprojHash;Project=projectData;version=currentVersion} 100 | let cacheJson= Encode.Auto.toString(4,data,extra=extraEncoders) 101 | File.WriteAllText(cachePath,cacheJson) 102 | lgInfo "Saved cache of projectOptions for '{proj}' " fsproj.FullName 103 | 104 | let deleteCache (fsproj:FileInfo) = 105 | let cachePath=getCachePath fsproj.FullName 106 | try 107 | File.Delete(cachePath) 108 | lgInfo "Deleted cache for '{proj}' " fsproj.FullName 109 | with e-> lgWarn2 "Attempted to delete cache for {proj} but had exception {ex}" fsproj.FullName e 110 | -------------------------------------------------------------------------------- /src/FSharpLanguageServer/ProjectManager/Types.fs: -------------------------------------------------------------------------------- 1 | module FSharpLanguageServer.ProjectManager.Types 2 | open System 3 | open System.IO 4 | open LSP.Types 5 | open FSharp.Compiler.CodeAnalysis 6 | type ResolvedProject = { 7 | sources: FileInfo list 8 | options: FSharpProjectOptions 9 | target: FileInfo 10 | errors: Diagnostic list 11 | } 12 | 13 | type LazyProject = { 14 | file: FileInfo 15 | resolved: Lazy 16 | } -------------------------------------------------------------------------------- /src/FSharpLanguageServer/SyntaxTreeOps.fs: -------------------------------------------------------------------------------- 1 | module FSharpLanguageServer.SyntaxOps 2 | 3 | // Forked from https://github.com/dotnet/fsharp/blob/60a2fa663a3c4aed3f03c8bfc6f5e05b04284f23/src/fsharp/range.fs 4 | 5 | open FSharp.Compiler.Syntax 6 | open FSharp.Compiler.Text.Range 7 | 8 | let ident (s, r) = Ident(s, r) 9 | 10 | let textOfId (id: Ident) = id.idText 11 | 12 | let pathOfLid lid = List.map textOfId lid 13 | 14 | let arrPathOfLid lid = Array.ofList (pathOfLid lid) 15 | 16 | let textOfPath path = String.concat "." path 17 | 18 | let textOfLid lid = textOfPath (pathOfLid lid) 19 | 20 | let rangeOfLid (lid: Ident list) = 21 | match lid with 22 | | [] -> failwith "rangeOfLid" 23 | | [id] -> id.idRange 24 | | h :: t -> unionRanges h.idRange (List.last t).idRange 25 | -------------------------------------------------------------------------------- /src/FSharpLanguageServer/UnusedDeclarations.fs: -------------------------------------------------------------------------------- 1 | module FSharpLanguageServer.UnusedDeclarations 2 | 3 | // From https://github.com/Microsoft/visualfsharp/blob/master/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs 4 | 5 | open System.Collections.Generic 6 | open FSharp.Compiler.EditorServices 7 | open FSharp.Compiler.Symbols 8 | open FSharp.Compiler.CodeAnalysis 9 | 10 | type FSharpSymbol with 11 | member this.IsPrivateToFile = 12 | match this with 13 | | :? FSharpMemberOrFunctionOrValue as m -> not m.IsModuleValueOrMember 14 | | :? FSharpEntity as m -> m.Accessibility.IsPrivate 15 | | :? FSharpGenericParameter -> true 16 | | :? FSharpUnionCase as m -> m.Accessibility.IsPrivate 17 | | :? FSharpField as m -> m.Accessibility.IsPrivate 18 | | _ -> false 19 | member this.IsInternalToProject = 20 | match this with 21 | | :? FSharpParameter -> true 22 | | :? FSharpMemberOrFunctionOrValue as m -> not m.IsModuleValueOrMember || not m.Accessibility.IsPublic 23 | | :? FSharpEntity as m -> not m.Accessibility.IsPublic 24 | | :? FSharpGenericParameter -> true 25 | | :? FSharpUnionCase as m -> not m.Accessibility.IsPublic 26 | | :? FSharpField as m -> not m.Accessibility.IsPublic 27 | | _ -> false 28 | 29 | type FSharpSymbolUse with 30 | member this.IsPrivateToFile = 31 | let isPrivate = 32 | match this.Symbol with 33 | | :? FSharpMemberOrFunctionOrValue as m -> not m.IsModuleValueOrMember || m.Accessibility.IsPrivate 34 | | :? FSharpEntity as m -> m.Accessibility.IsPrivate 35 | | :? FSharpGenericParameter -> true 36 | | :? FSharpUnionCase as m -> m.Accessibility.IsPrivate 37 | | :? FSharpField as m -> m.Accessibility.IsPrivate 38 | | _ -> false 39 | let declarationLocation = 40 | match this.Symbol.SignatureLocation with 41 | | Some x -> Some x 42 | | _ -> 43 | match this.Symbol.DeclarationLocation with 44 | | Some x -> Some x 45 | | _ -> this.Symbol.ImplementationLocation 46 | let declaredInTheFile = 47 | match declarationLocation with 48 | | Some declRange -> declRange.FileName = this.Range.FileName 49 | | _ -> false 50 | isPrivate && declaredInTheFile 51 | 52 | let private isPotentiallyUnusedDeclaration(symbol: FSharpSymbol) : bool = 53 | match symbol with 54 | // Determining that a record, DU or module is used anywhere requires inspecting all their enclosed entities (fields, cases and func / vals) 55 | // for usages, which is too expensive to do. Hence we never gray them out. 56 | | :? FSharpEntity as e when e.IsFSharpRecord || e.IsFSharpUnion || e.IsInterface || e.IsFSharpModule || e.IsClass || e.IsNamespace -> false 57 | // FCS returns inconsistent results for override members; we're skipping these symbols. 58 | | :? FSharpMemberOrFunctionOrValue as f when 59 | f.IsOverrideOrExplicitInterfaceImplementation || 60 | f.IsBaseValue || 61 | f.IsConstructor -> false 62 | // Usage of DU case parameters does not give any meaningful feedback; we never gray them out. 63 | | :? FSharpParameter -> false 64 | | _ -> true 65 | 66 | let getUnusedDeclarationRanges(symbolsUses: FSharpSymbolUse[], isScript: bool) = 67 | let definitions = 68 | symbolsUses 69 | |> Array.filter (fun su -> 70 | su.IsFromDefinition && 71 | su.Symbol.DeclarationLocation.IsSome && 72 | (isScript || su.IsPrivateToFile) && 73 | not (su.Symbol.DisplayName.StartsWith "_") && 74 | isPotentiallyUnusedDeclaration su.Symbol) 75 | let usages = 76 | let usages = 77 | symbolsUses 78 | |> Array.filter (fun su -> not su.IsFromDefinition) 79 | |> Array.choose (fun su -> su.Symbol.DeclarationLocation) 80 | HashSet(usages) 81 | let unusedRanges = 82 | definitions 83 | |> Array.map (fun defSu -> defSu, usages.Contains defSu.Symbol.DeclarationLocation.Value) 84 | |> Array.groupBy (fun (defSu, _) -> defSu.Range) 85 | |> Array.filter (fun (_, defSus) -> defSus |> Array.forall (fun (_, isUsed) -> not isUsed)) 86 | |> Array.map (fun (m, _) -> m) 87 | unusedRanges -------------------------------------------------------------------------------- /src/FSharpLanguageServer/bin/Release/netcoreapp2.0/assembly/README.md: -------------------------------------------------------------------------------- 1 | This directory is a workaround of https://github.com/georgewfraser/fsharp-language-server/issues/29 2 | -------------------------------------------------------------------------------- /src/FSharpLanguageServer/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Compiler.Service 2 | HtmlAgilityPack 3 | FSharp.UMX 4 | Thoth.Json.Net 5 | Microsoft.Build.Utilities.Core -------------------------------------------------------------------------------- /src/LSP/DocumentStore.fs: -------------------------------------------------------------------------------- 1 | namespace LSP 2 | 3 | open LSP.Log 4 | open System 5 | open System.IO 6 | open System.Collections.Generic 7 | open System.Text 8 | open Types 9 | open BaseTypes 10 | open FSharp 11 | open LSP.Utils 12 | type private VolatileFile = { 13 | text: StringBuilder 14 | mutable version: int 15 | } 16 | 17 | module DocumentStoreUtils = 18 | let findRange(text: StringBuilder, range: Range): int * int = 19 | let mutable line = 0 20 | let mutable char = 0 21 | let mutable startOffset = 0 22 | let mutable endOffset = 0 23 | for offset = 0 to text.Length do 24 | if line = range.start.line && char = range.start.character then 25 | startOffset <- offset 26 | if line = range.``end``.line && char = range.``end``.character then 27 | endOffset <- offset 28 | if offset < text.Length then 29 | let c = text.[offset] 30 | if c = '\n' then 31 | line <- line + 1 32 | char <- 0 33 | else 34 | char <- char + 1 35 | (startOffset, endOffset) 36 | 37 | open DocumentStoreUtils 38 | 39 | type DocumentStore() = 40 | /// All open documents, organized by absolute path 41 | let activeDocuments = new Dictionary() 42 | /// Replace a section of an open file 43 | let patch(doc: VersionedTextDocumentIdentifier, range: Range, text: string): unit = 44 | let file = normedFileInfo(doc.uri.LocalPath) 45 | let existing = activeDocuments.[file.FullName] 46 | let startOffset, endOffset = findRange(existing.text, range) 47 | existing.text.Remove(startOffset, endOffset - startOffset) |> ignore 48 | existing.text.Insert(startOffset, text) |> ignore 49 | existing.version <- doc.version 50 | /// Replace the entire contents of an open file 51 | let replace(doc: VersionedTextDocumentIdentifier, text: string): unit = 52 | let file = normedFileInfo(doc.uri.LocalPath) 53 | let existing = activeDocuments.[file.FullName] 54 | existing.text.Clear() |> ignore 55 | existing.text.Append(text) |> ignore 56 | existing.version <- doc.version 57 | 58 | member this.Open(doc: DidOpenTextDocumentParams): unit = 59 | let file = normedFileInfo(doc.textDocument.uri.LocalPath) 60 | let text = StringBuilder(doc.textDocument.text) 61 | let version = {text = text; version = doc.textDocument.version} 62 | activeDocuments.[file.FullName] <- version 63 | 64 | member this.Change(doc: DidChangeTextDocumentParams): unit = 65 | let file = normedFileInfo(doc.textDocument.uri.LocalPath) 66 | let existing = activeDocuments.[file.FullName] 67 | if doc.textDocument.version <= existing.version then 68 | let oldVersion = existing.version 69 | let newVersion = doc.textDocument.version 70 | lgInfo3 "Changed version: {newVersion} of doc {name} is earlier than existing version {oldVersion}" newVersion file.Name oldVersion 71 | else 72 | for change in doc.contentChanges do 73 | match change.range with 74 | | Some range -> patch(doc.textDocument, range, change.text) 75 | | None -> replace(doc.textDocument, change.text) 76 | 77 | member this.GetText(file: NormedFileInfo): string option = 78 | let found, value = activeDocuments.TryGetValue(file.FullName) 79 | if found then Some(value.text.ToString()) else None 80 | 81 | member this.GetVersion(file: FileInfo): int option = 82 | let found, value = activeDocuments.TryGetValue(file.FullName) 83 | if found then Some(value.version) else None 84 | 85 | member this.Get(file: FileInfo): option = 86 | let found, value = activeDocuments.TryGetValue(file.FullName) 87 | if found then Some(value.text.ToString(), value.version) else None 88 | 89 | member this.Close(doc: DidCloseTextDocumentParams): unit = 90 | let file = normedFileInfo(doc.textDocument.uri.LocalPath) 91 | activeDocuments.Remove(file.FullName) |> ignore 92 | 93 | member this.OpenFiles(): FileInfo list = 94 | [for file in activeDocuments.Keys do yield normedFileInfo(file)] -------------------------------------------------------------------------------- /src/LSP/LSP.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | true 5 | net6.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/LSP/Log.fs: -------------------------------------------------------------------------------- 1 | module LSP.Log 2 | 3 | open Serilog 4 | open Microsoft.FSharp.Core.Printf 5 | open System 6 | open System.Collections.Generic 7 | open System.IO 8 | open Serilog 9 | open Serilog 10 | open Serilog.Core 11 | open Serilog.Events 12 | let diagnosticsLog = ref stderr 13 | /// Print to LSP.Log.diagnosticsLog, which is stderr by default but can be redirected 14 | let dprintfn(fmt: Printf.TextWriterFormat<'T>): 'T = 15 | Printf.fprintfn diagnosticsLog.Value fmt 16 | 17 | 18 | let lgInfof fmt= 19 | ksprintf (Log.Information )fmt 20 | let lgWarnf fmt= 21 | ksprintf (Log.Warning )fmt 22 | let lgErrorf fmt= 23 | ksprintf (Log.Error )fmt 24 | let lgVerbosef fmt= 25 | ksprintf (Log.Verbose )fmt 26 | let lgDebugf fmt= 27 | ksprintf (Log.Debug )fmt 28 | 29 | let lgInfo (message:string) (data:obj)= 30 | Log.Information(message, data) 31 | let lgInfo2 (message:string) (data:obj) (data2:obj)= 32 | Log.Information(message, data,data2) 33 | let lgInfo3 (message:string) (data:obj) (data2:obj) (data3:obj)= 34 | Log.Information(message, data,data2,data3) 35 | 36 | let lgError (message:string) (data:obj)= 37 | Log.Error(message, data) 38 | let lgError2 (message:string) ( data:obj ) ( data2:obj )= 39 | Log.Error(message, data,data2) 40 | let lgError3 (message:string) (data:obj) ( data2:obj ) (data3:obj)= 41 | Log.Error(message, data,data2,data3) 42 | 43 | let lgWarn (message:string) (data:obj)= 44 | Log.Warning(message, data) 45 | let lgWarn2 (message:string) (data:obj) (data2:obj)= 46 | Log.Warning(message, data,data2) 47 | let lgWarn3 (message:string) (data:obj) (data2:obj) (data3:obj)= 48 | Log.Warning(message, data,data2,data3) 49 | 50 | let lgDebug (message:string) (data:obj)= 51 | Log.Debug(message, data) 52 | let lgDebug2 (message:string) (data:obj) (data2:obj)= 53 | Log.Debug(message, data,data2) 54 | let lgDebug3 (message:string) (data:obj) (data2:obj) (data3:obj)= 55 | Log.Debug(message, data,data2,data3) 56 | 57 | let lgVerb (message:string) (data:obj)= 58 | Log.Verbose(message, data) 59 | let lgVerb2 (message:string) (data:obj) (data2:obj)= 60 | Log.Verbose(message, data,data2) 61 | let lgVerb3 (message:string) (data:obj) (data2:obj) (data3:obj)= 62 | Log.Verbose(message, data,data2,data3) 63 | 64 | 65 | 66 | 67 | let startTime=DateTime.Now 68 | let createLogger logPath = 69 | let logName=sprintf "%sdebugLog-%i-%i_%i;%i-%is--.log" logPath startTime.Month startTime.Day startTime.Hour startTime.Minute startTime.Second 70 | dprintfn "%s"logName 71 | 72 | let logger= 73 | Serilog.LoggerConfiguration() 74 | .MinimumLevel.Verbose() 75 | .WriteTo.File(logName ,Serilog.Events.LogEventLevel.Verbose,rollingInterval=RollingInterval.Day,fileSizeLimitBytes=(int64 (1000*1000))) 76 | .WriteTo.File(logPath+"simpleLog-.log",Serilog.Events.LogEventLevel.Information,rollingInterval=RollingInterval.Day,fileSizeLimitBytes=(int64 (1000*1000))) 77 | .WriteTo.Console(theme=Sinks.SystemConsole.Themes.SystemConsoleTheme.Literate,restrictedToMinimumLevel=Serilog.Events.LogEventLevel.Information, standardErrorFromLevel=Serilog.Events.LogEventLevel.Information) 78 | .CreateLogger() 79 | (* let logger2= 80 | Serilog.LoggerConfiguration() 81 | .WriteTo.Async( 82 | fun c -> c.Console(outputTemplate = outputTemplate, standardErrorFromLevel = Nullable<_>(LogEventLevel.Verbose), theme = Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme.Code) |> ignore 83 | ) // make it so that every console log is logged to stderr 84 | *) 85 | Serilog.Log.Logger<-logger 86 | dprintfn "logger created" 87 | -------------------------------------------------------------------------------- /src/LSP/Ser.fs: -------------------------------------------------------------------------------- 1 | module LSP.Json.Ser 2 | 3 | open System 4 | open System.Reflection 5 | open Microsoft.FSharp.Reflection 6 | open Microsoft.FSharp.Reflection.FSharpReflectionExtensions 7 | open System.Text.RegularExpressions 8 | open FSharp.Data 9 | open LSP.Log 10 | let private escapeChars = Regex("[\t\n\r\"\\\\]", RegexOptions.Compiled) 11 | let private replaceChars = 12 | MatchEvaluator(fun m -> 13 | match m.Value with 14 | | "\\" -> "\\\\" 15 | | "\t" -> "\\t" 16 | | "\n" -> "\\n" 17 | | "\r" -> "\\r" 18 | | "\"" -> "\\\"" 19 | | v -> v) 20 | let private escapeStr(text:string) = 21 | let escaped = escapeChars.Replace(text, replaceChars) 22 | sprintf "\"%s\"" escaped 23 | 24 | let private isOption(t: Type) = 25 | t.IsGenericType && t.GetGenericTypeDefinition() = typedefof<_ option> 26 | 27 | let private isSeq(t: Type) = 28 | t.IsGenericType && t.GetGenericTypeDefinition() = typedefof> 29 | let private implementsSeq(t: Type) = 30 | let is = t.GetInterfaces() 31 | Seq.exists isSeq is 32 | 33 | let private isList(t: Type) = 34 | t.IsGenericType && t.GetGenericTypeDefinition() = typedefof> 35 | 36 | let private isMap(t: Type) = 37 | t.IsGenericType && t.GetGenericTypeDefinition() = typedefof> 38 | 39 | type JsonWriteOptions = { 40 | customWriters: obj list 41 | } 42 | 43 | let defaultJsonWriteOptions: JsonWriteOptions = { 44 | customWriters = [] 45 | } 46 | 47 | let private matchWriter(t: Type, w: obj): bool = 48 | let domain, _ = w.GetType() |> FSharpType.GetFunctionElements 49 | domain.IsAssignableFrom(t) 50 | 51 | let private findWriter(t: Type, customWriters: obj list): obj option = 52 | let matchT(w: obj) = matchWriter(t, w) 53 | Seq.tryFind matchT customWriters 54 | 55 | let asFun(w: obj): obj -> obj = 56 | let invoke = w.GetType().GetMethod("Invoke") 57 | fun x -> invoke.Invoke(w, [|x|]) 58 | 59 | type MakeHelpers = 60 | static member MakeList<'T> (items: obj seq): 'T list = 61 | [ for i in items do yield i :?> 'T ] 62 | static member MakeMap<'T> (items: (string * obj) seq): Map = 63 | let castV(k: string, v: obj) = (k, v :?> 'T) 64 | let castItems = Seq.map castV items 65 | Map.ofSeq(castItems) 66 | static member MakeOption<'T> (item: obj option): 'T option = 67 | match item with 68 | | None -> None 69 | | Some(i) -> Some(i :?> 'T) 70 | 71 | let private makeList(t: Type, items: obj seq) = 72 | typeof.GetMethod("MakeList").MakeGenericMethod([|t|]).Invoke(null, [|items|]) 73 | 74 | let private makeMap(t: Type, kvs: (string * obj) seq) = 75 | typeof.GetMethod("MakeMap").MakeGenericMethod([|t|]).Invoke(null, [|kvs|]) 76 | 77 | let private makeOption(t: Type, item: obj option) = 78 | typeof.GetMethod("MakeOption").MakeGenericMethod([|t|]).Invoke(null, [|item|]) 79 | 80 | let rec private serializer (options: JsonWriteOptions, t: Type): obj -> string = 81 | 82 | let custom = findWriter(t, options.customWriters) 83 | if custom.IsSome then 84 | let fObj = custom.Value 85 | let fType = fObj.GetType() 86 | let _, range = FSharpType.GetFunctionElements(fType) 87 | let serialize = serializer(options, range) 88 | let transform = asFun(fObj) 89 | fun o -> serialize(transform(o)) 90 | elif t = typeof then 91 | fun o -> sprintf "%b" (unbox o) 92 | elif t = typeof then 93 | fun o -> sprintf "%d" (unbox o) 94 | elif t = typeof then 95 | fun o -> sprintf "%d" (unbox o) 96 | elif t = typeof then 97 | fun o -> sprintf "%c" (unbox o) |> escapeStr 98 | elif t = typeof then 99 | fun o -> escapeStr (o :?> string) 100 | elif t = typeof then 101 | fun o -> 102 | let uri = o :?> Uri 103 | escapeStr(uri.ToString()) 104 | elif t = typeof then 105 | fun o -> 106 | let asJson = o :?> JsonValue 107 | asJson.ToString(JsonSaveOptions.DisableFormatting) 108 | elif FSharpType.IsRecord t then 109 | let fields = FSharpType.GetRecordFields(t) 110 | let serializers = [|for f in fields do yield fieldSerializer(options, f)|] 111 | fun outer -> 112 | let fieldStrings = [|for f in serializers do yield f(outer)|] 113 | let innerString = String.concat "," fieldStrings 114 | sprintf "{%s}" innerString 115 | elif implementsSeq t then 116 | let innerType = t.GetGenericArguments().[0] 117 | let serializeInner = serializer(options, innerType) 118 | fun outer -> 119 | let asEnum = outer :?> System.Collections.IEnumerable 120 | let asSeq = Seq.cast(asEnum) 121 | let inners = Seq.map serializeInner asSeq 122 | let join = String.Join(',', inners) 123 | sprintf "[%s]" join 124 | elif isOption t then 125 | let innerType = t.GetGenericArguments().[0] 126 | let isSomeProp = t.GetProperty("IsSome") 127 | let isSome outer = isSomeProp.GetValue(None, [|outer|]) :?> bool 128 | let valueProp = t.GetProperty("Value") 129 | let serializeInner = serializer(options, innerType) 130 | fun outer -> 131 | if isSome outer then 132 | let value = valueProp.GetValue outer 133 | serializeInner(value) 134 | else "null" 135 | else 136 | raise (Exception (sprintf "Don't know how to serialize %s to JSON" (t.ToString()))) 137 | and fieldSerializer (options: JsonWriteOptions, field: PropertyInfo): obj -> string = 138 | // dprintfn "Serializing feild %s" (field.ToString()) 139 | let name = escapeStr(field.Name) 140 | let innerSerializer = serializer(options, field.PropertyType) 141 | fun outer -> 142 | let value = field.GetValue(outer) 143 | let json = innerSerializer(value) 144 | sprintf "%s:%s" name json 145 | 146 | let serializerFactory<'T> (options: JsonWriteOptions): 'T -> string = serializer(options, typeof<'T>) 147 | 148 | type JsonReadOptions = { 149 | customReaders: obj list 150 | } 151 | 152 | let defaultJsonReadOptions: JsonReadOptions = { 153 | customReaders = [] 154 | } 155 | 156 | let private matchReader(t: Type, w: obj): bool = 157 | let _, range = w.GetType() |> FSharpType.GetFunctionElements 158 | t.IsAssignableFrom(range) 159 | 160 | let private findReader(t: Type, customReaders: obj list): obj option = 161 | let matchT(reader: obj) = matchReader(t, reader) 162 | Seq.tryFind matchT customReaders 163 | 164 | let rec private deserializer<'T> (options: JsonReadOptions, t: Type): JsonValue -> obj = 165 | let custom = findReader(t, options.customReaders) 166 | if custom.IsSome then 167 | let domain, _ = FSharpType.GetFunctionElements(custom.Value.GetType()) 168 | let deserializeDomain = deserializer(options, domain) 169 | let deserializeInner = asFun(custom.Value) 170 | fun j -> deserializeInner(deserializeDomain(j)) 171 | elif t = typeof then 172 | fun j -> box(j.AsBoolean()) 173 | elif t = typeof then 174 | fun j -> box(j.AsInteger()) 175 | elif t = typeof then 176 | fun j -> 177 | let s = j.AsString() 178 | if s.Length = 1 then box(s.[0]) 179 | else raise(Exception(sprintf "Expected char but found '%s'" s)) 180 | elif t = typeof then 181 | fun j -> box(j.AsString()) 182 | elif t = typeof then 183 | fun j -> 184 | // It seems that the Uri(_) constructor assumes the string has already been unescaped 185 | let escaped = j.AsString() 186 | let unescaped = Uri.UnescapeDataString(escaped) 187 | // This is pretty hacky but I couldn't figure out a better way 188 | // VSCode escapes # only once, but Uri(_) expects an unescaped string 189 | // It seems like either VSCode should be escaping # twice, or Uri(_) should be accepting escaped input 190 | let partlyEscaped = unescaped.Replace("?", "%3F").Replace("#", "%23") 191 | box(Uri(partlyEscaped)) 192 | elif t = typeof then 193 | fun j -> box(j) 194 | elif isList t then 195 | let innerType = t.GetGenericArguments().[0] 196 | let deserializeInner = deserializer(options, innerType) 197 | fun j -> 198 | let array = j.AsArray() 199 | let parse = Seq.map deserializeInner array 200 | let list = makeList(innerType, parse) 201 | box(list) 202 | elif isMap t then 203 | let genericArguments = t.GetGenericArguments() 204 | let stringType = genericArguments.[0] 205 | let valueType = genericArguments.[1] 206 | if stringType <> typeof then raise (Exception (sprintf "Keys of %A are not strings" t)) 207 | let deserializeInner = deserializer(options, valueType) 208 | fun j -> 209 | let props = j.Properties() 210 | let parse = Seq.map (fun (k, v) -> k, deserializeInner v) props 211 | makeMap(valueType, parse) 212 | elif isOption t then 213 | let innerType = t.GetGenericArguments().[0] 214 | let deserializeInner = deserializer(options, innerType) 215 | fun j -> 216 | if j = JsonValue.Null then 217 | box(makeOption(innerType, None)) 218 | else 219 | let parse = deserializeInner j 220 | box(makeOption(innerType, Some parse)) 221 | elif FSharpType.IsRecord t then 222 | let fields = FSharpType.GetRecordFields(t) 223 | let readers = [|for f in fields do yield fieldDeserializer(options, f)|] 224 | fun j -> 225 | let array = [| for field, reader in readers do 226 | yield reader j |] 227 | FSharpValue.MakeRecord(t, array) 228 | else 229 | raise (Exception (sprintf "Don't know how to deserialize %A from JSON" t)) 230 | and fieldDeserializer (options: JsonReadOptions, field: PropertyInfo): string * (JsonValue -> obj) = 231 | let deserializeInner = deserializer(options, field.PropertyType) 232 | let deserializeField(j: JsonValue) = 233 | let value = match j.TryGetProperty(field.Name) with Some v -> v | None -> JsonValue.Null 234 | box(deserializeInner(value)) 235 | field.Name, deserializeField 236 | 237 | let deserializerFactory<'T>(options: JsonReadOptions): JsonValue -> 'T = 238 | let d = deserializer(options, typeof<'T>) 239 | fun s -> d(s) :?> 'T 240 | -------------------------------------------------------------------------------- /src/LSP/Tokenizer.fs: -------------------------------------------------------------------------------- 1 | module LSP.Tokenizer 2 | 3 | open System 4 | open System.IO 5 | open System.Text 6 | open Log 7 | 8 | type Header = ContentLength of int | EmptyHeader | OtherHeader 9 | 10 | let parseHeader(header: string): Header = 11 | let contentLength = "Content-Length: " 12 | if header.StartsWith(contentLength) then 13 | let tail = header.Substring(contentLength.Length) 14 | let length = Int32.Parse(tail) 15 | ContentLength(length) 16 | elif header = "" then EmptyHeader 17 | else OtherHeader 18 | 19 | let rec private eatWhitespace(client: BinaryReader): char = 20 | let c = client.ReadChar() 21 | if Char.IsWhiteSpace(c) then 22 | eatWhitespace(client) 23 | else 24 | c 25 | 26 | let readLength(byteLength: int, client: BinaryReader): string = 27 | // Somehow, we are getting extra \r\n sequences, only when we compile to a standalone executable 28 | let head = eatWhitespace(client) 29 | let tail = client.ReadBytes(byteLength - 1) 30 | let string = Encoding.UTF8.GetString(tail) 31 | Convert.ToString(head) + string 32 | 33 | let readLine(client: BinaryReader): string option = 34 | let buffer = StringBuilder() 35 | try 36 | let mutable endOfLine = false 37 | while not endOfLine do 38 | let nextChar = client.ReadChar() 39 | if nextChar = '\n' then do 40 | endOfLine <- true 41 | elif nextChar = '\r' then do 42 | assert(client.ReadChar() = '\n') 43 | endOfLine <- true 44 | else do 45 | buffer.Append(nextChar) |> ignore 46 | Some(buffer.ToString()) 47 | with 48 | | :? EndOfStreamException -> 49 | if buffer.Length > 0 then 50 | Some(buffer.ToString()) 51 | else 52 | None 53 | 54 | let tokenize(client: BinaryReader): seq = 55 | seq { 56 | let mutable contentLength = -1 57 | let mutable endOfInput = false 58 | while not endOfInput do 59 | let maybeHeader = readLine(client) 60 | let next = Option.map parseHeader maybeHeader 61 | match next with 62 | | None -> endOfInput <- true 63 | | Some(ContentLength l) -> contentLength <- l 64 | | Some(EmptyHeader) -> yield readLength(contentLength, client) 65 | | _ -> () 66 | } 67 | -------------------------------------------------------------------------------- /src/LSP/Types/BaseTypes.fs: -------------------------------------------------------------------------------- 1 | 2 | [] 3 | module LSP.BaseTypes 4 | open System 5 | type Position = { 6 | line: int 7 | character: int 8 | } 9 | 10 | type Range = { 11 | start: Position 12 | ``end``: Position 13 | } 14 | type WorkspaceFolder = { 15 | uri: Uri 16 | name: string 17 | } 18 | type TextDocumentIdentifier = { 19 | uri: Uri 20 | } 21 | -------------------------------------------------------------------------------- /src/LSP/Utils.fs: -------------------------------------------------------------------------------- 1 | namespace LSP 2 | open System 3 | open System.IO 4 | open System.Text.RegularExpressions 5 | type NormedFileInfo=FileInfo 6 | [] 7 | module Utils= 8 | let normalizeDriveLetter (path:string)= 9 | if Regex("(^\w:)").Match(path).Success then 10 | let a=path.ToCharArray() 11 | a[0]<-(a[0] |>Char.ToUpperInvariant) 12 | new String(a) 13 | else path 14 | let normedFileInfo path = 15 | path|> normalizeDriveLetter|>NormedFileInfo -------------------------------------------------------------------------------- /src/LSP/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Compiler.Service 2 | FSharp.Data 3 | Serilog.Sinks.Console 4 | Serilog.Sinks.File 5 | FSharp.SystemTextJson -------------------------------------------------------------------------------- /src/ProjInfo/Library.fs: -------------------------------------------------------------------------------- 1 | module ProjInfo 2 | 3 | open Ionide.ProjInfo 4 | open Ionide.ProjInfo.Types 5 | open System.IO 6 | open System 7 | open FSharp.Compiler.CodeAnalysis 8 | open System.Diagnostics 9 | open Microsoft.Build.Utilities 10 | let dotnetRestore(proj)= 11 | let args = sprintf "restore %s " proj 12 | let info = 13 | ProcessStartInfo( 14 | RedirectStandardOutput = true, 15 | RedirectStandardError = true, 16 | UseShellExecute = false, 17 | FileName = "dotnet", 18 | Arguments = args 19 | ) 20 | Process.Start(info).WaitForExit() 21 | let crack path= 22 | 23 | dotnetRestore(path) 24 | let toolsPath = Init.init (DirectoryInfo Environment.CurrentDirectory) None 25 | let loader= Ionide.ProjInfo.WorkspaceLoader.Create(toolsPath) 26 | let mutable pos = Map.empty 27 | 28 | 29 | let errors=ResizeArray() 30 | loader.Notifications.Add (function 31 | | WorkspaceProjectState.Loaded (po, knownProjects, _) -> pos <- Map.add po.ProjectFileName po pos 32 | | WorkspaceProjectState.Failed(path,a)-> 33 | errors.Add(a) 34 | |_->()) 35 | let parsed = loader.LoadProjects [ path ] |> Seq.toList 36 | printfn "Errors:\n %A" errors 37 | (parsed,FCS.mapToFSharpProjectOptions parsed.Head parsed,errors) 38 | // For more information see https://aka.ms/fsharp-console-apps 39 | printfn "Hello from F#" 40 | 41 | let crackFileInf (fileInfo:FileInfo)= 42 | crack(fileInfo.FullName) 43 | -------------------------------------------------------------------------------- /src/ProjInfo/ProjInfo.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0 5 | true 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/ProjInfo/paket.references: -------------------------------------------------------------------------------- 1 | Ionide.ProjInfo 2 | Ionide.ProjInfo.FCS 3 | Ionide.ProjInfo.ProjectSystem 4 | Microsoft.Build 5 | Microsoft.Build.Framework 6 | Microsoft.Build.Locator 7 | Microsoft.Build.Tasks.Core 8 | Microsoft.Build.Utilities.Core 9 | Microsoft.SourceLink.GitHub 10 | Newtonsoft.Json 11 | 12 | 13 | 14 | 15 | 16 | System.Threading.ThreadPool 17 | System.Net.NameResolution 18 | System.IO.FileSystem 19 | FSharp.Core -------------------------------------------------------------------------------- /syntaxes/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Krzysztof Cieslak 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /syntaxes/README.md: -------------------------------------------------------------------------------- 1 | From https://github.com/ionide/ionide-fsgrammar/tree/master/grammar and https://github.com/ionide/ionide-vscode-fsharp/blob/master/release/language-configuration.json -------------------------------------------------------------------------------- /syntaxes/fsharp.configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "comments": { 4 | "lineComment": "//", 5 | "blockComment": [ "(*", "*)" ] 6 | }, 7 | 8 | "autoClosingPairs": [ 9 | ["[", "]"], 10 | ["(", ")"], 11 | ["{", "}"], 12 | ["<@", "@>"], 13 | ["``", "``"], 14 | ["\"", "\""], 15 | ["(|", "|)"], 16 | ["[<",">]"], 17 | ["[|", "|]"], 18 | ["(*", "*)" ], 19 | ["<@@", "@@>"] 20 | ], 21 | 22 | "surroundingPairs": [ 23 | ["<@@", "@@>"], 24 | ["(|", "|)"], 25 | ["[<",">]"], 26 | ["[|", "|]"], 27 | ["{", "}"], 28 | ["[", "]"], 29 | ["(", ")"], 30 | ["\"", "\""], 31 | ["\"\"\"", "\"\"\""], 32 | ["(*", "*)"], 33 | ["<@", "@>"], 34 | ["'", "'"] 35 | ], 36 | 37 | "brackets": [ 38 | ["(", ")"], 39 | ["(*", "*)"], 40 | ["{", "}"], 41 | ["[", "]"], 42 | ["[|", "|]"], 43 | ["<@", "@>"], 44 | ["<@@", "@@>"] 45 | ], 46 | 47 | "wordPattern": "((\\w*')(\\w'?)*)|(-?\\d*\\.\\d\\w*)|([^\\`\\~\\!\\@\\$\\^\\&\\*\\(\\)\\=\\+\\[\\{\\]\\}\\\\\\|\\;\\:\\'\\\"\\,\\.\\<\\>\\\/\\s]+)" 48 | 49 | } 50 | -------------------------------------------------------------------------------- /syntaxes/fsharp.fsi.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fsharp.fsi", 3 | "scopeName": "source.fsharp.fsi", 4 | "fileTypes": [ 5 | "fsi" 6 | ], 7 | "foldingStartMarker": "", 8 | "foldingStopMarker": "", 9 | "patterns": [ 10 | { 11 | "include": "source.fsharp" 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /syntaxes/fsharp.fsx.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fsharp.fsx", 3 | "scopeName": "source.fsharp.fsx", 4 | "fileTypes": [ 5 | "fsx" 6 | ], 7 | "foldingStartMarker": "", 8 | "foldingStopMarker": "", 9 | "patterns": [ 10 | { 11 | "include": "source.fsharp" 12 | }, 13 | { 14 | "name": "preprocessor.source.fsharp.fsx", 15 | "match": "^#(load|r|I|time)" 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /tests/Expecto/Common.fs: -------------------------------------------------------------------------------- 1 | module FSharpLanguageServer.Tests.Common 2 | 3 | open System 4 | open System.IO 5 | 6 | let private findProjectRoot(start: DirectoryInfo): DirectoryInfo = 7 | seq { 8 | let mutable dir = start 9 | while dir <> dir.Root do 10 | for _ in dir.GetFiles "fsharp-language-server.sln" do 11 | yield dir 12 | dir <- dir.Parent 13 | } |> Seq.head 14 | let private testDirectory = DirectoryInfo(Directory.GetCurrentDirectory()) 15 | 16 | /// The root of the project folder 17 | let projectRoot = findProjectRoot(testDirectory) -------------------------------------------------------------------------------- /tests/Expecto/FSharpLanguageServer.Expecto.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | Exe 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /tests/Expecto/FsharpLanguageServer/FormattingTests.fs: -------------------------------------------------------------------------------- 1 | module FSharpLanguageServer.Tests.FormattingTests 2 | 3 | open FSharp.Compiler 4 | open FSharp.Compiler.EditorServices 5 | open FSharpLanguageServer.Tests.Common 6 | open FSharpLanguageServer 7 | open System 8 | open System.IO 9 | open LSP.Types 10 | open FSharp.Data 11 | open FSharp.Compiler.CodeAnalysis 12 | open ServerTests 13 | open Expecto 14 | 15 | 16 | let tests= 17 | testList "formatting" [ 18 | 19 | test "hover over function With alias type" { 20 | let client, server = createServerAndReadFile("MainProject", "Hover.fs") 21 | match server.Hover(textDocumentPosition("MainProject", "Hover.fs", 19, 6)) |> Async.RunSynchronously with 22 | | None -> failtest("No hover") 23 | | Some hover ->Expect.isNonEmpty hover.contents "Hover list is empty" 24 | 25 | } 26 | 27 | test "hover_over_System_function" { 28 | let client, server = createServerAndReadFile("MainProject", "Hover.fs") 29 | 30 | match server.Hover(textDocumentPosition("MainProject", "Hover.fs", 14, 36)) |> Async.RunSynchronously with 31 | | None -> failtest("No hover") 32 | | Some hover -> 33 | if List.isEmpty hover.contents then failtest("Hover list is empty") 34 | let matches=hover.contents|>List.filter(fun x-> 35 | let doc= 36 | match x with 37 | |PlainString(s)->s 38 | |HighlightedString(s,_)->s 39 | Expect.isFalse (doc.Contains "") "Documentation contains xml tag meaning it was not correctly formatted with xml tags removed" 40 | doc.Contains("Applies a function to each element of the collection, threading an accumulator argument") 41 | ) 42 | Expect.isNonEmpty matches "List does not contain required System function doc string" 43 | } 44 | 45 | test "hoverMethodParams"{ 46 | let client, server = createServerAndReadFile("MainProject", "Hover.fs") 47 | 48 | match server.Hover(textDocumentPosition("MainProject", "Hover.fs", 25, 19)) |> Async.RunSynchronously with 49 | | None -> failtest("No hover") 50 | | Some hover -> 51 | if List.isEmpty hover.contents then failtest("Hover list is empty") 52 | let matches=hover.contents|>List.filter(fun x-> 53 | let doc= 54 | match x with 55 | |PlainString(s)->s 56 | |HighlightedString(s,_)->s 57 | doc.Contains("The encrypted authorization message expected by the server.") 58 | ) 59 | Expect.isNonEmpty matches "List does not contain required function doc string" 60 | } 61 | 62 | test "hoverMDDocs"{ 63 | let client, server = createServerAndReadFile("MainProject", "Hover.fs") 64 | 65 | match server.Hover(textDocumentPosition("MainProject", "Hover.fs", 23, 10)) |> Async.RunSynchronously with 66 | | None -> failtest("No hover") 67 | | Some hover -> 68 | if List.isEmpty hover.contents then failtest("Hover list is empty") 69 | let matches=hover.contents|>List.filter(fun x-> 70 | let doc= 71 | match x with 72 | |PlainString(s)->s 73 | |HighlightedString(s,_)->s 74 | doc.Contains("This function has documentation") 75 | ) 76 | Expect.isNonEmpty matches "List does not contain required function doc string from a non-xml comment" 77 | } 78 | ] 79 | 80 | 81 | -------------------------------------------------------------------------------- /tests/Expecto/FsharpLanguageServer/ProjectManagerTests.fs: -------------------------------------------------------------------------------- 1 | module FSharpLanguageServer.Tests.ProjectManagerTests 2 | 3 | open FSharp.Compiler 4 | open FSharp.Compiler.EditorServices 5 | open FSharpLanguageServer.Tests.Common 6 | open FSharpLanguageServer 7 | open System 8 | open System.IO 9 | open LSP.Types 10 | open FSharp.Data 11 | open FSharp.Compiler.CodeAnalysis 12 | open Expecto 13 | open FSharpLanguageServer.ProjectManager.Manager 14 | open FSharpLanguageServer.ProjectManager 15 | open System.Diagnostics 16 | open LSP.Utils 17 | type MockClient() = 18 | member val Diagnostics = System.Collections.Generic.List() 19 | interface ILanguageClient with 20 | member this.PublishDiagnostics(p: PublishDiagnosticsParams): unit = 21 | () 22 | member this.ShowMessage(p: ShowMessageParams): unit = 23 | () 24 | member this.RegisterCapability(p: RegisterCapability): unit = 25 | () 26 | member this.CustomNotification(method: string, p: JsonValue): unit = 27 | () 28 | 29 | //setup 30 | 31 | LSP.Log.diagnosticsLog := stdout 32 | let tests=testSequenced<|testList "project cracking" [ 33 | 34 | test "find project file" { 35 | let projects = ProjectManager(FSharpChecker.Create(),true) 36 | let root = Path.Combine [|projectRoot.FullName; "sample"; "MainProject"|] |> DirectoryInfo 37 | Async.RunSynchronously(projects.AddWorkspaceRoot(root)) 38 | let file = normedFileInfo(Path.Combine [|projectRoot.FullName; "sample"; "MainProject"; "Hover.fs"|]) 39 | let project = projects.FindProjectOptions(file) 40 | match project with 41 | | Error(m) -> failtestf "%A" m 42 | | Ok(f) -> if not(f.ProjectFileName.EndsWith "MainProject.fsproj") then failtestf "%A" f 43 | } 44 | 45 | test "choose fsproj referenced by sln" { 46 | let projects = ProjectManager(FSharpChecker.Create(),true) 47 | let root = Path.Combine [|projectRoot.FullName; "sample"; "SlnReferences"|] |> DirectoryInfo 48 | Async.RunSynchronously(projects.AddWorkspaceRoot(root)) 49 | let file = normedFileInfo(Path.Combine [|projectRoot.FullName; "sample"; "SlnReferences"; "Main.fs"|]) 50 | let project = projects.FindProjectOptions(file) 51 | match project with 52 | | Error(m) -> failtestf "%A" m 53 | | Ok(f) -> if not(f.ProjectFileName.EndsWith "ReferencedProject.fsproj") then failtestf "%A" f 54 | } 55 | 56 | test "find script file" { 57 | let projects = ProjectManager(FSharpChecker.Create(),true) 58 | let root = Path.Combine [|projectRoot.FullName; "sample"; "Script"|] |> DirectoryInfo 59 | Async.RunSynchronously(projects.AddWorkspaceRoot(root)) 60 | let file = normedFileInfo(Path.Combine [|projectRoot.FullName; "sample"; "Script"; "LoadedByScript.fs"|]) 61 | let project = projects.FindProjectOptions(file) 62 | match project with 63 | | Error(m) -> failtestf "%A" m 64 | | Ok(f) -> if not(f.ProjectFileName.EndsWith("MainScript.fsx.fsproj")) then failtestf "%A" f 65 | } 66 | 67 | test "find an local dll" { 68 | let projects = ProjectManager(FSharpChecker.Create(),true) 69 | let root = Path.Combine [|projectRoot.FullName; "sample"; "HasLocalDll"|] |> DirectoryInfo 70 | Async.RunSynchronously(projects.AddWorkspaceRoot(root)) 71 | let file = normedFileInfo(Path.Combine [|projectRoot.FullName; "sample"; "HasLocalDll"; "Program.fs"|]) 72 | match projects.FindProjectOptions(file) with 73 | | Error(m) -> failtestf "%A" m 74 | | Ok(parsed) -> 75 | let isLocalDll(s: string) = s.EndsWith("IndirectDep.dll") 76 | if not (Seq.exists isLocalDll parsed.OtherOptions) then failtestf "%A" parsed.OtherOptions 77 | } 78 | 79 | test "project-file-not-found" { 80 | let projects = ProjectManager(FSharpChecker.Create(),true) 81 | let file = normedFileInfo(Path.Combine [|projectRoot.FullName; "sample"; "MainProject"; "Hover.fs"|]) 82 | let project = projects.FindProjectOptions file 83 | match project with 84 | | Ok(f) -> failtestf "Shouldn't have found project file %s" f.ProjectFileName 85 | | Error(_) -> () 86 | } 87 | 88 | test "bad project file" { 89 | let projects = ProjectManager(FSharpChecker.Create(),true) 90 | let root = Path.Combine [|projectRoot.FullName; "sample"; "BadProject"|] |> DirectoryInfo 91 | Async.RunSynchronously(projects.AddWorkspaceRoot root) 92 | } 93 | 94 | test "get script options" { 95 | let projects = ProjectManager(FSharpChecker.Create(),true) 96 | let script = Path.Combine [|projectRoot.FullName; "sample"; "Script"; "MainScript.fsx"|] |> FileInfo 97 | projects.NewProjectFile(script) 98 | match projects.FindProjectOptions(script) with 99 | | Error(m) -> failtestf "%A" m 100 | | Ok(options) -> 101 | let references = [for o in options.OtherOptions do if o.StartsWith("-r:") then yield normedFileInfo(o.Substring("-r:".Length)).Name] 102 | Expect.contains references "FSharp.Core.dll" "Missing ref" 103 | Expect.contains references "System.Runtime.dll" "Missing ref" 104 | } 105 | test "get cached ProjectOptions" { 106 | let projects = ProjectManager(FSharpChecker.Create(),true) 107 | let root = Path.Combine [|projectRoot.FullName; "sample"; "MainProject"|] |> DirectoryInfo 108 | Async.RunSynchronously(projects.AddWorkspaceRoot(root)) 109 | let file = normedFileInfo(Path.Combine [|projectRoot.FullName; "sample"; "MainProject"; "Hover.fs"|]) 110 | let project = projects.FindProjectOptions(file) 111 | match project with 112 | | Error(m) -> failtestf "Couldn't load project %A" m 113 | | Ok(f) -> 114 | 115 | let cacheJson= FileCache.tryGetCached(normedFileInfo(f.ProjectFileName)) 116 | Expect.isOk cacheJson (sprintf"Could not get the cached project data, reason: %A" cacheJson) 117 | } 118 | 119 | let dotnetBuild(proj)= 120 | let args = sprintf "build %s " proj 121 | let info = 122 | ProcessStartInfo( 123 | RedirectStandardOutput = true, 124 | RedirectStandardError = true, 125 | UseShellExecute = false, 126 | FileName = "dotnet", 127 | Arguments = args 128 | ) 129 | let p=Process.Start(info) 130 | 131 | p.OutputDataReceived.Add(fun args -> if args.Data <> null then printfn "Build: %A" args.Data) 132 | p.ErrorDataReceived.Add(fun args -> if args.Data <> null then printfn "Build Errors: %A" args.Data) 133 | if not(p.Start()) then 134 | failwithf "Failed dotnet %s" args 135 | p.BeginOutputReadLine() 136 | p.BeginErrorReadLine() 137 | p.WaitForExit() 138 | 139 | //There exists a problem where building a paket project will change the project.assets.json as compared to buildalyzer 140 | //this will cause the cache to be invalidated after every build and require rechecking of all files 141 | test "building doesn't interfere with cached ProjectOptions" { 142 | let projects = ProjectManager(FSharpChecker.Create(),true) 143 | let root = Path.Combine [|projectRoot.FullName; "sample"; "MainProject"|] |> DirectoryInfo 144 | Async.RunSynchronously(projects.AddWorkspaceRoot(root)) 145 | let file = normedFileInfo(Path.Combine [|projectRoot.FullName; "sample"; "MainProject"; "Hover.fs"|]) 146 | let project = projects.FindProjectOptions(file) 147 | 148 | 149 | match project with 150 | | Error(m) -> failtestf "Couldn't load project %A" m 151 | | Ok(f) -> 152 | 153 | dotnetBuild(f.ProjectFileName); 154 | let cacheJson= FileCache.tryGetCached(normedFileInfo(f.ProjectFileName)) 155 | Expect.isOk cacheJson (sprintf"Could not get the cached project data, reason: %A" cacheJson) 156 | 157 | } 158 | ] -------------------------------------------------------------------------------- /tests/Expecto/LSP/DocumentStoreTests.fs: -------------------------------------------------------------------------------- 1 | module LSP.Tests.DocumentStoreTests 2 | 3 | open System 4 | open System.Text 5 | open System.IO 6 | open LSP.Types 7 | open LSP 8 | 9 | 10 | LSP.Log.diagnosticsLog := stdout 11 | open Expecto 12 | 13 | let tests= 14 | testList "formatting" [ 15 | 16 | test "convert prefix Range to offsets"{ 17 | let text = "foo\r\n\ 18 | bar\r\n\ 19 | doh" 20 | let textBuilder = new StringBuilder(text) 21 | let range = 22 | { start = {line = 0; character = 0} 23 | ``end`` = {line = 0; character = 3} } 24 | let found = DocumentStoreUtils.findRange(textBuilder, range) 25 | Expect.equal (0, 3) found "not equal" 26 | } 27 | 28 | test "convert suffix Range to offsets"{ 29 | let text = "foo\r\n\ 30 | bar\r\n\ 31 | doh" 32 | let textBuilder = new StringBuilder(text) 33 | let range = 34 | { start = {line = 2; character = 1} 35 | ``end`` = {line = 2; character = 3} } 36 | let found = DocumentStoreUtils.findRange(textBuilder, range) 37 | Expect.equal (11, 13) found "not equal" 38 | } 39 | 40 | 41 | test "convert line-spanning Range to offsets"{ 42 | let text = "foo\r\n\ 43 | bar\r\n\ 44 | doh" 45 | let textBuilder = new StringBuilder(text) 46 | let range = 47 | { start = {line = 1; character = 2} 48 | ``end`` = {line = 2; character = 1} } 49 | let found = DocumentStoreUtils.findRange(textBuilder, range) 50 | Expect.equal (7, 11) found "not equal" 51 | } 52 | let exampleUri = Uri("file://" + Directory.GetCurrentDirectory() + "example.txt") 53 | 54 | 55 | test "open document"{ 56 | let store = DocumentStore() 57 | let exampleUri = exampleUri 58 | let helloWorld = "Hello world!" 59 | let openDoc: DidOpenTextDocumentParams = 60 | { textDocument = 61 | { uri = exampleUri 62 | languageId = "plaintext" 63 | version = 1 64 | text = helloWorld } } 65 | store.Open(openDoc) 66 | let found = store.GetText(normedFileInfo(exampleUri.LocalPath)) 67 | Expect.equal (Some(helloWorld) ) found "not equal" 68 | } 69 | let helloStore()= 70 | let store = DocumentStore() 71 | let helloWorld = "Hello world!" 72 | let openDoc: DidOpenTextDocumentParams = 73 | { textDocument = 74 | { uri = exampleUri 75 | languageId = "plaintext" 76 | version = 1 77 | text = helloWorld } } 78 | store.Open(openDoc) 79 | store 80 | 81 | 82 | test "replace a document"{ 83 | let store = helloStore() 84 | let newText = "Replaced everything" 85 | let replaceAll: DidChangeTextDocumentParams = 86 | { textDocument = 87 | { uri = exampleUri 88 | version = 2 } 89 | contentChanges = 90 | [ { range = None 91 | rangeLength = None 92 | text = newText } ] } 93 | store.Change(replaceAll) 94 | let found = store.GetText(normedFileInfo(exampleUri.LocalPath)) 95 | Expect.equal (Some(newText)) found "not equal" 96 | } 97 | 98 | test "patch a document"{ 99 | let store = helloStore() 100 | let newText = "George" 101 | let replaceAll: DidChangeTextDocumentParams = 102 | { textDocument = 103 | { uri = exampleUri 104 | version = 2 } 105 | contentChanges = 106 | [ { 107 | range = Some { 108 | start = {line = 0; character = 6} 109 | ``end`` = {line = 0; character = 11} } 110 | rangeLength = None 111 | text = newText } ] 112 | } 113 | store.Change(replaceAll) 114 | let found = store.GetText(normedFileInfo(exampleUri.LocalPath)) 115 | Expect.equal (Some("Hello George!")) found "not equal" 116 | } 117 | ] -------------------------------------------------------------------------------- /tests/Expecto/LSP/JsonTests.fs: -------------------------------------------------------------------------------- 1 | module LSP.Tests.JsonTests 2 | 3 | open System 4 | open System.Text.RegularExpressions 5 | open FSharp.Data 6 | open LSP.Json.Ser 7 | open Expecto 8 | 9 | LSP.Log.diagnosticsLog := stdout 10 | 11 | let removeSpace(expected: string) = 12 | Regex.Replace(expected, @"\s", "") 13 | 14 | let tests= 15 | testList "Json Tests" [ 16 | test "remove space from string"{ 17 | let found = removeSpace("foo bar") 18 | Expect.equal "foobar" found "fail" 19 | } 20 | 21 | test "remove newline from string"{ 22 | let actual = """foo 23 | bar""" 24 | let found = removeSpace(actual) 25 | Expect.equal "foobar" found "fail" 26 | } 27 | 28 | test "serialize primitive types to JSON"{ 29 | let found = serializerFactory defaultJsonWriteOptions true 30 | Expect.equal "true" found "fail" 31 | let found = serializerFactory defaultJsonWriteOptions 1 32 | Expect.equal "1" found "fail" 33 | let found = serializerFactory defaultJsonWriteOptions "foo" 34 | Expect.equal "\"foo\"" found "fail" 35 | let found = serializerFactory defaultJsonWriteOptions 'f' 36 | Expect.equal "\"f\"" found "fail" 37 | } 38 | 39 | test "serialize URI to JSON"{ 40 | let example = Uri("https://google.com") 41 | let found = serializerFactory defaultJsonWriteOptions example 42 | Expect.equal "\"https://google.com/\"" found "fail" 43 | } 44 | 45 | test "serialize JsonValue to JSON"{ 46 | let example = JsonValue.Parse "{}" 47 | let found = serializerFactory defaultJsonWriteOptions example 48 | Expect.equal "{}" found "fail" 49 | } 50 | 51 | test "serialize option to JSON"{ 52 | let found = serializerFactory defaultJsonWriteOptions (Some 1) 53 | Expect.equal "1" found "fail" 54 | let found = serializerFactory defaultJsonWriteOptions (None) 55 | Expect.equal "null" found "fail" 56 | } 57 | ] 58 | 59 | type SimpleRecord = {simpleMember: int} 60 | 61 | let tests2= 62 | testList "Json Tests2" [ 63 | test "serialize record to JSON"{ 64 | let record = {simpleMember = 1} 65 | let found = serializerFactory defaultJsonWriteOptions record 66 | Expect.equal """{"simpleMember":1}""" found "fail" 67 | } 68 | 69 | test "serialize list of ints to JSON"{ 70 | let example = [1; 2] 71 | let found = serializerFactory defaultJsonWriteOptions example 72 | Expect.equal """[1,2]""" found "fail" 73 | } 74 | 75 | test "serialize list of strings to JSON"{ 76 | let example = ["foo"; "bar"] 77 | let found = serializerFactory defaultJsonWriteOptions example 78 | Expect.equal """["foo","bar"]""" found "fail" 79 | } 80 | 81 | test "serialize a record with a custom writer"{ 82 | let record = {simpleMember = 1} 83 | let customWriter(r: SimpleRecord): string = sprintf "simpleMember=%d" r.simpleMember 84 | let options = {defaultJsonWriteOptions with customWriters = [customWriter]} 85 | let found = serializerFactory options record 86 | Expect.equal "\"simpleMember=1\"" found "fail" 87 | } 88 | ] 89 | 90 | type Foo= Bar | Doh 91 | type FooRecord = {foo: Foo} 92 | 93 | let tests3 = 94 | testList "Json Tests3" [ 95 | test "serialize a union with a custom writer"{ 96 | let record = {foo = Bar} 97 | let customWriter = function 98 | | Bar -> 10 99 | | Doh -> 20 100 | let options = {defaultJsonWriteOptions with customWriters = [customWriter]} 101 | let found = serializerFactory options record 102 | Expect.equal """{"foo":10}""" found "fail" 103 | } 104 | ] 105 | // type UnionWithFields = 106 | // | OptionA of A: string 107 | // | OptionB of int 108 | 109 | // 110 | // let ``serialize union with fields`` () = 111 | // let options = defaultJsonReadOptions 112 | // let serializer = serializerFactory 113 | // let found = serializer options (OptionA "foo") 114 | // Assert.AreEqual("""{"A":"foo"}""", found) 115 | // let serializer = serializerFactory 116 | // let found = serializer options (OptionB 1) 117 | // Assert.AreEqual("""{"A":[1]}""", found) 118 | 119 | type IFoo = 120 | abstract member Foo: unit -> string 121 | type MyFoo() = 122 | interface IFoo with 123 | member this.Foo() = "foo" 124 | 125 | type SimpleTypes = { 126 | b: bool 127 | i: int 128 | c: char 129 | s: string 130 | webUri: Uri 131 | fileUri: Uri 132 | } 133 | type NestedField = { 134 | oneField: int 135 | } 136 | 137 | type ComplexTypes = { 138 | nested: NestedField 139 | intList: int list 140 | stringAsInt: int 141 | intOptionPresent: int option 142 | intOptionAbsent: int option 143 | } 144 | type TestOptionalRead = { 145 | optionField: int option 146 | } 147 | type TestEnum = One | Two 148 | 149 | let deserializeTestEnum(i: int) = 150 | match i with 151 | | 1 -> One 152 | | 2 -> Two 153 | 154 | type ContainsEnum = { 155 | e: TestEnum 156 | } 157 | 158 | let tests4 = 159 | testList "Json Tests4" [ 160 | test "serialize an interface with a custom writer"{ 161 | let customWriter(foo: IFoo): string = 162 | foo.Foo() 163 | let options = {defaultJsonWriteOptions with customWriters = [customWriter]} 164 | let example = MyFoo() 165 | let found = serializerFactory options example 166 | Expect.equal "\"foo\"" found "fail" 167 | let found = serializerFactory options example 168 | Expect.equal "\"foo\"" found "fail" 169 | } 170 | 171 | 172 | test "deserialize simple types"{ 173 | let sample = """ 174 | { 175 | "b": true, 176 | "i": 1, 177 | "c": "x", 178 | "s": "foo", 179 | "webUri": "https://github.com", 180 | "fileUri": "file:///d%3A/foo.txt" 181 | }""" 182 | let options = defaultJsonReadOptions 183 | let found = deserializerFactory options (JsonValue.Parse sample) 184 | Expect.equal true found.b "fail" 185 | Expect.equal 1 found.i "fail" 186 | Expect.equal 'x' found.c "fail" 187 | Expect.equal "foo" found.s "fail" 188 | Expect.equal (Uri("https://github.com")) found.webUri "fail" 189 | Expect.equal "d:\\foo.txt" found.fileUri.LocalPath "fail" 190 | } 191 | 192 | 193 | test "deserialize complex types"{ 194 | let sample = """ 195 | { 196 | "nested": { 197 | "oneField": 1 198 | }, 199 | "intList": [1], 200 | "stringAsInt": "1", 201 | "intOptionPresent": 1, 202 | "intOptionAbsent": null 203 | }""" 204 | let options = defaultJsonReadOptions 205 | let found = deserializerFactory options (JsonValue.Parse sample) 206 | Expect.equal {oneField=1} found.nested "fail" 207 | Expect.equal 1 found.stringAsInt "fail" 208 | Expect.equal [1] found.intList "fail" 209 | Expect.equal (Some 1) found.intOptionPresent "fail" 210 | Expect.equal None found.intOptionAbsent "fail" 211 | } 212 | 213 | 214 | test "deserialize optional types"{ 215 | let options = defaultJsonReadOptions 216 | let found = deserializerFactory options (JsonValue.Parse """{"optionField":1}""") 217 | Expect.equal {optionField=Some 1} found "fail" 218 | let found = deserializerFactory options (JsonValue.Parse """{"optionField":null}""") 219 | Expect.equal {optionField=None} found "fail" 220 | let found = deserializerFactory options (JsonValue.Parse """{}""") 221 | Expect.equal {optionField=None} found "fail" 222 | let found = deserializerFactory options (JsonValue.Parse """[1]""") 223 | Expect.equal [Some 1] found "fail" 224 | let found = deserializerFactory options (JsonValue.Parse """[null]""") 225 | let noneIntList: int option list=[None] 226 | Expect.equal noneIntList found "fail" 227 | } 228 | 229 | test "deserialize map"{ 230 | let options = defaultJsonReadOptions 231 | let found = deserializerFactory> options (JsonValue.Parse """{"k":1}""") 232 | let map = Map.add "k" 1 Map.empty 233 | Expect.equal map found "fail" 234 | } 235 | 236 | test "deserialize enum"{ 237 | let options = { defaultJsonReadOptions with customReaders = [deserializeTestEnum]} 238 | let found = deserializerFactory options (JsonValue.Parse """{"e":1}""") 239 | Expect.equal One found.e "fail" 240 | } 241 | ] -------------------------------------------------------------------------------- /tests/Expecto/LSP/LanguageServerTests.fs: -------------------------------------------------------------------------------- 1 | module LSP.Tests.LanguageServerTests 2 | 3 | open System 4 | open System.IO 5 | open System.Text 6 | open FSharp.Data 7 | open LSP.Types 8 | open LSP.SemanticToken 9 | open LSP 10 | open Expecto 11 | 12 | 13 | 14 | let binaryWriter() = 15 | let stream = new MemoryStream() 16 | let writer = new BinaryWriter(stream) 17 | let toString() = 18 | let bytes = stream.ToArray() 19 | Encoding.UTF8.GetString(bytes) 20 | writer, toString 21 | 22 | let tests = 23 | testList "language server Tests" [ 24 | test "write text"{ 25 | let writer, toString = binaryWriter() 26 | writer.Write(Encoding.UTF8.GetBytes "foo") 27 | let found = toString() 28 | Expect.equal "foo" found "Wrong text" 29 | } 30 | 31 | test "write response"{ 32 | let writer, toString = binaryWriter() 33 | LanguageServer.respond(writer, 1, "2") 34 | let expected = "Content-Length: 35\r\n\r\n\ 35 | {\"id\":1,\"jsonrpc\":\"2.0\",\"result\":2}" 36 | let found = toString() 37 | Expect.equal expected found "wrong response" 38 | } 39 | 40 | test "write multibyte characters"{ 41 | let writer, toString = binaryWriter() 42 | LanguageServer.respond(writer, 1, "🔥") 43 | let expected = "Content-Length: 38\r\n\r\n\ 44 | {\"id\":1,\"jsonrpc\":\"2.0\",\"result\":🔥}" 45 | let found = toString() 46 | Expect.equal expected found "wrong text" 47 | } 48 | ] 49 | let TODO() = raise (Exception "TODO") 50 | 51 | type MockServer() = 52 | interface ILanguageServer with 53 | member this.Initialize(p: InitializeParams): Async = 54 | async { 55 | return { capabilities = defaultServerCapabilities } 56 | } 57 | member this.Initialized(): Async = TODO() 58 | member this.Shutdown(): Async = TODO() 59 | member this.DidChangeConfiguration(p: DidChangeConfigurationParams): Async = TODO() 60 | member this.DidOpenTextDocument(p: DidOpenTextDocumentParams): Async = TODO() 61 | member this.DidChangeTextDocument(p: DidChangeTextDocumentParams): Async = TODO() 62 | member this.WillSaveTextDocument(p: WillSaveTextDocumentParams): Async = TODO() 63 | member this.WillSaveWaitUntilTextDocument(p: WillSaveTextDocumentParams): Async = TODO() 64 | member this.DidSaveTextDocument(p: DidSaveTextDocumentParams): Async = TODO() 65 | member this.DidCloseTextDocument(p: DidCloseTextDocumentParams): Async = TODO() 66 | member this.DidChangeWatchedFiles(p: DidChangeWatchedFilesParams): Async = TODO() 67 | member this.Completion(p: TextDocumentPositionParams): Async = TODO() 68 | member this.Hover(p: TextDocumentPositionParams): Async = TODO() 69 | member this.ResolveCompletionItem(p: CompletionItem): Async = TODO() 70 | member this.SignatureHelp(p: TextDocumentPositionParams): Async = TODO() 71 | member this.GotoDefinition(p: TextDocumentPositionParams): Async = TODO() 72 | member this.FindReferences(p: ReferenceParams): Async = TODO() 73 | member this.DocumentHighlight(p: TextDocumentPositionParams): Async = TODO() 74 | member this.DocumentSymbols(p: DocumentSymbolParams): Async = TODO() 75 | member this.WorkspaceSymbols(p: WorkspaceSymbolParams): Async = TODO() 76 | member this.CodeActions(p: CodeActionParams): Async = TODO() 77 | member this.CodeLens(p: CodeLensParams): Async = TODO() 78 | member this.ResolveCodeLens(p: CodeLens): Async = TODO() 79 | member this.DocumentLink(p: DocumentLinkParams): Async = TODO() 80 | member this.ResolveDocumentLink(p: DocumentLink): Async = TODO() 81 | member this.DocumentFormatting(p: DocumentFormattingParams): Async = TODO() 82 | member this.DocumentRangeFormatting(p: DocumentRangeFormattingParams): Async = TODO() 83 | member this.DocumentOnTypeFormatting(p: DocumentOnTypeFormattingParams): Async = TODO() 84 | member this.Rename(p: RenameParams): Async = TODO() 85 | member this.ExecuteCommand(p: ExecuteCommandParams): Async = TODO() 86 | member this.DidChangeWorkspaceFolders(p: DidChangeWorkspaceFoldersParams): Async = TODO() 87 | member this.SemanticTokensFull (p: SemanticTokensParams) : Async=TODO() 88 | member this.SemanticTokensFullDelta (p: SemanticTokensDeltaParams): Async=TODO() 89 | 90 | member this.SemanticTokensRange (p: SemanticTokensRangeParams): Async=TODO() 91 | let messageStream(messages: string list): BinaryReader = 92 | let stdin = new MemoryStream() 93 | for m in messages do 94 | let trim = m.Trim() 95 | let length = Encoding.UTF8.GetByteCount(trim) 96 | let wrapper = sprintf "Content-Length: %d\r\n\r\n%s" length trim 97 | let bytes = Encoding.UTF8.GetBytes(wrapper) 98 | stdin.Write(bytes, 0, bytes.Length) 99 | stdin.Seek(int64 0, SeekOrigin.Begin) |> ignore 100 | new BinaryReader(stdin) 101 | 102 | let initializeMessage = """ 103 | { 104 | "jsonrpc": "2.0", 105 | "id": 1, 106 | "method": "initialize", 107 | "params": {} 108 | } 109 | """ 110 | 111 | let exitMessage = """ 112 | { 113 | "jsonrpc": "2.0", 114 | "method": "exit" 115 | } 116 | """ 117 | let mock(server: ILanguageServer) (messages: string list): string = 118 | let stdout = new MemoryStream() 119 | let writeOut = new BinaryWriter(stdout) 120 | let readIn = messageStream(messages) 121 | let serverFactory = fun _ -> server 122 | LanguageServer.connect(serverFactory, readIn, writeOut, false) 123 | Encoding.UTF8.GetString(stdout.ToArray()) 124 | 125 | let tests2 = 126 | testList "language server Tests" [ 127 | 128 | test "read messages from a stream"{ 129 | let stdin = messageStream [initializeMessage] 130 | let messages = LanguageServer.readMessages(stdin) 131 | let found = Seq.toList(messages) 132 | Expect.equal [Parser.RequestMessage(1, "initialize", JsonValue.Parse "{}")] found "stream not terminated" 133 | } 134 | 135 | 136 | test "exit message terminates stream"{ 137 | let stdin = messageStream [initializeMessage; exitMessage; initializeMessage] 138 | let messages = LanguageServer.readMessages(stdin) 139 | let found = Seq.toList messages 140 | Expect.equal [Parser.RequestMessage(1, "initialize", JsonValue.Parse "{}")] found "stream not terminated" 141 | } 142 | 143 | test "end of bytes terminates stream"{ 144 | let stdin = messageStream [initializeMessage] 145 | let messages = LanguageServer.readMessages(stdin) 146 | let found = Seq.toList messages 147 | Expect.equal [Parser.RequestMessage(1, "initialize", JsonValue.Parse "{}")] found "stream not terminated" 148 | } 149 | test "send Initialize"{ 150 | let message = """ 151 | { 152 | "jsonrpc": "2.0", 153 | "id": 1, 154 | "method": "initialize", 155 | "params": {"processId": null,"rootUri":null,"capabilities":{}} 156 | } 157 | """ 158 | let server = MockServer() 159 | let result = mock server [message] 160 | if not (result.Contains("capabilities")) then failtestf "%A does not contain capabilities" result 161 | } 162 | ] -------------------------------------------------------------------------------- /tests/Expecto/LSP/TokenizerTests.fs: -------------------------------------------------------------------------------- 1 | module LSP.Tests.TokenizerTests 2 | 3 | open System.IO 4 | open System.Text 5 | open Expecto 6 | open LSP 7 | LSP.Log.diagnosticsLog := stdout 8 | 9 | 10 | let tests = 11 | testList "Parser test" [ 12 | 13 | test "parse content length header"{ 14 | let sample = "Content-Length: 10" 15 | let found = Tokenizer.parseHeader sample 16 | Expect.equal (Tokenizer.ContentLength 10) found "not equal" 17 | } 18 | 19 | test "parse content type header"{ 20 | let sample = "Content-Type: application/vscode-jsonrpc; charset=utf-8" 21 | let found = Tokenizer.parseHeader sample 22 | Expect.equal Tokenizer.OtherHeader found "not equal" 23 | } 24 | 25 | test "parse empty line indicating start of message"{ 26 | let found = Tokenizer.parseHeader "" 27 | Expect.equal Tokenizer.EmptyHeader found "not equal" 28 | } 29 | let binaryReader(sample: string): BinaryReader = 30 | let bytes = Encoding.UTF8.GetBytes(sample) 31 | let stream = new MemoryStream(bytes) 32 | new BinaryReader(stream, Encoding.UTF8) 33 | 34 | 35 | test "take header token"{ 36 | let sample = "Line 1\r\n\ 37 | Line 2" 38 | let found = Tokenizer.readLine (binaryReader sample) 39 | Expect.equal (Some "Line 1") found "not equal" 40 | } 41 | 42 | test "allow newline without carriage-return"{ 43 | let sample = "Line 1\n\ 44 | Line 2" 45 | let found = Tokenizer.readLine (binaryReader sample) 46 | Expect.equal (Some "Line 1") found "not equal" 47 | } 48 | 49 | test "take message token"{ 50 | let sample = "{}\r\n\ 51 | next line..." 52 | let found = Tokenizer.readLength(2, binaryReader(sample)) 53 | Expect.equal "{}" found "not equal" 54 | } 55 | 56 | test "tokenize stream"{ 57 | let sample = "Content-Length: 2\r\n\ 58 | \r\n\ 59 | {}\ 60 | Content-Length: 1\r\n\ 61 | \r\n\ 62 | 1" 63 | let found = Tokenizer.tokenize (binaryReader sample) |> Seq.toList 64 | Expect.equal ["{}"; "1"] found "not equal" 65 | } 66 | 67 | test "tokenize stream with multibyte characters"{ 68 | let sample = "Content-Length: 5\r\n\ 69 | \r\n\ 70 | _🔥\ 71 | Content-Length: 5\r\n\ 72 | \r\n\ 73 | _🐼" 74 | let found = Tokenizer.tokenize (binaryReader sample) |> Seq.toList 75 | Expect.equal ["_🔥"; "_🐼"] found "not equal" 76 | } 77 | 78 | ] -------------------------------------------------------------------------------- /tests/Expecto/Program.fs: -------------------------------------------------------------------------------- 1 | module FSharpLanguageServer.Tests.Main 2 | open Expecto 3 | 4 | 5 | [] 6 | let mainTests= 7 | 8 | testSequenced<|testList "All" [ 9 | testSequenced<|testList "LanguageServer" [ 10 | ServerTests.serverTests 11 | ProjectManagerTests.tests 12 | FormattingTests.tests 13 | ] 14 | testList "ProjectCracker" [ 15 | ProjectCrackerTests.crackingTests 16 | ProjectCrackerTests.tests2 17 | ] 18 | testList "LSPTests" [ 19 | LSP.Tests.DocumentStoreTests.tests 20 | LSP.Tests.JsonTests.tests 21 | LSP.Tests.JsonTests.tests2 22 | LSP.Tests.JsonTests.tests3 23 | LSP.Tests.JsonTests.tests4 24 | LSP.Tests.LanguageServerTests.tests 25 | LSP.Tests.LanguageServerTests.tests2 26 | LSP.Tests.ParserTests.tests 27 | LSP.Tests.TokenizerTests.tests 28 | ] 29 | ] 30 | 31 | 32 | [] 33 | let main args = 34 | runTestsInAssemblyWithCLIArgs [] args 35 | 36 | 37 | -------------------------------------------------------------------------------- /tests/Expecto/paket.references: -------------------------------------------------------------------------------- 1 | YoloDev.Expecto.TestSdk 2 | Expecto 3 | 4 | 5 | 6 | 7 | Microsoft.Build 8 | Microsoft.Build.Framework 9 | Microsoft.Build.Locator 10 | Microsoft.Build.Tasks.Core 11 | Microsoft.Build.Utilities.Core copy_local:true 12 | Microsoft.SourceLink.GitHub -------------------------------------------------------------------------------- /tests/ProjectInfo/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using Microsoft.Build.Evaluation; 5 | using Microsoft.Build.Logging; 6 | using Microsoft.Build.Framework; 7 | using System.Reflection; 8 | 9 | namespace FSharpLanguageServer { 10 | class ProjectInfo { 11 | static void Main(string[] args) { 12 | var basePath = "/usr/local/share/dotnet/sdk/2.1.300"; 13 | Environment.SetEnvironmentVariable("MSBuildSDKsPath", Path.Combine(basePath, "Sdks")); 14 | Environment.SetEnvironmentVariable("MSBUILD_EXE_PATH", Path.Combine(basePath, "MSBuild.dll")); 15 | var globalProperties = new Dictionary(); 16 | globalProperties.Add("DesignTimeBuild", "true"); 17 | globalProperties.Add("BuildingInsideVisualStudio", "true"); 18 | globalProperties.Add("BuildProjectReferences", "false"); 19 | globalProperties.Add("_ResolveReferenceDependencies", "true"); 20 | globalProperties.Add("SolutionDir", "/Users/georgefraser/Documents/fsharp-language-server/sample"); 21 | // Setting this property will cause any XAML markup compiler tasks to run in the 22 | // current AppDomain, rather than creating a new one. This is important because 23 | // our AppDomain.AssemblyResolve handler for MSBuild will not be connected to 24 | // the XAML markup compiler's AppDomain, causing the task not to be able to find 25 | // MSBuild. 26 | globalProperties.Add("AlwaysCompileMarkupFilesInSeparateDomain", "false"); 27 | // This properties allow the design-time build to handle the Compile target without actually invoking the compiler. 28 | // See https://github.com/dotnet/roslyn/pull/4604 for details. 29 | globalProperties.Add("ProvideCommandLineArgs", "true"); 30 | globalProperties.Add("SkipCompilerExecution", "true" ); 31 | var projectCollection = new ProjectCollection(globalProperties); 32 | var project = projectCollection.LoadProject("/Users/georgefraser/Documents/fsharp-language-server/sample/ReferenceCSharp/ReferenceCSharp.fsproj"); 33 | var projectInstance = project.CreateProjectInstance(); 34 | var buildResult = projectInstance.Build(new string[] { "Compile", "CoreCompile" }, new ILogger[]{ new ConsoleLogger() }); 35 | String type = ""; 36 | foreach (var item in projectInstance.Items) { 37 | if (type != item.ItemType) { 38 | type = item.ItemType; 39 | Console.WriteLine(type); 40 | } 41 | Console.WriteLine(" " + item.EvaluatedInclude); 42 | } 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /tests/ProjectInfo/ProjectInfo.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | AnyCPU 6 | Exe 7 | 8 | 9 | 10 | 15.6.82 11 | 4.3.0 12 | 2.8.0 13 | 2.3.1 14 | 15 | 0.2.0 16 | 0.6.0 17 | 0.6.0 18 | 1.1.0 19 | 1.1.0 20 | 1.1.0 21 | 1.1.0 22 | $(MSBuildPackageVersion) 23 | $(MSBuildPackageVersion) 24 | $(MSBuildPackageVersion) 25 | $(MSBuildPackageVersion) 26 | $(RoslynPackageVersion) 27 | $(RoslynPackageVersion) 28 | $(RoslynPackageVersion) 29 | $(RoslynPackageVersion) 30 | $(RoslynPackageVersion) 31 | $(RoslynPackageVersion) 32 | 1.1.0 33 | 1.1.0 34 | 1.1.0 35 | 1.1.0 36 | 1.1.0 37 | 1.1.0 38 | 1.1.0 39 | 1.1.0 40 | 2.0.0 41 | 1.1.0 42 | 1.1.0 43 | 1.1.0 44 | 1.1.0 45 | 1.1.0 46 | 1.1.0 47 | 1.1.0 48 | 15.0.0 49 | 15.3.0 50 | 1.14.114 51 | 15.0.12 52 | 9.0.1 53 | $(NuGetPackageVersion) 54 | $(NuGetPackageVersion) 55 | $(NuGetPackageVersion) 56 | $(NuGetPackageVersion) 57 | 1.4.0 58 | 1.0.31 59 | 1.4.2 60 | 4.6.0 61 | 4.3.0 62 | $(XunitPackageVersion) 63 | $(XunitPackageVersion) 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /tests/Scratch/Program.fs: -------------------------------------------------------------------------------- 1 | module ProjectCrackerMain 2 | 3 | open System 4 | open System.IO 5 | open System.Collections.Generic 6 | open ProjInfoCracker 7 | [] 8 | let main(argv: array): int = 9 | let r=crackProj(Path.GetFullPath"C:\\Users\\Eli\\Documents\\programming\\FSharp\\testProj\\testProj.fsproj") 10 | 11 | 1 -------------------------------------------------------------------------------- /tests/Scratch/Scratch.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | AnyCPU 6 | Exe 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /tests/Scratch/paket.references: -------------------------------------------------------------------------------- 1 | 2 | FSharp.Core -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "noUnusedLocals": true, 4 | "noUnusedParameters": true, 5 | "noImplicitAny": true, 6 | "noImplicitReturns": true, 7 | "target": "es2019", 8 | "module": "commonjs", 9 | "moduleResolution": "node", 10 | "rootDir": ".", 11 | "outDir": "out", 12 | "lib": [ "es2019" ], 13 | "sourceMap": true 14 | }, 15 | "exclude": [ 16 | "node_modules", 17 | "server", 18 | "Scratch.ts" 19 | ] 20 | } -------------------------------------------------------------------------------- /videos/Autocomplete.mov.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsprojects/fsharp-language-server/471439c562c484f2fe57adfd54485e7c1ee4ca29/videos/Autocomplete.mov.gif -------------------------------------------------------------------------------- /videos/DebugTest.mov.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsprojects/fsharp-language-server/471439c562c484f2fe57adfd54485e7c1ee4ca29/videos/DebugTest.mov.gif -------------------------------------------------------------------------------- /videos/DocumentSymbols.mov.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsprojects/fsharp-language-server/471439c562c484f2fe57adfd54485e7c1ee4ca29/videos/DocumentSymbols.mov.gif -------------------------------------------------------------------------------- /videos/EmacsLspMode.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsprojects/fsharp-language-server/471439c562c484f2fe57adfd54485e7c1ee4ca29/videos/EmacsLspMode.gif -------------------------------------------------------------------------------- /videos/FindReferences.mov.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsprojects/fsharp-language-server/471439c562c484f2fe57adfd54485e7c1ee4ca29/videos/FindReferences.mov.gif -------------------------------------------------------------------------------- /videos/GoToDefinition.mov.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsprojects/fsharp-language-server/471439c562c484f2fe57adfd54485e7c1ee4ca29/videos/GoToDefinition.mov.gif -------------------------------------------------------------------------------- /videos/Hover.mov.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsprojects/fsharp-language-server/471439c562c484f2fe57adfd54485e7c1ee4ca29/videos/Hover.mov.gif -------------------------------------------------------------------------------- /videos/LSP-vs-Ionide-Warm.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsprojects/fsharp-language-server/471439c562c484f2fe57adfd54485e7c1ee4ca29/videos/LSP-vs-Ionide-Warm.gif -------------------------------------------------------------------------------- /videos/RenameSymbol.mov.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsprojects/fsharp-language-server/471439c562c484f2fe57adfd54485e7c1ee4ca29/videos/RenameSymbol.mov.gif -------------------------------------------------------------------------------- /videos/ShowErrors.mov.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsprojects/fsharp-language-server/471439c562c484f2fe57adfd54485e7c1ee4ca29/videos/ShowErrors.mov.gif -------------------------------------------------------------------------------- /videos/SignatureHelp.mov.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsprojects/fsharp-language-server/471439c562c484f2fe57adfd54485e7c1ee4ca29/videos/SignatureHelp.mov.gif -------------------------------------------------------------------------------- /videos/VimDeoplete.mov.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsprojects/fsharp-language-server/471439c562c484f2fe57adfd54485e7c1ee4ca29/videos/VimDeoplete.mov.gif -------------------------------------------------------------------------------- /videos/WorkspaceSymbols.mov.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsprojects/fsharp-language-server/471439c562c484f2fe57adfd54485e7c1ee4ca29/videos/WorkspaceSymbols.mov.gif --------------------------------------------------------------------------------