├── .config
└── dotnet-tools.json
├── .editorconfig
├── .fantomasignore
├── .github
├── FUNDING.yml
├── dependabot.yml
└── workflows
│ ├── main.yml
│ └── pr.yml
├── .gitignore
├── .gitpod.yml
├── Directory.Build.props
├── LICENSE
├── README.md
├── build.fsx
├── fantomas-tools.sln
├── global.json
├── infrastructure
├── .gitignore
├── Program.fs
├── Pulumi.main.yaml
├── Pulumi.yaml
├── infrastructure.fsproj
├── packages.lock.json
└── scratch.ps1
└── src
├── client
├── .gitignore
├── .prettierignore
├── .prettierrc
├── LICENSE
├── README.md
├── bun.lockb
├── fsharp
│ ├── ASTViewer
│ │ ├── Decoders.fs
│ │ ├── Encoders.fs
│ │ ├── Model.fs
│ │ ├── State.fs
│ │ ├── State.fsi
│ │ ├── View.fs
│ │ └── View.fsi
│ ├── App.fs
│ ├── BubbleMessage.fs
│ ├── Editor.fs
│ ├── FantomasOnline
│ │ ├── Decoders.fs
│ │ ├── Encoders.fs
│ │ ├── Model.fs
│ │ ├── State.fs
│ │ ├── View.fs
│ │ └── View.fsi
│ ├── FantomasTools.fsproj
│ ├── Http.fs
│ ├── Loader.fs
│ ├── Model.fs
│ ├── Navigation.fs
│ ├── Oak
│ │ ├── Decoders.fs
│ │ ├── Encoders.fs
│ │ ├── Graph.fs
│ │ ├── GraphView.fs
│ │ ├── Model.fs
│ │ ├── State.fs
│ │ ├── View.fs
│ │ └── View.fsi
│ ├── SettingControls.fs
│ ├── State.fs
│ ├── Style.fs
│ ├── UrlTools.fs
│ ├── Utils.fs
│ ├── VersionBar.fs
│ ├── View.fs
│ └── packages.lock.json
├── index.html
├── package.json
├── public
│ ├── debug.html
│ ├── fantomas_logo.png
│ ├── logo.png
│ └── robots.txt
├── src
│ ├── index.jsx
│ └── styles
│ │ └── style.css
└── vite.config.js
├── server
├── ASTViewer
│ ├── ASTViewer.fsproj
│ ├── Decoders.fs
│ ├── Encoders.fs
│ ├── ExpandedAST.fs
│ ├── ExpandedAST.fsi
│ ├── GetAST.fs
│ ├── Lambda.fs
│ ├── Program.fs
│ └── packages.lock.json
├── AWSLambdaExtensions.fs
├── FantomasOnline.Shared
│ ├── Decoders.fs
│ ├── Encoders.fs
│ └── Http.fs
├── FantomasOnlineMain
│ ├── .gitignore
│ ├── FantomasOnlineMain.fsproj
│ ├── FormatCode.fs
│ ├── Lambda.fs
│ ├── Program.fs
│ ├── host.json
│ └── packages.lock.json
├── FantomasOnlinePreview
│ ├── .gitignore
│ ├── FantomasOnlinePreview.fsproj
│ ├── FormatCode.fs
│ ├── Lambda.fs
│ ├── Program.fs
│ ├── host.json
│ └── packages.lock.json
├── FantomasOnlineV5
│ ├── .gitignore
│ ├── FantomasOnlineV5.fsproj
│ ├── FormatCode.fs
│ ├── Lambda.fs
│ ├── Program.fs
│ ├── host.json
│ └── packages.lock.json
├── FantomasOnlineV6
│ ├── .gitignore
│ ├── FantomasOnlineV6.fsproj
│ ├── FormatCode.fs
│ ├── Lambda.fs
│ ├── Program.fs
│ ├── host.json
│ └── packages.lock.json
├── FantomasOnlineV7
│ ├── .gitignore
│ ├── FantomasOnlineV7.fsproj
│ ├── FormatCode.fs
│ ├── Lambda.fs
│ ├── Program.fs
│ ├── host.json
│ └── packages.lock.json
├── HttpConstants.fs
├── OakViewer
│ ├── .gitignore
│ ├── Decoders.fs
│ ├── Encoders.fs
│ ├── Encoders.fsi
│ ├── GetOak.fs
│ ├── Lambda.fs
│ ├── OakViewer.fsproj
│ ├── Program.fs
│ ├── host.json
│ └── packages.lock.json
└── SuaveExtensions.fs
└── shared
├── ASTViewerShared.fs
├── FantomasOnlineShared.fs
├── OakShared.fs
└── Types.fs
/.config/dotnet-tools.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 1,
3 | "isRoot": true,
4 | "tools": {
5 | "fantomas": {
6 | "version": "7.0.0",
7 | "commands": [
8 | "fantomas"
9 | ],
10 | "rollForward": false
11 | }
12 | }
13 | }
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | [src/**/*.fs]
2 | fsharp_max_if_then_else_short_width = 50
3 | fsharp_max_function_binding_width = 50
4 | fsharp_align_function_signature_to_indentation = true
5 | fsharp_keep_max_number_of_blank_lines = 1
6 | fsharp_experimental_keep_indent_in_branch = true
7 |
8 | [build.fsx]
9 | fsharp_keep_max_number_of_blank_lines = 1
10 | fsharp_blank_lines_around_nested_multiline_expressions=false
11 |
12 | [src/server/**/Program.fs]
13 | fsharp_max_infix_operator_expression = 120
14 |
15 | [{src/client/fsharp/App.fs,src/client/fsharp/SettingControls.fs,src/client/fsharp/Loader.fs,src/client/fsharp/Oak/GraphView.fs,src/client/fsharp/View.fs,src/client/fsharp/*/View.fs}]
16 | fsharp_experimental_elmish = true
17 |
--------------------------------------------------------------------------------
/.fantomasignore:
--------------------------------------------------------------------------------
1 | .deps/
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: nojaf
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: npm
4 | directory: "/src/client"
5 | schedule:
6 | interval: daily
7 | time: "11:00"
8 | open-pull-requests-limit: 10
9 | allow:
10 | - dependency-name: "Fantomas.Core"
11 | - package-ecosystem: nuget
12 | directory: "/src/server/FantomasOnlineV7"
13 | schedule:
14 | interval: daily
15 | time: "10:00"
16 | open-pull-requests-limit: 10
17 | allow:
18 | - dependency-name: "Fantomas.Core"
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | # This is a basic workflow to help you get started with Actions
2 |
3 | name: CI
4 |
5 | # Controls when the action will run. Triggers the workflow on push or pull request
6 | # events but only for the master branch
7 | on:
8 | push:
9 | branches: [ main ]
10 | repository_dispatch:
11 | types: fantomas-commit-on-main
12 |
13 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel
14 | jobs:
15 | # This workflow contains a single job called "build"
16 | build:
17 | # The type of runner that the job will run on
18 | runs-on: ubuntu-latest
19 |
20 | # Steps represent a sequence of tasks that will be executed as part of the job
21 | steps:
22 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
23 | - uses: actions/checkout@v4
24 | - uses: oven-sh/setup-bun@v1
25 |
26 | - name: Setup dotnet
27 | uses: actions/setup-dotnet@v4
28 |
29 | - name: Download Fantomas
30 | run: dotnet fsi build.fsx -p Fantomas-Git
31 |
32 | - name: Run CI Target
33 | run: dotnet fsi build.fsx
34 |
35 | - name: Deploy Frontend
36 | uses: peaceiris/actions-gh-pages@v3
37 | with:
38 | github_token: ${{ secrets.GITHUB_TOKEN }}
39 | publish_dir: ./artifacts/client
40 |
41 | - name: Configure AWS Credentials
42 | uses: aws-actions/configure-aws-credentials@v1
43 | with:
44 | aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
45 | aws-region: ${{ secrets.AWS_REGION }}
46 | aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
47 |
48 | - uses: pulumi/actions@v3
49 | with:
50 | command: up
51 | stack-name: main
52 | work-dir: ./infrastructure
53 | env:
54 | PULUMI_ACCESS_TOKEN: ${{ secrets.PULUMI_ACCESS_TOKEN }}
55 |
--------------------------------------------------------------------------------
/.github/workflows/pr.yml:
--------------------------------------------------------------------------------
1 | name: PR
2 |
3 | # Controls when the action will run. Triggers the workflow on push or pull request
4 | # events but only for the master branch
5 | on:
6 | pull_request:
7 | branches: [ main ]
8 |
9 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel
10 | jobs:
11 | # This workflow contains a single job called "build"
12 | build:
13 | # The type of runner that the job will run on
14 | runs-on: ubuntu-latest
15 |
16 | # Steps represent a sequence of tasks that will be executed as part of the job
17 | steps:
18 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
19 | - uses: actions/checkout@v4
20 | - uses: oven-sh/setup-bun@v1
21 |
22 | - name: Setup dotnet
23 | uses: actions/setup-dotnet@v4
24 |
25 | - name: Clone Fantomas
26 | run: dotnet fsi build.fsx -p Fantomas-Git
27 |
28 | - name: Build
29 | run: dotnet fsi build.fsx
--------------------------------------------------------------------------------
/.gitpod.yml:
--------------------------------------------------------------------------------
1 | image: gitpod/workspace-dotnet
2 | vscode:
3 | extensions:
4 | - muhammad-sammy.csharp
5 | - ionide.ionide-fsharp
6 | - syler.sass-indented
7 | tasks:
8 | - name: Download Fantomas dependency
9 | - command: rm paket-files/ -rf && dotnet tool restore && dotnet fake run build.fsx -t Install -p 2
10 | ports:
11 | - port: 9060
12 | onOpen: open-browser
13 | visibility: public
14 | - port: 7899
15 | onOpen: ignore
16 | visibility: public
17 | - port: 7412
18 | onOpen: ignore
19 | visibility: public
20 | - port: 9856
21 | onOpen: ignore
22 | visibility: public
23 | - port: 11084
24 | onOpen: ignore
25 | visibility: public
26 | - port: 2568
27 | onOpen: ignore
28 | visibility: public
29 | - port: 9007
30 | onOpen: ignore
31 | visibility: public
32 | - port: 10707
33 | onOpen: ignore
34 | visibility: public
35 |
--------------------------------------------------------------------------------
/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 | true
5 | FS0025
6 | 3390;$(WarnOn)
7 | true
8 |
9 | true
10 | NU1603
11 | true
12 | true
13 | preview
14 | $(OtherFlags) --test:GraphBasedChecking --test:ParallelOptimization --test:ParallelIlxGen
15 |
16 | $(MSBuildThisFileDirectory).deps\fantomas
17 | $(MSBuildThisFileDirectory).deps\v7.0
18 | LatestMajor
19 | true
20 | en
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Florian Verdonck
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 all
13 | 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 THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Fantomas tools
2 |
3 | Collection of tools used when developing for Fantomas
4 |
5 | ## Prerequisites
6 |
7 | To run this tool locally you need:
8 |
9 | * [Bun](https://bun.sh/)
10 | * [.NET 8.x SDK](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)
11 |
12 | ## Running locally
13 |
14 | * Pull in the source dependencies:
15 |
16 | ```shell
17 | dotnet fsi build.fsx -- -p Fantomas-Git
18 | ```
19 |
20 | * Run the Watch pipeline:
21 |
22 | ```shell
23 | dotnet fsi build.fsx -- -p Watch
24 | ```
25 |
26 | Making changes should reflect in the tool.
27 |
28 | Or try the Run pipeline:
29 |
30 | ```shell
31 | dotnet fsi build -- -p Start
32 | ```
33 |
34 | This will run a published version of the tools.
35 |
36 | * Open http://localhost:9060
37 |
38 | ## Running in Gitpod
39 |
40 | * Open the repository via https://gitpod.io/#https://github.com/fsprojects/fantomas-tools
41 |
42 | * Run
43 |
44 | ```shell
45 | dotnet fsi build.fsx -- -p Fantomas-Git
46 | ```
47 |
48 | ```shell
49 | dotnet fsi build.fsx -- -p Watch
50 | ```
51 |
52 | * Open browser for port `9060`
53 |
54 | ## Other pipelines
55 |
56 | To see any other avaiable build script pipelines:
57 |
58 | ```shell
59 | dotnet fsi build.fsx -- --help
60 | ```
--------------------------------------------------------------------------------
/global.json:
--------------------------------------------------------------------------------
1 | {
2 | "sdk": {
3 | "version": "8.0.400",
4 | "rollForward": "latestPatch"
5 | }
6 | }
--------------------------------------------------------------------------------
/infrastructure/Pulumi.main.yaml:
--------------------------------------------------------------------------------
1 | config:
2 | aws:region: eu-west-1
3 | pulumi:template: aws-fsharp
4 |
--------------------------------------------------------------------------------
/infrastructure/Pulumi.yaml:
--------------------------------------------------------------------------------
1 | name: fantomas-aws
2 | runtime: dotnet
3 | description: A minimal AWS F# Pulumi program
--------------------------------------------------------------------------------
/infrastructure/infrastructure.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net8.0
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/infrastructure/scratch.ps1:
--------------------------------------------------------------------------------
1 | az storage cors add --methods GET OPTIONS --service t --origins "https://fsprojects.github.io" "http://localhost:63342" --account-name "storfantomasmain" --max-age 3600 --exposed-headers "*" --allowed-headers "*"
--------------------------------------------------------------------------------
/src/client/.gitignore:
--------------------------------------------------------------------------------
1 | .build
2 | build
3 | web_modules
4 | node_modules
5 | snowpack.key
6 | snowpack.crt
7 | .snowpack
--------------------------------------------------------------------------------
/src/client/.prettierignore:
--------------------------------------------------------------------------------
1 | src/bin
--------------------------------------------------------------------------------
/src/client/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "trailingComma": "all"
4 | }
5 |
--------------------------------------------------------------------------------
/src/client/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Fred K. Schott
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 all
13 | 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 THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/client/README.md:
--------------------------------------------------------------------------------
1 | # New Project
2 |
3 | > ✨ Bootstrapped with Create Snowpack App (CSA).
4 |
5 | ## Available Scripts
6 |
7 | ### npm start
8 |
9 | Runs the app in the development mode.
10 | Open http://localhost:8080 to view it in the browser.
11 |
12 | The page will reload if you make edits.
13 | You will also see any lint errors in the console.
14 |
15 | ### npm run build
16 |
17 | Builds a static copy of your site to the `build/` folder.
18 | Your app is ready to be deployed!
19 |
20 | **For the best production performance:** Add a build bundler plugin like "@snowpack/plugin-webpack" to your `snowpack.config.js` config file.
21 |
22 | ### npm test
23 |
24 | Launches the application test runner.
25 | Run with the `--watch` flag (`npm test -- --watch`) to run in interactive watch mode.
26 |
--------------------------------------------------------------------------------
/src/client/bun.lockb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fsprojects/fantomas-tools/a3382db67bcca1bf88988ba4e76741e845be7f99/src/client/bun.lockb
--------------------------------------------------------------------------------
/src/client/fsharp/ASTViewer/Decoders.fs:
--------------------------------------------------------------------------------
1 | module FantomasTools.Client.ASTViewer.Decoders
2 |
3 | open ASTViewer.Shared
4 | open Elmish
5 | open FantomasTools.Client
6 | open FantomasTools.Client.ASTViewer.Model
7 | open Thoth.Json
8 |
9 | let decodeUrlModel: Decoder list> =
10 | Decode.object (fun get ->
11 | // Optional to make old urls still work.
12 | let expand = get.Optional.Field "expand" Decode.bool
13 |
14 | [ match expand with
15 | | Some expand -> yield Cmd.ofMsg (SetExpand expand)
16 | | None -> () ])
17 |
18 | let decodeKeyValue: Decoder = fun _ -> Ok
19 |
20 | #nowarn "40"
21 |
22 | let responseDecoder: Decoder =
23 | Decode.object (fun get ->
24 | { Ast = get.Required.Field "ast" Decode.string
25 | Diagnostics = get.Required.Field "diagnostics" (Decode.array Diagnostic.Decode) })
26 |
27 | let decodeResult json = Decode.fromString responseDecoder json
28 |
--------------------------------------------------------------------------------
/src/client/fsharp/ASTViewer/Encoders.fs:
--------------------------------------------------------------------------------
1 | module FantomasTools.Client.ASTViewer.Encoders
2 |
3 | open ASTViewer.Shared
4 | open FantomasTools.Client
5 | open Thoth.Json
6 |
7 | let encodeUrlModel expand (bubble: BubbleModel) : JsonValue =
8 | Encode.object
9 | [ "defines", Encode.string bubble.Defines
10 | "isFsi", Encode.bool bubble.IsFsi
11 | "code", Encode.string bubble.SourceCode
12 | "expand", Encode.bool expand ]
13 |
14 | let encodeInput (input: Request) =
15 | Encode.object
16 | [ "sourceCode", Encode.string input.SourceCode
17 | "defines", (Array.map Encode.string input.Defines |> Encode.array)
18 | "isFsi", Encode.bool input.IsFsi
19 | "expand", Encode.bool input.Expand ]
20 | |> Encode.toString 2
21 |
--------------------------------------------------------------------------------
/src/client/fsharp/ASTViewer/Model.fs:
--------------------------------------------------------------------------------
1 | module FantomasTools.Client.ASTViewer.Model
2 |
3 | open FantomasTools.Client
4 |
5 | type Msg =
6 | | Bubble of BubbleMessage
7 | | VersionFound of string
8 | | DoParse
9 | | ASTParsed of ASTViewer.Shared.Response
10 | | Error of string
11 | | SetExpand of value: bool
12 |
13 | []
14 | type AstViewerTabState =
15 | | Loading
16 | | Result of ASTViewer.Shared.Response
17 | | Error of string
18 |
19 | type Model =
20 | { State: AstViewerTabState
21 | Version: string
22 | Expand: bool }
23 |
--------------------------------------------------------------------------------
/src/client/fsharp/ASTViewer/State.fs:
--------------------------------------------------------------------------------
1 | module FantomasTools.Client.ASTViewer.State
2 |
3 | open System
4 | open Elmish
5 | open Thoth.Json
6 | open Fable.Core
7 | open ASTViewer
8 | open FantomasTools.Client.ASTViewer.Model
9 | open FantomasTools.Client
10 | open FantomasTools.Client.ASTViewer.Decoders
11 | open FantomasTools.Client.ASTViewer.Encoders
12 |
13 | []
14 | let backend: string = jsNative
15 |
16 | let getVersion () = sprintf "%s/%s" backend "version" |> Http.getText
17 |
18 | let fetchNodeRequest url (payload: Shared.Request) dispatch =
19 | let json = encodeInput payload
20 |
21 | Http.postJson url json
22 | |> Promise.iter (fun (status, body) ->
23 | match status with
24 | | 200 ->
25 | match decodeResult body with
26 | | Ok r -> ASTParsed r
27 | | Result.Error err -> Error $"failed to decode response: %A{err}"
28 | | 400 -> Error body
29 | | 413 -> Error "the input was too large to process"
30 | | _ -> Error body
31 | |> dispatch)
32 |
33 | let fetchUntypedAST (payload: Shared.Request) dispatch =
34 | let url = $"%s{backend}/untyped-ast"
35 | fetchNodeRequest url payload dispatch
36 |
37 | let initialModel =
38 | { State = AstViewerTabState.Result { Ast = ""; Diagnostics = Array.empty }
39 | Version = ""
40 | Expand = true }
41 |
42 | let getMessageFromError (ex: exn) = Error ex.Message
43 |
44 | // defines the initial state and initial command (= side-effect) of the application
45 | let init isActive : Model * Cmd =
46 | let cmd =
47 | Cmd.batch
48 | [ if isActive then
49 | yield! UrlTools.restoreModelFromUrl decodeUrlModel []
50 | yield Cmd.OfPromise.either getVersion () VersionFound getMessageFromError ]
51 |
52 | initialModel, cmd
53 |
54 | let getDefines (bubble: BubbleModel) =
55 | bubble.Defines.Split([| ' '; ','; ';' |], StringSplitOptions.RemoveEmptyEntries)
56 |
57 | let modelToParseRequest expand (bubble: BubbleModel) : Shared.Request =
58 | { SourceCode = bubble.SourceCode
59 | Defines = getDefines bubble
60 | IsFsi = bubble.IsFsi
61 | Expand = expand }
62 |
63 | let updateUrl expand (bubble: BubbleModel) _ =
64 | let json = Encode.toString 2 (encodeUrlModel expand bubble)
65 | UrlTools.updateUrlWithData json
66 |
67 | // The update function computes the next state of the application based on the current state and the incoming events/messages
68 | // It can also run side-effects (encoded as commands) like calling the server via Http.
69 | // these commands in turn, can dispatch messages to which the update function will react.
70 | let update (bubble: BubbleModel) (msg: Msg) (model: Model) : Model * Cmd =
71 | match msg with
72 | | Msg.Bubble _ -> model, Cmd.none // handle in upper update function
73 | | ASTParsed astResult ->
74 | let nextModel =
75 | { model with
76 | State = AstViewerTabState.Result astResult }
77 |
78 | let resultCmd = Cmd.ofMsg (BubbleMessage.SetResultCode astResult.Ast |> Msg.Bubble)
79 |
80 | let diagnosticsCmd =
81 | Cmd.ofMsg (BubbleMessage.SetDiagnostics astResult.Diagnostics |> Msg.Bubble)
82 |
83 | nextModel, Cmd.batch [ resultCmd; diagnosticsCmd ]
84 | | Error e ->
85 | let nextModel =
86 | { model with
87 | State = AstViewerTabState.Error e }
88 |
89 | nextModel, Cmd.none
90 | | DoParse ->
91 | let parseRequest = modelToParseRequest model.Expand bubble
92 |
93 | let cmd =
94 | Cmd.batch
95 | [ Cmd.ofEffect (fetchUntypedAST parseRequest)
96 | Cmd.ofEffect (updateUrl model.Expand bubble) ]
97 |
98 | { model with
99 | State = AstViewerTabState.Loading },
100 | cmd
101 |
102 | | VersionFound version -> { model with Version = version }, Cmd.none
103 | | SetExpand value -> { model with Expand = value }, Cmd.none
104 |
--------------------------------------------------------------------------------
/src/client/fsharp/ASTViewer/State.fsi:
--------------------------------------------------------------------------------
1 | module FantomasTools.Client.ASTViewer.State
2 |
3 | open Elmish
4 | open FantomasTools.Client.ASTViewer.Model
5 | open FantomasTools.Client
6 |
7 | val init: isActive: bool -> Model * Cmd
8 | val update: bubble: BubbleModel -> msg: Msg -> model: Model -> Model * Cmd
9 |
--------------------------------------------------------------------------------
/src/client/fsharp/ASTViewer/View.fs:
--------------------------------------------------------------------------------
1 | module FantomasTools.Client.ASTViewer.View
2 |
3 | open System
4 | open System.Text.RegularExpressions
5 | open Fable.Core
6 | open Fable.Core.JsInterop
7 | open Fable.React
8 | open Fable.React.Props
9 | open FantomasTools.Client
10 | open FantomasTools.Client.ASTViewer.Model
11 |
12 | // Enter 'localStorage.setItem("debugASTRangeHighlight", "true");' in your browser console to enable.
13 | let debugASTRangeHighlight: bool =
14 | not (String.IsNullOrWhiteSpace(Browser.WebStorage.localStorage.getItem "debugASTRangeHighlight"))
15 |
16 | let cursorChanged (bubbleMsg: BubbleMessage -> unit) (model: Model) (e: obj) : unit =
17 | let lineNumber: int = e?position?lineNumber
18 | let column: int = e?position?column
19 |
20 | match model.State with
21 | | AstViewerTabState.Result { Ast = astText } ->
22 | let lines = astText.Split([| "\r\n"; "\n" |], StringSplitOptions.None)
23 | // Try and get the line where the cursor clicked in the AST editor
24 | match Array.tryItem (lineNumber - 1) lines with
25 | | None -> ()
26 | | Some sourceLine ->
27 |
28 | if debugASTRangeHighlight then
29 | JS.console.log (sourceLine.Trim())
30 |
31 | let pattern = @"\(\d+,\d+--\d+,\d+\)"
32 |
33 | let rangeDigits =
34 | Regex.Matches(sourceLine, pattern)
35 | |> Seq.cast
36 | |> fun matches ->
37 | if debugASTRangeHighlight then
38 | JS.console.log matches
39 |
40 | matches
41 | |> Seq.tryPick (fun m ->
42 | if debugASTRangeHighlight then
43 | JS.console.log m.Value
44 |
45 | let startIndex = m.Index
46 | let endIndex = m.Index + m.Value.Length
47 | // Verify the match contains the cursor column.
48 | if startIndex <= column && column <= endIndex then
49 | m.Value.Split([| ','; '-'; '('; ')' |], StringSplitOptions.RemoveEmptyEntries)
50 | |> Array.map int
51 | |> Array.toList
52 | |> Some
53 | else
54 | None)
55 |
56 | match rangeDigits with
57 | | Some [ startLine; startColumn; endLine; endColumn ] ->
58 | let range =
59 | { StartLine = startLine
60 | StartColumn = startColumn
61 | EndLine = endLine
62 | EndColumn = endColumn }
63 |
64 | bubbleMsg (BubbleMessage.HighLight range)
65 | | _ -> bubbleMsg (BubbleMessage.HighLight Range.Zero)
66 |
67 | | _ -> ()
68 |
69 | let commands dispatch =
70 | button [ ClassName Style.Primary; OnClick(fun _ -> dispatch DoParse) ] [ str "Show Untyped AST" ]
71 |
72 | let settings (bubble: BubbleModel) (model: Model) dispatch =
73 | fragment [] [
74 | VersionBar.versionBar $"FSC - %s{model.Version}"
75 | SettingControls.input
76 | "ast-defines"
77 | (BubbleMessage.SetDefines >> Bubble >> dispatch)
78 | (str "Defines")
79 | "Enter your defines separated with a space"
80 | bubble.Defines
81 | SettingControls.toggleButton
82 | (fun _ -> BubbleMessage.SetFsi true |> Bubble |> dispatch)
83 | (fun _ -> BubbleMessage.SetFsi false |> Bubble |> dispatch)
84 | "*.fsi"
85 | "*.fs"
86 | (str "File extension")
87 | bubble.IsFsi
88 | SettingControls.toggleButton
89 | (fun _ -> dispatch (SetExpand false))
90 | (fun _ -> dispatch (SetExpand true))
91 | "Regular"
92 | "Expanded"
93 | (str "Mode")
94 | (not model.Expand)
95 | ]
96 |
97 | let view (model: Model) =
98 | match model.State with
99 | | AstViewerTabState.Loading -> Loader.tabLoading
100 | | _ -> null
101 |
--------------------------------------------------------------------------------
/src/client/fsharp/ASTViewer/View.fsi:
--------------------------------------------------------------------------------
1 | module FantomasTools.Client.ASTViewer.View
2 |
3 | open Fable.React
4 | open FantomasTools.Client
5 | open FantomasTools.Client.ASTViewer.Model
6 |
7 | val cursorChanged: bubbleMsg: (BubbleMessage -> unit) -> model: Model -> e: obj -> unit
8 | val commands: dispatch: (Msg -> unit) -> ReactElement
9 | val settings: bubble: BubbleModel -> model: Model -> dispatch: (Msg -> unit) -> ReactElement
10 | val view: model: Model -> ReactElement
11 |
--------------------------------------------------------------------------------
/src/client/fsharp/App.fs:
--------------------------------------------------------------------------------
1 | module FantomasTools.Client.App
2 |
3 | open Fable.Core.JsInterop
4 | open Browser.Types
5 | open Fable.React
6 | open Feliz
7 | open Feliz.Router
8 | open Feliz.UseElmish
9 | open Browser.Dom
10 | open FantomasTools.Client
11 |
12 | []
13 | let App () =
14 | let model, dispatch = React.useElmish (State.init, State.update, [||])
15 |
16 | let onUrlChanged url =
17 | let activeTab = Navigation.parseUrl url
18 | dispatch (Model.Msg.SelectTab activeTab)
19 |
20 | let routes = View.rightPane model dispatch
21 |
22 | fragment [] [
23 | View.navigation dispatch
24 | main [] [
25 | View.editor model dispatch
26 | React.router [ router.onUrlChanged onUrlChanged; router.children [ routes ] ]
27 | ]
28 | ]
29 |
30 | let createRoot: Element -> {| render: ReactElement -> unit |} =
31 | import "createRoot" "react-dom/client"
32 |
33 | let root = createRoot (document.getElementById "app")
34 | root.render (React.strictMode [ App() ])
35 |
--------------------------------------------------------------------------------
/src/client/fsharp/BubbleMessage.fs:
--------------------------------------------------------------------------------
1 | namespace FantomasTools.Client
2 |
3 | /// Messages sent from the individual tab update loop to the main update loop
4 | type BubbleMessage =
5 | | SetFsi of bool
6 | | SetDefines of string
7 | | SetSourceCode of string
8 | | SetResultCode of string
9 | | SetDiagnostics of Diagnostic array
10 | | HighLight of Range
11 |
12 | /// This is the shared data among all the tabs.
13 | type BubbleModel =
14 | {
15 | SourceCode: string
16 | IsFsi: bool
17 | Defines: string
18 | /// The result of the used tool.
19 | /// Used in AST and Fantomas tab.
20 | ResultCode: string
21 | Diagnostics: Diagnostic array
22 | HighLight: Range
23 | }
24 |
--------------------------------------------------------------------------------
/src/client/fsharp/FantomasOnline/Decoders.fs:
--------------------------------------------------------------------------------
1 | module FantomasTools.Client.FantomasOnline.Decoders
2 |
3 | open FantomasTools.Client
4 | open Thoth.Json
5 | open FantomasOnline.Shared
6 |
7 | let private optionDecoder: Decoder =
8 | Decode.object (fun get ->
9 | let t = get.Required.Field "$type" Decode.string
10 |
11 | if t = "int" then
12 | get.Required.Field "$value" (Decode.tuple3 Decode.int Decode.string Decode.int)
13 | |> FantomasOption.IntOption
14 | elif t = "bool" then
15 | get.Required.Field "$value" (Decode.tuple3 Decode.int Decode.string Decode.bool)
16 | |> FantomasOption.BoolOption
17 | elif t = "multilineFormatterType" then
18 | get.Required.Field "$value" (Decode.tuple3 Decode.int Decode.string Decode.string)
19 | |> FantomasOption.MultilineFormatterTypeOption
20 | elif t = "endOfLineStyle" then
21 | get.Required.Field "$value" (Decode.tuple3 Decode.int Decode.string Decode.string)
22 | |> FantomasOption.EndOfLineStyleOption
23 | elif t = "multilineBracketStyle" then
24 | get.Required.Field "$value" (Decode.tuple3 Decode.int Decode.string Decode.string)
25 | |> FantomasOption.MultilineBracketStyleOption
26 | else
27 | failwithf $"Cannot decode %s{t}")
28 |
29 | let decodeOptions json =
30 | Decode.fromString (Decode.array optionDecoder) json
31 | |> Result.map (Array.sortBy sortByOption >> List.ofArray)
32 |
33 | let decodeOptionsFromUrl: Decoder =
34 | Decode.object (fun get -> get.Required.Field "settings" (Decode.list optionDecoder))
35 |
36 | let decodeFormatResponse: Decoder =
37 | Decode.object (fun get ->
38 | { FirstFormat = get.Required.Field "firstFormat" Decode.string
39 | FirstValidation = get.Required.Field "firstValidation" (Decode.array Diagnostic.Decode)
40 | SecondFormat = get.Optional.Field "secondFormat" Decode.string
41 | SecondValidation = get.Required.Field "secondValidation" (Decode.array Diagnostic.Decode) })
42 |
--------------------------------------------------------------------------------
/src/client/fsharp/FantomasOnline/Encoders.fs:
--------------------------------------------------------------------------------
1 | module FantomasTools.Client.FantomasOnline.Encoders
2 |
3 | open FantomasOnline.Shared
4 | open FantomasTools.Client.FantomasOnline.Model
5 | open Thoth.Json
6 |
7 | let private encodeOption fantomasOption =
8 | let key, value =
9 | match fantomasOption with
10 | | IntOption(o, k, v) -> "int", Encode.tuple3 Encode.int Encode.string Encode.int (o, k, v)
11 | | BoolOption(o, k, v) -> "bool", Encode.tuple3 Encode.int Encode.string Encode.bool (o, k, v)
12 | | MultilineFormatterTypeOption(o, k, v) ->
13 | "multilineFormatterType", Encode.tuple3 Encode.int Encode.string Encode.string (o, k, v)
14 | | EndOfLineStyleOption(o, k, v) ->
15 | "endOfLineStyle", Encode.tuple3 Encode.int Encode.string Encode.string (o, k, v)
16 | | MultilineBracketStyleOption(o, k, v) ->
17 | "multilineBracketStyle", Encode.tuple3 Encode.int Encode.string Encode.string (o, k, v)
18 |
19 | Encode.object [ "$type", Encode.string key; "$value", value ]
20 |
21 | let private encodeUserSettings model =
22 | model.UserOptions
23 | |> Map.toList
24 | |> List.sortBy (snd >> sortByOption)
25 | |> List.map (snd >> encodeOption)
26 | |> Encode.list
27 |
28 | let encodeRequest code isFsi (model: Model) =
29 | Encode.object
30 | [ "sourceCode", Encode.string code
31 | "options", encodeUserSettings model
32 | "isFsi", Encode.bool isFsi ]
33 | |> Encode.toString 2
34 |
35 | let encodeUrlModel code isFsi model =
36 | Encode.object
37 | [ "code", Encode.string code
38 | "settings", encodeUserSettings model
39 | "isFsi", Encode.bool isFsi ]
40 |
41 | let encodeUserSettingToConfiguration options =
42 | let encodeValue option =
43 | match option with
44 | | IntOption(_, _, v) -> Encode.int v
45 | | BoolOption(_, _, v) -> Encode.bool v
46 | | MultilineFormatterTypeOption(_, _, v)
47 | | EndOfLineStyleOption(_, _, v)
48 | | MultilineBracketStyleOption(_, _, v) -> Encode.string v
49 |
50 | options
51 | |> List.map (fun option -> getOptionKey option, encodeValue option)
52 | |> Encode.object
53 | |> Encode.toString 4
54 |
--------------------------------------------------------------------------------
/src/client/fsharp/FantomasOnline/Model.fs:
--------------------------------------------------------------------------------
1 | module FantomasTools.Client.FantomasOnline.Model
2 |
3 | open FantomasOnline.Shared
4 | open FantomasTools.Client
5 |
6 | type FantomasMode =
7 | | V5
8 | | V6
9 | | V7
10 | | Main // main branch
11 | | Preview // also main branch
12 |
13 | type Msg =
14 | | Bubble of BubbleMessage
15 | | VersionReceived of string
16 | | OptionsReceived of FantomasOption list
17 | | FormatException of string
18 | | Format
19 | | FormattedReceived of FormatResponse
20 | | UpdateOption of (string * FantomasOption)
21 | | ChangeMode of FantomasMode
22 | | CopySettings
23 | | UpdateSettingsFilter of string
24 | | ResetSettings
25 |
26 | []
27 | type FantomasTabState =
28 | | LoadingOptions
29 | | OptionsLoaded
30 | | LoadingFormatRequest
31 | | FormatResult of FormatResponse
32 | | FormatError of string
33 |
34 | type Model =
35 | { Version: string
36 | DefaultOptions: FantomasOption list
37 | UserOptions: Map
38 | Mode: FantomasMode
39 | State: FantomasTabState
40 | SettingsFilter: string }
41 |
42 | member this.SettingsChangedByTheUser =
43 | let defaultValues = this.DefaultOptions |> List.sortBy sortByOption
44 |
45 | let userValues =
46 | this.UserOptions |> Map.toList |> List.map snd |> List.sortBy sortByOption
47 |
48 | List.zip defaultValues userValues
49 | |> List.filter (fun (dv, uv) -> dv <> uv)
50 | |> List.map snd
51 |
52 | member this.MaxLineLength: int =
53 | tryGetOptionValue this.UserOptions this.DefaultOptions "MaxLineLength" int
54 | |> Option.defaultValue 120
55 |
--------------------------------------------------------------------------------
/src/client/fsharp/FantomasOnline/View.fsi:
--------------------------------------------------------------------------------
1 | module FantomasTools.Client.FantomasOnline.View
2 |
3 | open Fable.React
4 | open FantomasTools.Client
5 | open FantomasTools.Client.FantomasOnline.Model
6 |
7 | val commands: bubble: BubbleModel -> model: Model -> dispatch: (Msg -> unit) -> ReactElement
8 | val settings: isFsi: bool -> model: Model -> dispatch: (Msg -> unit) -> ReactElement
9 | val view: model: Model -> dispatch: (Msg -> unit) -> ReactElement
10 |
--------------------------------------------------------------------------------
/src/client/fsharp/FantomasTools.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net8.0
4 | FantomasTools.Client
5 | FantomasTools.Client
6 | $(DefineConstants);FABLE_COMPILER
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 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/src/client/fsharp/Http.fs:
--------------------------------------------------------------------------------
1 | module FantomasTools.Client.Http
2 |
3 | open Fable.Core
4 | open Fable.Core.JsInterop
5 | open Fetch
6 |
7 | let postJson<'TResponse> (url: string) (body: string) : JS.Promise =
8 | let options =
9 | requestProps
10 | [ requestHeaders [ ContentType "application/json" ]
11 | Method HttpMethod.POST
12 | Body !^body ]
13 |
14 | GlobalFetch.fetch (RequestInfo.Url url, options)
15 | |> Promise.bind (fun res ->
16 | promise {
17 | let! text = res.text ()
18 | return (res.Status, text)
19 | })
20 |
21 | let getText (url: string) : JS.Promise =
22 | let options =
23 | requestProps [ requestHeaders [ ContentType "application/json" ]; Method HttpMethod.GET ]
24 |
25 | GlobalFetch.fetch (RequestInfo.Url url, options)
26 | |> Promise.bind (fun res -> res.text ())
27 |
--------------------------------------------------------------------------------
/src/client/fsharp/Loader.fs:
--------------------------------------------------------------------------------
1 | module FantomasTools.Client.Loader
2 |
3 | open Fable.React
4 | open Fable.React.Props
5 | open FantomasTools.Client
6 |
7 | let loading = div [ Id "loading" ] [ div [] [] ]
8 |
9 | let tabLoading = div [ ClassName Style.TabContent ] [ loading ]
10 |
--------------------------------------------------------------------------------
/src/client/fsharp/Model.fs:
--------------------------------------------------------------------------------
1 | module FantomasTools.Client.Model
2 |
3 | open FantomasTools.Client
4 |
5 | type ActiveTab =
6 | | HomeTab
7 | | ASTTab
8 | | OakTab
9 | | FantomasTab of FantomasTools.Client.FantomasOnline.Model.FantomasMode
10 |
11 | type Model =
12 | { ActiveTab: ActiveTab
13 | SettingsOpen: bool
14 | Bubble: BubbleModel
15 | OakModel: OakViewer.Model.Model
16 | ASTModel: ASTViewer.Model.Model
17 | FantomasModel: FantomasOnline.Model.Model }
18 |
19 | type Msg =
20 | | SelectTab of ActiveTab
21 | | UpdateSourceCode of string
22 | | OakMsg of OakViewer.Model.Msg
23 | | ASTMsg of ASTViewer.Model.Msg
24 | | FantomasMsg of FantomasOnline.Model.Msg
25 | | ToggleSettings
26 |
--------------------------------------------------------------------------------
/src/client/fsharp/Navigation.fs:
--------------------------------------------------------------------------------
1 | module FantomasTools.Client.Navigation
2 |
3 | open FantomasTools.Client
4 | open FantomasTools.Client.Model
5 | open Elmish
6 | open Feliz.Router
7 |
8 | let cmdForCurrentTab tab (model: Model) =
9 | let noSourceCode = System.String.IsNullOrWhiteSpace model.Bubble.SourceCode
10 |
11 | match tab with
12 | | HomeTab -> Cmd.none
13 | | ASTTab -> Cmd.ofMsg ASTViewer.Model.DoParse |> Cmd.map Msg.ASTMsg
14 | | OakTab -> Cmd.ofMsg OakViewer.Model.GetOak |> Cmd.map Msg.OakMsg
15 | | FantomasTab mode ->
16 | if noSourceCode then
17 | Cmd.none
18 | elif mode <> model.FantomasModel.Mode then
19 | Cmd.batch
20 | [ Cmd.map FantomasMsg (FantomasOnline.State.getOptionsCmd mode)
21 | Cmd.map FantomasMsg (FantomasOnline.State.getVersionCmd mode) ]
22 | elif not (List.isEmpty model.FantomasModel.DefaultOptions) then
23 | Cmd.ofMsg FantomasOnline.Model.Format |> Cmd.map FantomasMsg
24 | else
25 | Cmd.none
26 |
27 | let toHash =
28 | function
29 | | HomeTab -> "#/"
30 | | OakTab -> "#/oak"
31 | | ASTTab -> "#/ast"
32 | | FantomasTab FantomasOnline.Model.V5 -> "#/fantomas/v5"
33 | | FantomasTab FantomasOnline.Model.V6 -> "#/fantomas/v6"
34 | | FantomasTab FantomasOnline.Model.V7 -> "#/fantomas/v7"
35 | | FantomasTab FantomasOnline.Model.Main -> "#/fantomas/main"
36 | | FantomasTab FantomasOnline.Model.Preview -> "#/fantomas/preview"
37 |
38 | let parseUrl segments =
39 | match segments with
40 | | [ "ast" ]
41 | | [ "ast"; Route.Query [ "data", _ ] ] -> ActiveTab.ASTTab
42 | | [ "oak" ]
43 | | [ "oak"; Route.Query [ "data", _ ] ] -> ActiveTab.OakTab
44 | | [ "fantomas"; "v5" ]
45 | | [ "fantomas"; "v5"; Route.Query [ "data", _ ] ] -> ActiveTab.FantomasTab(FantomasOnline.Model.V5)
46 | | [ "fantomas"; "v6" ]
47 | | [ "fantomas"; "v6"; Route.Query [ "data", _ ] ] -> ActiveTab.FantomasTab(FantomasOnline.Model.V6)
48 | | [ "fantomas"; "v7" ]
49 | | [ "fantomas"; "v7"; Route.Query [ "data", _ ] ] -> ActiveTab.FantomasTab(FantomasOnline.Model.V7)
50 | | [ "fantomas"; "main" ]
51 | | [ "fantomas"; "main"; Route.Query [ "data", _ ] ] -> ActiveTab.FantomasTab(FantomasOnline.Model.Main)
52 | | [ "fantomas"; "preview" ]
53 | | [ "fantomas"; "preview"; Route.Query [ "data", _ ] ] -> ActiveTab.FantomasTab(FantomasOnline.Model.Preview)
54 | | _ -> ActiveTab.HomeTab
55 |
--------------------------------------------------------------------------------
/src/client/fsharp/Oak/Decoders.fs:
--------------------------------------------------------------------------------
1 | module FantomasTools.Client.OakViewer.Decoders
2 |
3 | open Thoth.Json
4 | open FantomasTools.Client
5 | open FantomasTools.Client.OakViewer.Model
6 |
7 | let decodeUrlModel: Decoder =
8 | Decode.object (fun get -> get.Optional.Field "isGraphView" Decode.bool |> Option.defaultValue false)
9 |
10 | let decodeTriviaNode: Decoder =
11 | Decode.object (fun get ->
12 | { Type = get.Required.Field "type" Decode.string
13 | Range = get.Required.Field "range" Range.Decode
14 | Content = get.Optional.Field "content" Decode.string })
15 |
16 | let rec private decodeNode (name: string) (value: JsonValue) =
17 | Decode.object
18 | (fun get ->
19 | { Type = get.Required.Field "type" Decode.string
20 | Text = get.Optional.Field "text" Decode.string
21 | Range = get.Required.Field "range" Range.Decode
22 | ContentBefore = get.Required.Field "contentBefore" (Decode.array decodeTriviaNode)
23 | Children = get.Required.Field "children" (Decode.array decodeNode)
24 | ContentAfter = get.Required.Field "contentAfter" (Decode.array decodeTriviaNode) })
25 | name
26 | value
27 |
28 | let decodeOak: string -> obj -> Result =
29 | Decode.object (fun get ->
30 | let oak = get.Required.Field "oak" decodeNode
31 | let diagnostics = get.Required.Field "diagnostics" (Decode.array Diagnostic.Decode)
32 | oak, diagnostics)
33 |
--------------------------------------------------------------------------------
/src/client/fsharp/Oak/Encoders.fs:
--------------------------------------------------------------------------------
1 | module FantomasTools.Client.OakViewer.Encoders
2 |
3 | open FantomasTools.Client
4 | open Thoth.Json
5 | open FantomasTools.Client.OakViewer.Model
6 |
7 | let encodeParseRequest (pr: OakViewer.ParseRequest) =
8 | Encode.object
9 | [ "sourceCode", Encode.string pr.SourceCode
10 | "defines", Array.map Encode.string pr.Defines |> Encode.array
11 | "isFsi", Encode.bool pr.IsFsi ]
12 | |> Encode.toString 4
13 |
14 | let encodeUrlModel (bubble: BubbleModel) (model: Model) =
15 | Encode.object
16 | [ "code", Encode.string bubble.SourceCode // the "code" key is a convention
17 | "defines", Encode.string bubble.Defines
18 | "isFsi", Encode.bool bubble.IsFsi
19 | "isGraphView", Encode.bool model.IsGraphView ]
20 |
--------------------------------------------------------------------------------
/src/client/fsharp/Oak/Graph.fs:
--------------------------------------------------------------------------------
1 | module FantomasTools.Client.OakViewer.Graph
2 |
3 | open Browser.Types
4 | open Fable.Core
5 | open Fable.Core.JsInterop
6 | open Fable.React
7 | open Feliz
8 |
9 | module VisNetwork =
10 | type node =
11 | {| id: int
12 | label: string
13 | title: string
14 | level: int
15 | color: string
16 | shape: string
17 | value: int
18 | font: obj |}
19 |
20 | type edge =
21 | {| from: int
22 | ``to``: int
23 | dashes: bool |}
24 |
25 | type options =
26 | {| layout: obj
27 | interaction: obj
28 | width: string
29 | height: string
30 | nodes: obj |}
31 |
32 | []
33 | type DataSet(_data: U2 array) = class end
34 |
35 | type data = {| nodes: DataSet; edges: DataSet |}
36 |
37 | []
38 | type Network(_container: Element, _data: obj, _options: obj) =
39 | inherit System.Object()
40 |
41 | []
42 | member this.OnSelect _callback : unit = jsNative
43 |
44 | []
45 | member this.OnHover _callback : unit = jsNative
46 |
47 | []
48 | member this.OffSelect() : unit = jsNative
49 |
50 | []
51 | member this.OffHover() : unit = jsNative
52 |
53 | []
54 | member this.SetData(_data: data) = jsNative
55 |
56 | []
57 | member this.SetOptions(_options: options) = jsNative
58 |
59 | type GraphProps =
60 | {| options: VisNetwork.options
61 | data: VisNetwork.data
62 | selectNode: {| nodes: int array |} -> unit
63 | hoverNode: {| node: int |} -> unit |}
64 |
65 | type RefProp =
66 | | Ref of obj
67 |
68 | interface IHTMLProp
69 |
70 | []
71 | let Graph (props: GraphProps) =
72 | let divRef = React.useRef null
73 | let network, setNetwork = React.useState None
74 |
75 | React.useEffectOnce (fun () ->
76 | if not (isNullOrUndefined divRef.current) then
77 | let network' = VisNetwork.Network(divRef.current, props.data, props.options)
78 | network'.OnSelect(props.selectNode)
79 | network'.OnHover(props.hoverNode)
80 | setNetwork (Some network'))
81 |
82 | React.useEffect (
83 | fun () ->
84 | network
85 | |> Option.iter (fun network ->
86 | network.OffSelect()
87 | network.OffHover()
88 | network.SetData props.data
89 | network.OnSelect(props.selectNode)
90 | network.OnHover(props.hoverNode))
91 | , [| box network; box props.data |]
92 | )
93 |
94 | React.useEffect (
95 | fun () -> network |> Option.iter (fun network -> network.SetOptions props.options)
96 | , [| box network; box props.options |]
97 | )
98 |
99 | div [ Ref divRef ] [ str "graph, never render" ]
100 |
--------------------------------------------------------------------------------
/src/client/fsharp/Oak/Model.fs:
--------------------------------------------------------------------------------
1 | module FantomasTools.Client.OakViewer.Model
2 |
3 | open Fable.Core
4 | open FantomasTools.Client
5 |
6 | type TriviaNode =
7 | { Type: string
8 | Range: Range
9 | Content: string option }
10 |
11 | type OakNode =
12 | { Type: string
13 | Text: string option
14 | Range: Range
15 | ContentBefore: TriviaNode array
16 | Children: OakNode array
17 | ContentAfter: TriviaNode array }
18 |
19 | module GraphView =
20 | []
21 | type NodeId = NodeId of int
22 |
23 | []
24 | type NodeLabel = NodeLabel of string
25 |
26 | []
27 | type NodeColor = NodeColor of string
28 |
29 | []
30 | type NodeShape =
31 | | Ellipse
32 | | Box
33 |
34 | type Node =
35 | { Label: NodeLabel
36 | Level: int
37 | Color: NodeColor
38 | Shape: NodeShape
39 | ScaleValue: int }
40 |
41 | type Edge =
42 | { From: NodeId
43 | To: NodeId
44 | Dashed: bool }
45 |
46 | type Layout =
47 | | TopDown
48 | | LeftRight
49 | | Free
50 |
51 | type Scale =
52 | | NoScale
53 | | SubTreeNodes
54 | | AllNodes
55 |
56 | type Options =
57 | { NodeLimit: int
58 | Layout: Layout
59 | Scale: Scale
60 | ScaleMaxSize: int }
61 |
62 | type Msg =
63 | | Bubble of BubbleMessage
64 | | GetOak
65 | | OakReceived of oak: OakNode * diagnostics: Diagnostic array
66 | | FSCVersionReceived of string
67 | | SetGraphView of bool
68 | | SetGraphViewNodeLimit of int
69 | | SetGraphViewLayout of GraphView.Layout
70 | | SetGraphViewScale of GraphView.Scale
71 | | SetGraphViewScaleMax of int
72 | | GraphViewSetRoot of GraphView.NodeId
73 | | GraphViewGoBack
74 | | Error of string
75 |
76 | []
77 | type OakViewerTabState =
78 | | Loading
79 | | Result of OakNode
80 | | Error of string
81 |
82 | type Model =
83 | { State: OakViewerTabState
84 | Version: string
85 | IsGraphView: bool
86 | GraphViewOptions: GraphView.Options
87 | GraphViewRootNodes: GraphView.NodeId list }
88 |
--------------------------------------------------------------------------------
/src/client/fsharp/Oak/State.fs:
--------------------------------------------------------------------------------
1 | module FantomasTools.Client.OakViewer.State
2 |
3 | open System
4 | open Fable.Core
5 | open FantomasTools.Client
6 | open Elmish
7 | open Thoth.Json
8 | open FantomasTools.Client.OakViewer.Model
9 | open FantomasTools.Client.OakViewer.Encoders
10 | open FantomasTools.Client.OakViewer.Decoders
11 |
12 | []
13 | let private backend: string = jsNative
14 |
15 | let private fetchOak (payload: OakViewer.ParseRequest) dispatch =
16 | let url = sprintf "%s/get-oak" backend
17 | let json = encodeParseRequest payload
18 |
19 | Http.postJson url json
20 | |> Promise.iter (fun (status, body) ->
21 | match status with
22 | | 200 ->
23 | match Decode.fromString decodeOak body with
24 | | Ok response -> Msg.OakReceived response
25 | | Result.Error err -> Msg.Error err
26 | | _ -> Msg.Error body
27 | |> dispatch)
28 |
29 | let private fetchFSCVersion () = sprintf "%s/version" backend |> Http.getText
30 |
31 | let private initialModel: Model =
32 | { State =
33 | OakViewerTabState.Result(
34 | { Type = "Oak"
35 | Text = None
36 | Range = Range.Zero
37 | ContentBefore = Array.empty
38 | Children = Array.empty
39 | ContentAfter = Array.empty }
40 | )
41 | Version = "???"
42 | IsGraphView = false
43 | GraphViewOptions =
44 | { Layout = GraphView.TopDown
45 | NodeLimit = 25
46 | Scale = GraphView.SubTreeNodes
47 | ScaleMaxSize = 25 }
48 | GraphViewRootNodes = [] }
49 |
50 | let private splitDefines (value: string) =
51 | value.Split([| ' '; ';' |], StringSplitOptions.RemoveEmptyEntries)
52 |
53 | let private modelToParseRequest (bubble: BubbleModel) : OakViewer.ParseRequest =
54 | { SourceCode = bubble.SourceCode
55 | Defines = splitDefines bubble.Defines
56 | IsFsi = bubble.IsFsi }
57 |
58 | let init () =
59 | let isGraphView = UrlTools.restoreModelFromUrl decodeUrlModel false
60 |
61 | let cmd =
62 | Cmd.OfPromise.either fetchFSCVersion () FSCVersionReceived (fun ex -> Error ex.Message)
63 |
64 | { initialModel with
65 | IsGraphView = isGraphView },
66 | cmd
67 |
68 | let private updateUrl (bubble: BubbleModel) (model: Model) _ =
69 | let json = Encode.toString 2 (encodeUrlModel bubble model)
70 | UrlTools.updateUrlWithData json
71 |
72 | let update (bubble: BubbleModel) (msg: Msg) model : Model * Cmd =
73 | match msg with
74 | | Msg.Bubble _ -> model, Cmd.none // handle in upper update function
75 | | Msg.GetOak ->
76 | let parseRequest = modelToParseRequest bubble
77 |
78 | let cmd =
79 | Cmd.batch [ Cmd.ofEffect (fetchOak parseRequest); Cmd.ofEffect (updateUrl bubble model) ]
80 |
81 | { model with
82 | State = OakViewerTabState.Loading },
83 | cmd
84 | | Msg.OakReceived(oak, diagnostics) ->
85 | let cmd = Cmd.ofMsg (Msg.Bubble(BubbleMessage.SetDiagnostics diagnostics))
86 |
87 | { model with
88 | State = OakViewerTabState.Result oak
89 | GraphViewRootNodes = [] },
90 | cmd
91 | | Msg.Error error ->
92 | { initialModel with
93 | State = OakViewerTabState.Error error },
94 | Cmd.none
95 | | FSCVersionReceived version -> { model with Version = version }, Cmd.none
96 | | SetGraphView value -> let m = { model with IsGraphView = value } in m, Cmd.ofEffect (updateUrl bubble m)
97 | | SetGraphViewLayout value ->
98 | { model with
99 | GraphViewOptions =
100 | { model.GraphViewOptions with
101 | Layout = value } },
102 | Cmd.none
103 | | SetGraphViewNodeLimit value ->
104 | { model with
105 | GraphViewOptions =
106 | { model.GraphViewOptions with
107 | NodeLimit = value } },
108 | Cmd.none
109 | | SetGraphViewScale value ->
110 | { model with
111 | GraphViewOptions =
112 | { model.GraphViewOptions with
113 | Scale = value } },
114 | Cmd.none
115 | | SetGraphViewScaleMax value ->
116 | { model with
117 | GraphViewOptions =
118 | { model.GraphViewOptions with
119 | ScaleMaxSize = value } },
120 | Cmd.none
121 | | GraphViewSetRoot nodeId ->
122 | { model with
123 | GraphViewRootNodes = nodeId :: model.GraphViewRootNodes },
124 | Cmd.none
125 | | GraphViewGoBack ->
126 | { model with
127 | GraphViewRootNodes =
128 | if model.GraphViewRootNodes = [] then
129 | []
130 | else
131 | List.tail model.GraphViewRootNodes },
132 | Cmd.none
133 |
--------------------------------------------------------------------------------
/src/client/fsharp/Oak/View.fsi:
--------------------------------------------------------------------------------
1 | module FantomasTools.Client.OakViewer.View
2 |
3 | open Fable.React
4 | open FantomasTools.Client
5 | open FantomasTools.Client.OakViewer.Model
6 |
7 | val view: model: Model -> dispatch: (Msg -> unit) -> ReactElement
8 | val commands: dispatch: (Msg -> unit) -> ReactElement
9 | val settings: bubble: BubbleModel -> model: Model -> dispatch: (Msg -> unit) -> ReactElement
10 |
--------------------------------------------------------------------------------
/src/client/fsharp/SettingControls.fs:
--------------------------------------------------------------------------------
1 | module FantomasTools.Client.SettingControls
2 |
3 | open Fable.React
4 | open Fable.React.Props
5 | open FantomasTools.Client
6 |
7 | let input key onChange (labelValue: ReactElement) placeholder value =
8 | div [ ClassName Style.Setting ] [
9 | label [] [ labelValue ]
10 | input [
11 | Placeholder placeholder
12 | OnChange(fun ev -> ev.Value |> onChange)
13 | DefaultValue value
14 | Key key
15 | ]
16 | ]
17 |
18 | let private toggleButton_ onClick active label =
19 | let className = if active then Style.Active else ""
20 |
21 | button [ ClassName className; Key label; OnClick onClick ] [ str label ]
22 |
23 | let toggleButton onTrue onFalse labelTrue labelFalse (labelValue: ReactElement) value =
24 | div [ ClassName Style.Setting ] [
25 | label [] [ labelValue ]
26 | div [ ClassName Style.ToggleButton ] [
27 | toggleButton_ onTrue value labelTrue
28 | toggleButton_ onFalse (not value) labelFalse
29 | ]
30 | ]
31 |
32 | type MultiButtonSettings =
33 | { Label: string
34 | OnClick: obj -> unit
35 | IsActive: bool }
36 |
37 | let multiButton labelValue (options: MultiButtonSettings list) =
38 | let buttons =
39 | options
40 | |> List.map (fun { Label = l; OnClick = o; IsActive = i } -> toggleButton_ o i l)
41 |
42 | div [ ClassName Style.Setting ] [
43 | label [] [ str labelValue ]
44 | div [ ClassName Style.ToggleButton ] [ ofList buttons ]
45 | ]
46 |
--------------------------------------------------------------------------------
/src/client/fsharp/State.fs:
--------------------------------------------------------------------------------
1 | module FantomasTools.Client.State
2 |
3 | open FantomasTools.Client
4 | open Elmish
5 | open FantomasTools.Client.Model
6 | open Thoth.Json
7 | open Feliz.Router
8 |
9 | let private getBubbleFromUrl () : BubbleModel =
10 | let empty =
11 | { SourceCode = ""
12 | IsFsi = false
13 | Defines = ""
14 | ResultCode = ""
15 | Diagnostics = Array.empty
16 | HighLight = Range.Zero }
17 |
18 | UrlTools.restoreModelFromUrl
19 | (Decode.object (fun get ->
20 | let sourceCode = get.Required.Field "code" Decode.string
21 | let isFsi = get.Optional.Field "isFsi" Decode.bool |> Option.defaultValue false
22 | let defines = get.Optional.Field "defines" Decode.string |> Option.defaultValue ""
23 |
24 | { empty with
25 | SourceCode = sourceCode
26 | IsFsi = isFsi
27 | Defines = defines }))
28 | empty
29 |
30 | let private getIsFsiFileFromUrl () =
31 | UrlTools.restoreModelFromUrl (Decode.object (fun get -> get.Required.Field "isFsi" Decode.bool)) false
32 |
33 | let init _ =
34 | let bubble = getBubbleFromUrl ()
35 | let currentTab = Navigation.parseUrl (Router.currentUrl ())
36 |
37 | let astModel, astCmd = ASTViewer.State.init (currentTab = ASTTab)
38 | let oakModel, oakCmd = OakViewer.State.init ()
39 |
40 | let fantomasModel, fantomasCmd =
41 | let tab =
42 | match currentTab with
43 | | ActiveTab.FantomasTab ft -> ft
44 | | _ -> FantomasTools.Client.FantomasOnline.Model.Main
45 |
46 | FantomasOnline.State.init tab
47 |
48 | let model =
49 | { ActiveTab = currentTab
50 | SettingsOpen = false
51 | Bubble = bubble
52 | OakModel = oakModel
53 | ASTModel = astModel
54 | FantomasModel = fantomasModel }
55 |
56 | let cmd =
57 | Cmd.batch
58 | [ Cmd.map ASTMsg astCmd
59 | Cmd.map OakMsg oakCmd
60 | Cmd.map FantomasMsg fantomasCmd ]
61 |
62 | model, cmd
63 |
64 | let private reload model =
65 | if not model.SettingsOpen then
66 | match model.ActiveTab with
67 | | ASTTab -> Cmd.ofMsg FantomasTools.Client.ASTViewer.Model.DoParse |> Cmd.map ASTMsg
68 | | FantomasTab _ ->
69 | Cmd.ofMsg FantomasTools.Client.FantomasOnline.Model.Format
70 | |> Cmd.map FantomasMsg
71 | | OakTab -> Cmd.ofMsg FantomasTools.Client.OakViewer.Model.GetOak |> Cmd.map OakMsg
72 | | _ -> Cmd.none
73 | else
74 | Cmd.none
75 |
76 | let update msg model =
77 | match msg with
78 | | SelectTab tab ->
79 | let nextModel =
80 | match tab with
81 | | ActiveTab.FantomasTab ft when (ft <> model.FantomasModel.Mode) ->
82 | { model with
83 | ActiveTab = tab
84 | Bubble =
85 | { model.Bubble with
86 | Diagnostics = Array.empty }
87 | FantomasModel = { model.FantomasModel with Mode = ft } }
88 | | _ ->
89 | { model with
90 | ActiveTab = tab
91 | Bubble =
92 | { model.Bubble with
93 | Diagnostics = Array.empty } }
94 |
95 | let cmd = Navigation.cmdForCurrentTab tab model
96 |
97 | nextModel, cmd
98 | | UpdateSourceCode code ->
99 | let bubble = { model.Bubble with SourceCode = code }
100 | { model with Bubble = bubble }, Cmd.none
101 | | ToggleSettings ->
102 | let m =
103 | { model with
104 | SettingsOpen = not model.SettingsOpen }
105 |
106 | m, reload m
107 |
108 | | ASTMsg(ASTViewer.Model.Msg.Bubble bubbleMsg)
109 | | OakMsg(OakViewer.Model.Msg.Bubble bubbleMsg)
110 | | FantomasMsg(FantomasOnline.Model.Msg.Bubble bubbleMsg) ->
111 | let bubble, cmd =
112 | match bubbleMsg with
113 | | SetSourceCode code -> { model.Bubble with SourceCode = code }, Cmd.none
114 | | SetFsi isFsiFile -> { model.Bubble with IsFsi = isFsiFile }, Cmd.none
115 | | SetDefines defines -> { model.Bubble with Defines = defines }, Cmd.none
116 | | SetResultCode code -> { model.Bubble with ResultCode = code }, Cmd.none
117 | | SetDiagnostics diagnostics ->
118 | { model.Bubble with
119 | Diagnostics = diagnostics },
120 | Cmd.none
121 | | HighLight hlr -> { model.Bubble with HighLight = hlr }, Cmd.none
122 |
123 | { model with Bubble = bubble }, cmd
124 |
125 | | OakMsg oMsg ->
126 | let oModel, oCmd = OakViewer.State.update model.Bubble oMsg model.OakModel
127 |
128 | { model with OakModel = oModel }, Cmd.map OakMsg oCmd
129 |
130 | | ASTMsg aMsg ->
131 | let aModel, aCmd = ASTViewer.State.update model.Bubble aMsg model.ASTModel
132 |
133 | { model with ASTModel = aModel }, Cmd.map ASTMsg aCmd
134 |
135 | | FantomasMsg(FantomasOnline.Model.ChangeMode mode) ->
136 | let cmd =
137 | let changeVersion (hashWithoutQuery: string) =
138 | let version m =
139 | match m with
140 | | FantomasOnline.Model.V5 -> "v5"
141 | | FantomasOnline.Model.V6 -> "v6"
142 | | FantomasOnline.Model.V7 -> "v7"
143 | | FantomasOnline.Model.Main -> "main"
144 | | FantomasOnline.Model.Preview -> "preview"
145 |
146 | let oldVersion = version model.FantomasModel.Mode
147 | let newVersion = version mode
148 | hashWithoutQuery.Replace(oldVersion, newVersion)
149 |
150 | Cmd.ofEffect (fun dispatch ->
151 | UrlTools.updateUrlBy changeVersion
152 | dispatch (SelectTab(ActiveTab.FantomasTab(mode))))
153 |
154 | model, cmd
155 | | FantomasMsg fMsg ->
156 | let isActiveTab =
157 | match model.ActiveTab with
158 | | FantomasTab _ -> true
159 | | _ -> false
160 |
161 | let fModel, fCmd =
162 | FantomasOnline.State.update isActiveTab model.Bubble fMsg model.FantomasModel
163 |
164 | { model with FantomasModel = fModel }, Cmd.map FantomasMsg fCmd
165 |
--------------------------------------------------------------------------------
/src/client/fsharp/Style.fs:
--------------------------------------------------------------------------------
1 | namespace FantomasTools.Client
2 |
3 | open Zanaptak.TypedCssClasses
4 |
5 | module private Config =
6 | []
7 | let cssFile = __SOURCE_DIRECTORY__ + "/../src/styles/style.css"
8 |
9 | type Style = CssClasses
10 |
--------------------------------------------------------------------------------
/src/client/fsharp/UrlTools.fs:
--------------------------------------------------------------------------------
1 | module FantomasTools.Client.UrlTools
2 |
3 | open Fable.Core.JsInterop
4 | open Fable.Core
5 | open Thoth.Json
6 | open Browser.Types
7 | open Browser
8 | open System
9 |
10 | let private setGetParam (encodedJson: string) : unit =
11 | if not (isNullOrUndefined history.pushState) then
12 | let hashPieces =
13 | window.location.hash.Split([| '?' |], StringSplitOptions.RemoveEmptyEntries)
14 |
15 | let hash =
16 | if
17 | not (isNullOrUndefined hashPieces)
18 | && not (String.IsNullOrWhiteSpace(hashPieces.[0]))
19 | then
20 | hashPieces.[0]
21 | else
22 | ""
23 |
24 | let ``params`` = URLSearchParams.Create()
25 | ``params``.set ("data", encodedJson)
26 |
27 | let newUrl =
28 | $"{window.location.protocol}//{window.location.host}{window.location.pathname}{hash}?{``params``.ToString()}"
29 |
30 | let currentUrl = window.location.toString ()
31 |
32 | if currentUrl <> newUrl then
33 | history.pushState ({| path = newUrl |}, "", newUrl)
34 |
35 | let private encodeUrl (_x: string) : string =
36 | import "compressToEncodedURIComponent" "lz-string"
37 |
38 | let private decodeUrl (_x: string) : string =
39 | import "decompressFromEncodedURIComponent" "lz-string"
40 |
41 | let private URLSearchParamsExist: bool = emitJsExpr () "'URLSearchParams' in window"
42 |
43 | let updateUrlBy (mapFn: string -> string) : unit =
44 | if URLSearchParamsExist then
45 | let hashPieces = window.location.hash.Split('?')
46 | let ``params`` = URLSearchParams.Create(hashPieces.[1])
47 |
48 | let safeHash =
49 | if isNullOrUndefined window.location.hash then
50 | ""
51 | else
52 | window.location.hash
53 |
54 | let newHash = (mapFn safeHash).Split('?').[0]
55 |
56 | let newUrl =
57 | $"{window.location.protocol}//{window.location.host}{window.location.pathname}{newHash}?{``params``.ToString()}"
58 |
59 | history.pushState ({| path = newUrl |}, "", newUrl)
60 |
61 | let updateUrlWithData json = setGetParam (encodeUrl json)
62 |
63 | let private (|KeyValuesFromHash|_|) hash =
64 | if String.IsNullOrWhiteSpace(hash) then
65 | None
66 | else
67 | let search = hash.Split('?')
68 |
69 | if Seq.length search > 1 then
70 | search.[1].Split('&')
71 | |> Array.map (fun kv -> kv.Split('=').[0], kv.Split('=').[1])
72 | |> Array.choose (fun (k, v) -> if k = "data" then Some v else None)
73 | |> Array.tryHead
74 | else
75 | None
76 |
77 | let restoreModelFromUrl decoder defaultValue =
78 | match Browser.Dom.window.location.hash with
79 | | KeyValuesFromHash v ->
80 | let json = JS.decodeURIComponent v |> decodeUrl
81 | let modelResult = Decode.fromString decoder json
82 |
83 | match modelResult with
84 | | Result.Ok m -> m
85 | | Error err ->
86 | printfn "%A" err
87 | defaultValue
88 | | _ -> defaultValue
89 |
--------------------------------------------------------------------------------
/src/client/fsharp/Utils.fs:
--------------------------------------------------------------------------------
1 | module FantomasTools.Client.Utils
2 |
3 | let memoizeBy (g: 'a -> 'c) (f: 'a -> 'b) =
4 | let cache = System.Collections.Generic.Dictionary<_, _>()
5 |
6 | fun x ->
7 | let key = g x
8 |
9 | if cache.ContainsKey key then
10 | cache[key]
11 | else
12 | let y = f x
13 | cache.Add(key, y)
14 | y
15 |
16 | let inline memoize f = memoizeBy id f
17 |
18 | let inline memoize2 f =
19 | memoizeBy id (fun (x, y) -> f x y) |> fun f -> fun x y -> f (x, y)
20 |
--------------------------------------------------------------------------------
/src/client/fsharp/VersionBar.fs:
--------------------------------------------------------------------------------
1 | module FantomasTools.Client.VersionBar
2 |
3 | open Fable.React
4 | open Fable.React.Props
5 | open FantomasTools.Client
6 |
7 | let versionBar version =
8 | div [ ClassName Style.VersionBar ] [ str version ]
9 |
--------------------------------------------------------------------------------
/src/client/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | Fantomas Tools
13 |
14 |
15 |
16 |
17 |
18 |
19 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/src/client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "dev": "bunx --bun vite",
4 | "build": "bunx --bun vite build",
5 | "serve": "bunx --bun vite preview",
6 | "format": "prettier --write \"src/**/*.{js,jsx}\" vite.config.js",
7 | "lint": "prettier --check \"src/**/*.{js,jsx}\" vite.config.js"
8 | },
9 | "type": "module",
10 | "dependencies": {
11 | "@monaco-editor/react": "4.6.0",
12 | "lz-string": "1.4.4",
13 | "notyf": "3.10.0",
14 | "react": "^18.2.0",
15 | "react-dom": "^18.2.0",
16 | "use-sync-external-store": "^1.2.0",
17 | "vis-data": "7.1.8",
18 | "vis-network": "9.1.9"
19 | },
20 | "devDependencies": {
21 | "@vitejs/plugin-react": "4.2.1",
22 | "prettier": "3.1.1",
23 | "prop-types": "^15.8.1",
24 | "vite": "^5.2.0-beta.1",
25 | "vite-plugin-fable": "^0.0.26"
26 | },
27 | "trustedDependencies": [
28 | "vite-plugin-fable"
29 | ]
30 | }
31 |
--------------------------------------------------------------------------------
/src/client/public/debug.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Document
7 |
8 |
9 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/client/public/fantomas_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fsprojects/fantomas-tools/a3382db67bcca1bf88988ba4e76741e845be7f99/src/client/public/fantomas_logo.png
--------------------------------------------------------------------------------
/src/client/public/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fsprojects/fantomas-tools/a3382db67bcca1bf88988ba4e76741e845be7f99/src/client/public/logo.png
--------------------------------------------------------------------------------
/src/client/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/src/client/src/index.jsx:
--------------------------------------------------------------------------------
1 | import '../fsharp/App.fs';
2 | import './styles/style.css';
3 | import 'notyf/notyf.min.css';
4 | import 'vis-network/styles/vis-network.css';
5 |
--------------------------------------------------------------------------------
/src/client/vite.config.js:
--------------------------------------------------------------------------------
1 | import path from 'node:path';
2 | import { fileURLToPath } from 'node:url';
3 | import { defineConfig } from 'vite';
4 | import react from '@vitejs/plugin-react';
5 | import fable from 'vite-plugin-fable';
6 |
7 | const currentDir = path.dirname(fileURLToPath(import.meta.url));
8 | const fsproj = path.join(currentDir, 'fsharp/FantomasTools.fsproj');
9 |
10 | // https://vitejs.dev/config/
11 | export default defineConfig({
12 | plugins: [react({ jsxRuntime: 'classic' }), fable({ fsproj })],
13 | server: {
14 | port: 9060,
15 | },
16 | build: {
17 | outDir: 'build',
18 | },
19 | base: '/fantomas-tools/',
20 | preview: {
21 | port: 9060,
22 | },
23 | });
24 |
--------------------------------------------------------------------------------
/src/server/ASTViewer/ASTViewer.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net8.0
4 | true
5 | Lambda
6 | exe
7 |
8 | true
9 | false
10 | true
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/src/server/ASTViewer/Decoders.fs:
--------------------------------------------------------------------------------
1 | module ASTViewer.Server.Decoders
2 |
3 | open Thoth.Json.Net
4 | open ASTViewer.Shared
5 |
6 | let private decodeInput =
7 | Decode.object (fun get ->
8 | { SourceCode = get.Required.Field "sourceCode" Decode.string
9 | Defines = get.Required.Field "defines" (Decode.array Decode.string)
10 | IsFsi = get.Required.Field "isFsi" Decode.bool
11 | Expand = get.Required.Field "expand" Decode.bool })
12 |
13 | let decodeInputRequest json = Decode.fromString decodeInput json
14 |
--------------------------------------------------------------------------------
/src/server/ASTViewer/Encoders.fs:
--------------------------------------------------------------------------------
1 | module ASTViewer.Server.Encoders
2 |
3 | open Fantomas.FCS.Diagnostics
4 | open Fantomas.FCS.Text
5 | open Fantomas.FCS.Parse
6 | open Thoth.Json.Net
7 |
8 | let private mkRange (range: Range) : FantomasTools.Client.Range =
9 | { StartLine = range.StartLine
10 | StartColumn = range.StartColumn
11 | EndLine = range.EndLine
12 | EndColumn = range.EndColumn }
13 |
14 | let private fsharpErrorInfoSeverity =
15 | function
16 | | FSharpDiagnosticSeverity.Warning -> "warning"
17 | | FSharpDiagnosticSeverity.Error -> "error"
18 | | FSharpDiagnosticSeverity.Hidden -> "hidden"
19 | | FSharpDiagnosticSeverity.Info -> "info"
20 |
21 | let private encodeFSharpParserDiagnostic (info: FSharpParserDiagnostic) =
22 | ({ SubCategory = info.SubCategory
23 | Range =
24 | match info.Range with
25 | | None -> mkRange Range.Zero
26 | | Some r -> mkRange r
27 | Severity = fsharpErrorInfoSeverity info.Severity
28 | ErrorNumber = Option.defaultValue 0 info.ErrorNumber
29 | Message = info.Message }
30 | : FantomasTools.Client.Diagnostic)
31 | |> FantomasTools.Client.Diagnostic.Encode
32 |
33 | let encodeResponse ast (diagnostics: FSharpParserDiagnostic list) =
34 | let errors = List.map encodeFSharpParserDiagnostic diagnostics |> Encode.list
35 | Encode.object [ "ast", Encode.string ast; "diagnostics", errors ]
36 |
--------------------------------------------------------------------------------
/src/server/ASTViewer/ExpandedAST.fsi:
--------------------------------------------------------------------------------
1 | module ASTViewer.ExpandedAST
2 |
3 | open Fantomas.FCS.Syntax
4 |
5 | /// Process the ParsedInput tree using reflection to produce a rich "ToString" representation.
6 | /// This string comes very close to usable input if one were to construct the AST manually.
7 | /// Could be slow for large trees and might have some tail recursion problems.
8 | val getExpandedAST: ast: ParsedInput -> string
9 |
--------------------------------------------------------------------------------
/src/server/ASTViewer/GetAST.fs:
--------------------------------------------------------------------------------
1 | module ASTViewer.GetAST
2 |
3 | open System.Reflection
4 | open Thoth.Json.Net
5 | open Fantomas.FCS.Text
6 | open Fantomas.FCS.Parse
7 | open ASTViewer.Shared
8 | open ASTViewer.Server
9 |
10 | module Const =
11 | let sourceSizeLimit = 100 * 1024
12 |
13 | let getVersion () =
14 | let assembly = typeof.Assembly
15 |
16 | match Option.ofObj (assembly.GetCustomAttribute()) with
17 | | Some attr -> attr.InformationalVersion
18 | | None ->
19 | let version = assembly.GetName().Version
20 | sprintf "%i.%i.%i" version.Major version.Minor version.Revision
21 |
22 | []
23 | type ASTResponse =
24 | | Ok of json: string
25 | | TooLarge
26 | | InternalError of string
27 |
28 | let getUntypedAST json : ASTResponse =
29 | let parseRequest = Decoders.decodeInputRequest json
30 |
31 | match parseRequest with
32 | | Ok input when (input.SourceCode.Length < Const.sourceSizeLimit) ->
33 | let ast, errors =
34 | parseFile input.IsFsi (SourceText.ofString input.SourceCode) (List.ofArray input.Defines)
35 |
36 | let astString =
37 | if input.Expand then
38 | try
39 | ExpandedAST.getExpandedAST ast
40 | with ex ->
41 | $"Failed to expand AST, please contribute a fix for this.\nError:%s{ex.Message}"
42 | else
43 | $"%A{ast}"
44 |
45 | Encoders.encodeResponse astString errors |> Encode.toString 2 |> ASTResponse.Ok
46 |
47 | | Ok _ -> ASTResponse.TooLarge
48 | | Error err -> ASTResponse.InternalError $"%A{err}"
49 |
--------------------------------------------------------------------------------
/src/server/ASTViewer/Lambda.fs:
--------------------------------------------------------------------------------
1 | module ASTViewer.Lambda
2 |
3 | open System.Net
4 | open Amazon.Lambda.APIGatewayEvents
5 | open Amazon.Lambda.Core
6 | open AWSLambdaExtensions
7 | open ASTViewer.GetAST
8 | open HttpConstants
9 |
10 | // Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
11 | [)>]
12 | ()
13 |
14 | let GetVersion (_request: APIGatewayProxyRequest) (_context: ILambdaContext) =
15 | let version = getVersion ()
16 | mkAPIGatewayProxyResponse (HttpStatusCode.OK, HeaderValues.TextPlain, version)
17 |
18 | let private mapASTResponse response =
19 | match response with
20 | | ASTResponse.Ok json -> HttpStatusCode.OK, HeaderValues.ApplicationJson, json
21 | | ASTResponse.TooLarge -> HttpStatusCode.RequestEntityTooLarge, HeaderValues.ApplicationText, "File was too large"
22 | | ASTResponse.InternalError error -> HttpStatusCode.InternalServerError, HeaderValues.ApplicationText, error
23 |
24 | let PostUntypedAST (request: APIGatewayProxyRequest) (_context: ILambdaContext) =
25 | let astResponse = getUntypedAST request.Body
26 |
27 | mapASTResponse astResponse |> mkAPIGatewayProxyResponse
28 |
--------------------------------------------------------------------------------
/src/server/ASTViewer/Program.fs:
--------------------------------------------------------------------------------
1 | open Suave
2 | open Suave.Filters
3 | open Suave.Operators
4 | open Suave.Successful
5 | open SuaveExtensions
6 | open ASTViewer.GetAST
7 |
8 | []
9 | let main argv =
10 | let mapASTResponseToWebPart (response: ASTResponse) : WebPart =
11 | match response with
12 | | ASTResponse.Ok body -> (applicationJson >=> OK body)
13 | | ASTResponse.TooLarge -> (applicationText >=> REQUEST_ENTITY_TOO_LARGE "File was too large")
14 | | ASTResponse.InternalError error -> (applicationText >=> INTERNAL_SERVER_ERROR error)
15 |
16 | let untypedAst =
17 | request (fun req ctx ->
18 | async {
19 | let json = req.BodyText
20 | let astResponse = getUntypedAST json
21 | return! (mapASTResponseToWebPart astResponse) ctx
22 | })
23 |
24 | let routes =
25 | [ GET >=> path "/ast-viewer/version" >=> textPlain >=> OK(getVersion ())
26 | POST >=> path "/ast-viewer/untyped-ast" >=> untypedAst ]
27 |
28 | let port =
29 | match List.ofArray argv with
30 | | [ "--port"; port ] -> System.UInt16.Parse port
31 | | _ -> 7412us
32 |
33 | startFantomasTool port routes
34 |
35 | 0
36 |
--------------------------------------------------------------------------------
/src/server/ASTViewer/packages.lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 1,
3 | "dependencies": {
4 | "net8.0": {
5 | "Amazon.Lambda.APIGatewayEvents": {
6 | "type": "Direct",
7 | "requested": "[2.5.0, )",
8 | "resolved": "2.5.0",
9 | "contentHash": "u2M1e8e+eahgwSpa2jhBaakH37EgIZcHqmqpK/9DD/PMygxK5g7LPyUl6SRFnVnmVyD0zvjEh8lYnpYULY6WIQ=="
10 | },
11 | "Amazon.Lambda.Core": {
12 | "type": "Direct",
13 | "requested": "[2.1.0, )",
14 | "resolved": "2.1.0",
15 | "contentHash": "ok06UhfBZw6j6+PhiJR9C0EOMuJvnq8rMCHVkaFmPFrlI/q447ukwGZQKaAKqodV+MNTfpb/iPxjgUPVbSlVVw=="
16 | },
17 | "Amazon.Lambda.Serialization.SystemTextJson": {
18 | "type": "Direct",
19 | "requested": "[2.3.0, )",
20 | "resolved": "2.3.0",
21 | "contentHash": "qgFCDJp5lyUNqCq1z18U7fZ/+rmMyw6RJf9nKfnJrf79YupDj02klQAjxymEYN66NykWXyc68SGkow6fy53hfg==",
22 | "dependencies": {
23 | "Amazon.Lambda.Core": "2.1.0"
24 | }
25 | },
26 | "FSharp.Core": {
27 | "type": "Direct",
28 | "requested": "[8.0.403, )",
29 | "resolved": "8.0.403",
30 | "contentHash": "t1Pvv2++3zMQKKNuiQc1Lz4TCdaBajgG4mLhOE8AoFzborHQ/JbjIaJr6Mrq8m2z15KLu4r6Qz7E3oeACpljTg=="
31 | },
32 | "Microsoft.Net.Http.Headers": {
33 | "type": "Direct",
34 | "requested": "[2.2.8, )",
35 | "resolved": "2.2.8",
36 | "contentHash": "wHdwMv0QDDG2NWDSwax9cjkeQceGC1Qq53a31+31XpvTXVljKXRjWISlMoS/wZYKiqdqzuEvKFKwGHl+mt2jCA==",
37 | "dependencies": {
38 | "Microsoft.Extensions.Primitives": "2.2.0",
39 | "System.Buffers": "4.5.0"
40 | }
41 | },
42 | "Suave": {
43 | "type": "Direct",
44 | "requested": "[2.6.2, )",
45 | "resolved": "2.6.2",
46 | "contentHash": "JNTsgb3FrFnvsp7G93Y9XLIGVa47fG4GZ8un/+/iMMVBTRWl8l6Rlnqjo/PsQP6NojtkrLEM4SLz9AhfQIPNag==",
47 | "dependencies": {
48 | "FSharp.Core": "0.0.0"
49 | }
50 | },
51 | "Thoth.Json.Net": {
52 | "type": "Direct",
53 | "requested": "[8.0.0, )",
54 | "resolved": "8.0.0",
55 | "contentHash": "C/b+8g/xUTJTn7pbKC4bMAOy2tyolXAuHTXguT5TNzDKQ6sjnUfFa9B81fTt9PuUOdWFLyRKlXASuFhSQciJGQ==",
56 | "dependencies": {
57 | "FSharp.Core": "4.7.2",
58 | "Fable.Core": "[3.0.0, 4.0.0)",
59 | "Newtonsoft.Json": "11.0.2"
60 | }
61 | },
62 | "Fable.Core": {
63 | "type": "Transitive",
64 | "resolved": "3.0.0",
65 | "contentHash": "pkCOWJKAkCk36f5+q4F3XqlfsgCJL6i2lTLl4ZZVDswn8rjXo21EBG/gJ296a88LVBkI5LL2VwxQYqGZncomJw==",
66 | "dependencies": {
67 | "FSharp.Core": "4.5.2"
68 | }
69 | },
70 | "Microsoft.Extensions.Primitives": {
71 | "type": "Transitive",
72 | "resolved": "2.2.0",
73 | "contentHash": "azyQtqbm4fSaDzZHD/J+V6oWMFaf2tWP4WEGIYePLCMw3+b2RQdj9ybgbQyjCshcitQKQ4lEDOZjmSlTTrHxUg==",
74 | "dependencies": {
75 | "System.Memory": "4.5.1",
76 | "System.Runtime.CompilerServices.Unsafe": "4.5.1"
77 | }
78 | },
79 | "Microsoft.NETCore.Platforms": {
80 | "type": "Transitive",
81 | "resolved": "1.1.1",
82 | "contentHash": "TMBuzAHpTenGbGgk0SMTwyEkyijY/Eae4ZGsFNYJvAr/LDn1ku3Etp3FPxChmDp5HHF3kzJuoaa08N0xjqAJfQ=="
83 | },
84 | "Microsoft.NETCore.Targets": {
85 | "type": "Transitive",
86 | "resolved": "1.1.3",
87 | "contentHash": "3Wrmi0kJDzClwAC+iBdUBpEKmEle8FQNsCs77fkiOIw/9oYA07bL1EZNX0kQ2OMN3xpwvl0vAtOCYY3ndDNlhQ=="
88 | },
89 | "Newtonsoft.Json": {
90 | "type": "Transitive",
91 | "resolved": "11.0.2",
92 | "contentHash": "IvJe1pj7JHEsP8B8J8DwlMEx8UInrs/x+9oVY+oCD13jpLu4JbJU2WCIsMRn5C4yW9+DgkaO8uiVE5VHKjpmdQ=="
93 | },
94 | "System.Buffers": {
95 | "type": "Transitive",
96 | "resolved": "4.5.0",
97 | "contentHash": "pL2ChpaRRWI/p4LXyy4RgeWlYF2sgfj/pnVMvBqwNFr5cXg7CXNnWZWxrOONLg8VGdFB8oB+EG2Qw4MLgTOe+A=="
98 | },
99 | "System.Collections.Immutable": {
100 | "type": "Transitive",
101 | "resolved": "8.0.0",
102 | "contentHash": "AurL6Y5BA1WotzlEvVaIDpqzpIPvYnnldxru8oXJU2yFxFUy3+pNXjXd1ymO+RA0rq0+590Q8gaz2l3Sr7fmqg=="
103 | },
104 | "System.Diagnostics.DiagnosticSource": {
105 | "type": "Transitive",
106 | "resolved": "8.0.1",
107 | "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg=="
108 | },
109 | "System.Memory": {
110 | "type": "Transitive",
111 | "resolved": "4.6.0",
112 | "contentHash": "OEkbBQoklHngJ8UD8ez2AERSk2g+/qpAaSWWCBFbpH727HxDq5ydVkuncBaKcKfwRqXGWx64dS6G1SUScMsitg=="
113 | },
114 | "System.Runtime": {
115 | "type": "Transitive",
116 | "resolved": "4.3.1",
117 | "contentHash": "abhfv1dTK6NXOmu4bgHIONxHyEqFjW8HwXPmpY9gmll+ix9UNo4XDcmzJn6oLooftxNssVHdJC1pGT9jkSynQg==",
118 | "dependencies": {
119 | "Microsoft.NETCore.Platforms": "1.1.1",
120 | "Microsoft.NETCore.Targets": "1.1.3"
121 | }
122 | },
123 | "System.Runtime.CompilerServices.Unsafe": {
124 | "type": "Transitive",
125 | "resolved": "4.5.1",
126 | "contentHash": "Zh8t8oqolRaFa9vmOZfdQm/qKejdqz0J9kr7o2Fu0vPeoH3BL1EOXipKWwkWtLT1JPzjByrF19fGuFlNbmPpiw=="
127 | },
128 | "fantomas.core": {
129 | "type": "Project",
130 | "dependencies": {
131 | "FSharp.Core": "[8.0.100, )",
132 | "Fantomas.FCS": "[1.0.0, )"
133 | }
134 | },
135 | "fantomas.fcs": {
136 | "type": "Project",
137 | "dependencies": {
138 | "FSharp.Core": "[8.0.100, )",
139 | "System.Collections.Immutable": "[8.0.0, )",
140 | "System.Diagnostics.DiagnosticSource": "[8.0.1, )",
141 | "System.Memory": "[4.6.0, )",
142 | "System.Runtime": "[4.3.1, )"
143 | }
144 | }
145 | }
146 | }
147 | }
--------------------------------------------------------------------------------
/src/server/AWSLambdaExtensions.fs:
--------------------------------------------------------------------------------
1 | module AWSLambdaExtensions
2 |
3 | open System.Collections.Generic
4 | open System.Net
5 | open Amazon.Lambda.APIGatewayEvents
6 | open Microsoft.Net.Http.Headers
7 |
8 | let createHeaders headers =
9 | Seq.fold
10 | (fun (acc: Dictionary) (key, value) ->
11 | acc.[key] <- value
12 | acc)
13 | (Dictionary())
14 | headers
15 |
16 | let mkAPIGatewayProxyResponse (statusCode: HttpStatusCode, contentTypeHeaderValue: string, body: string) =
17 | APIGatewayProxyResponse(
18 | StatusCode = int statusCode,
19 | Body = body,
20 | Headers = createHeaders [ HeaderNames.ContentType, contentTypeHeaderValue ]
21 | )
22 |
--------------------------------------------------------------------------------
/src/server/FantomasOnline.Shared/Decoders.fs:
--------------------------------------------------------------------------------
1 | module FantomasOnline.Server.Shared.Decoders
2 |
3 | open FantomasOnline.Shared
4 | open Thoth.Json.Net
5 |
6 | let optionDecoder: Decoder =
7 | Decode.object (fun get ->
8 | let t = get.Required.Field "$type" Decode.string
9 |
10 | if t = "int" then
11 | get.Required.Field "$value" (Decode.tuple3 Decode.int Decode.string Decode.int)
12 | |> FantomasOption.IntOption
13 | elif t = "bool" then
14 | get.Required.Field "$value" (Decode.tuple3 Decode.int Decode.string Decode.bool)
15 | |> FantomasOption.BoolOption
16 | elif t = "multilineFormatterType" then
17 | get.Required.Field "$value" (Decode.tuple3 Decode.int Decode.string Decode.string)
18 | |> FantomasOption.MultilineFormatterTypeOption
19 | elif t = "endOfLineStyle" then
20 | get.Required.Field "$value" (Decode.tuple3 Decode.int Decode.string Decode.string)
21 | |> FantomasOption.EndOfLineStyleOption
22 | elif t = "multilineBracketStyle" then
23 | get.Required.Field "$value" (Decode.tuple3 Decode.int Decode.string Decode.string)
24 | |> FantomasOption.MultilineBracketStyleOption
25 | else
26 | failwithf $"Could not decode %s{t}")
27 |
28 | let requestDecoder: Decoder =
29 | Decode.object (fun get ->
30 | { SourceCode = get.Required.Field "sourceCode" Decode.string
31 | Options = get.Required.Field "options" (Decode.list optionDecoder |> Decode.map (List.sortBy sortByOption))
32 | IsFsi = get.Required.Field "isFsi" Decode.bool })
33 |
34 | let decodeRequest json = Decode.fromString requestDecoder json
35 |
--------------------------------------------------------------------------------
/src/server/FantomasOnline.Shared/Encoders.fs:
--------------------------------------------------------------------------------
1 | module FantomasOnline.Server.Shared.Encoders
2 |
3 | open Thoth.Json.Net
4 | open FantomasOnline.Shared
5 | open FantomasTools.Client
6 |
7 | let encodeOptions options =
8 | options
9 | |> List.toArray
10 | |> Array.map (fun option ->
11 | match option with
12 | | IntOption(o, k, i) ->
13 | Encode.object
14 | [ "$type", Encode.string "int"
15 | "$value", Encode.tuple3 Encode.int Encode.string Encode.int (o, k, i) ]
16 | | BoolOption(o, k, b) ->
17 | Encode.object
18 | [ "$type", Encode.string "bool"
19 | "$value", Encode.tuple3 Encode.int Encode.string Encode.bool (o, k, b) ]
20 | | MultilineFormatterTypeOption(o, k, v) ->
21 | Encode.object
22 | [ "$type", Encode.string "multilineFormatterType"
23 | "$value", Encode.tuple3 Encode.int Encode.string Encode.string (o, k, v) ]
24 | | EndOfLineStyleOption(o, k, v) ->
25 | Encode.object
26 | [ "$type", Encode.string "endOfLineStyle"
27 | "$value", Encode.tuple3 Encode.int Encode.string Encode.string (o, k, v) ]
28 | | MultilineBracketStyleOption(o, k, v) ->
29 | Encode.object
30 | [ "$type", Encode.string "multilineBracketStyle"
31 | "$value", Encode.tuple3 Encode.int Encode.string Encode.string (o, k, v) ])
32 | |> Encode.array
33 | |> Encode.toString 4
34 |
35 | let encodeFormatResponse (formatResponse: FormatResponse) =
36 | Encode.object
37 | [ "firstFormat", Encode.string formatResponse.FirstFormat
38 | "firstValidation", (formatResponse.FirstValidation |> Array.map Diagnostic.Encode |> Encode.array)
39 | "secondFormat", Encode.option Encode.string formatResponse.SecondFormat
40 | "secondValidation", (formatResponse.SecondValidation |> Array.map Diagnostic.Encode |> Encode.array) ]
41 |
--------------------------------------------------------------------------------
/src/server/FantomasOnline.Shared/Http.fs:
--------------------------------------------------------------------------------
1 | module FantomasOnline.Server.Shared.Http
2 |
3 | open System.Net
4 | open FantomasTools.Client
5 | open Thoth.Json.Net
6 | open HttpConstants
7 | open AWSLambdaExtensions
8 | open FantomasOnline.Server.Shared
9 | open FantomasOnline.Shared
10 |
11 | module Reflection =
12 | open FSharp.Reflection
13 |
14 | let getRecordFields x =
15 | let names = FSharpType.GetRecordFields(x.GetType()) |> Seq.map (fun x -> x.Name)
16 |
17 | let values = FSharpValue.GetRecordFields x
18 | Seq.zip names values |> Seq.toArray
19 |
20 | let mapOptionsToJson (options: FantomasOption list) = options |> Encoders.encodeOptions
21 |
22 | []
23 | type FormatResponse =
24 | | Ok of json: string
25 | | BadRequest of error: string
26 | | InternalError of error: string
27 |
28 | let formatCode
29 | (mapFantomasOptionsToRecord: FantomasOption list -> 'options)
30 | (format: string -> string -> 'options -> Async)
31 | (validate: string -> string -> Async)
32 | (json: string)
33 | : Async
34 | =
35 | async {
36 | let model = Decoders.decodeRequest json
37 |
38 | let configResult =
39 | Result.map (fun r -> r, mapFantomasOptionsToRecord r.Options) model
40 |
41 | match configResult with
42 | | Ok({ SourceCode = code; IsFsi = isFsi }, config) ->
43 | let fileName = if isFsi then "tmp.fsi" else "tmp.fsx"
44 |
45 | try
46 | let! firstFormat = format fileName code config
47 | let! firstValidation = validate fileName firstFormat
48 |
49 | let! secondFormat, secondValidation =
50 | if not (List.isEmpty firstValidation) then
51 | async.Return(None, [])
52 | else
53 | async {
54 | let! secondFormat = format fileName firstFormat config
55 | let! secondValidation = validate fileName secondFormat
56 | return (Some secondFormat, secondValidation)
57 | }
58 |
59 | let response =
60 | { FirstFormat = firstFormat
61 | FirstValidation = Array.ofList firstValidation
62 | SecondFormat = secondFormat
63 | SecondValidation = Array.ofList secondValidation }
64 | |> Encoders.encodeFormatResponse
65 | |> Encode.toString 4
66 |
67 | return FormatResponse.Ok response
68 | with exn ->
69 | return FormatResponse.InternalError(string exn)
70 | | Error err -> return FormatResponse.BadRequest err
71 | }
72 |
73 | let mapFormatResponseToAPIGatewayProxyResponse (response: FormatResponse) =
74 | match response with
75 | | FormatResponse.Ok json -> HttpStatusCode.OK, HeaderValues.ApplicationJson, json
76 | | FormatResponse.BadRequest error -> HttpStatusCode.BadRequest, HeaderValues.ApplicationText, error
77 | | FormatResponse.InternalError error -> HttpStatusCode.InternalServerError, HeaderValues.ApplicationText, error
78 | |> mkAPIGatewayProxyResponse
79 |
--------------------------------------------------------------------------------
/src/server/FantomasOnlineMain/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # Azure Functions localsettings file
5 | local.settings.json
6 |
7 | # User-specific files
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Dd]ebugPublic/
19 | [Rr]elease/
20 | [Rr]eleases/
21 | x64/
22 | x86/
23 | bld/
24 | [Bb]in/
25 | [Oo]bj/
26 | [Ll]og/
27 |
28 | # Visual Studio 2015 cache/options directory
29 | .vs/
30 | # Uncomment if you have tasks that create the project's static files in wwwroot
31 | #wwwroot/
32 |
33 | # MSTest test Results
34 | [Tt]est[Rr]esult*/
35 | [Bb]uild[Ll]og.*
36 |
37 | # NUNIT
38 | *.VisualState.xml
39 | TestResult.xml
40 |
41 | # Build Results of an ATL Project
42 | [Dd]ebugPS/
43 | [Rr]eleasePS/
44 | dlldata.c
45 |
46 | # DNX
47 | project.lock.json
48 | project.fragment.lock.json
49 | artifacts/
50 |
51 | *_i.c
52 | *_p.c
53 | *_i.h
54 | *.ilk
55 | *.meta
56 | *.obj
57 | *.pch
58 | *.pdb
59 | *.pgc
60 | *.pgd
61 | *.rsp
62 | *.sbr
63 | *.tlb
64 | *.tli
65 | *.tlh
66 | *.tmp
67 | *.tmp_proj
68 | *.log
69 | *.vspscc
70 | *.vssscc
71 | .builds
72 | *.pidb
73 | *.svclog
74 | *.scc
75 |
76 | # Chutzpah Test files
77 | _Chutzpah*
78 |
79 | # Visual C++ cache files
80 | ipch/
81 | *.aps
82 | *.ncb
83 | *.opendb
84 | *.opensdf
85 | *.sdf
86 | *.cachefile
87 | *.VC.db
88 | *.VC.VC.opendb
89 |
90 | # Visual Studio profiler
91 | *.psess
92 | *.vsp
93 | *.vspx
94 | *.sap
95 |
96 | # TFS 2012 Local Workspace
97 | $tf/
98 |
99 | # Guidance Automation Toolkit
100 | *.gpState
101 |
102 | # ReSharper is a .NET coding add-in
103 | _ReSharper*/
104 | *.[Rr]e[Ss]harper
105 | *.DotSettings.user
106 |
107 | # JustCode is a .NET coding add-in
108 | .JustCode
109 |
110 | # TeamCity is a build add-in
111 | _TeamCity*
112 |
113 | # DotCover is a Code Coverage Tool
114 | *.dotCover
115 |
116 | # NCrunch
117 | _NCrunch_*
118 | .*crunch*.local.xml
119 | nCrunchTemp_*
120 |
121 | # MightyMoose
122 | *.mm.*
123 | AutoTest.Net/
124 |
125 | # Web workbench (sass)
126 | .sass-cache/
127 |
128 | # Installshield output folder
129 | [Ee]xpress/
130 |
131 | # DocProject is a documentation generator add-in
132 | DocProject/buildhelp/
133 | DocProject/Help/*.HxT
134 | DocProject/Help/*.HxC
135 | DocProject/Help/*.hhc
136 | DocProject/Help/*.hhk
137 | DocProject/Help/*.hhp
138 | DocProject/Help/Html2
139 | DocProject/Help/html
140 |
141 | # Click-Once directory
142 | publish/
143 |
144 | # Publish Web Output
145 | *.[Pp]ublish.xml
146 | *.azurePubxml
147 | #*.pubxml
148 | *.publishproj
149 |
150 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
151 | # checkin your Azure Web App publish settings, but sensitive information contained
152 | # in these scripts will be unencrypted
153 | PublishScripts/
154 |
155 | # NuGet Packages
156 | *.nupkg
157 | # The packages folder can be ignored because of Package Restore
158 | **/packages/*
159 | # except build/, which is used as an MSBuild target.
160 | !**/packages/build/
161 | # Uncomment if necessary however generally it will be regenerated when needed
162 | #!**/packages/repositories.config
163 | # NuGet v3's project.json files produces more ignoreable files
164 | *.nuget.props
165 | *.nuget.targets
166 |
167 | # Microsoft Azure Build Output
168 | csx/
169 | *.build.csdef
170 |
171 | # Microsoft Azure Emulator
172 | ecf/
173 | rcf/
174 |
175 | # Windows Store app package directories and files
176 | AppPackages/
177 | BundleArtifacts/
178 | Package.StoreAssociation.xml
179 | _pkginfo.txt
180 |
181 | # Visual Studio cache files
182 | # files ending in .cache can be ignored
183 | *.[Cc]ache
184 | # but keep track of directories ending in .cache
185 | !*.[Cc]ache/
186 |
187 | # Others
188 | ClientBin/
189 | ~$*
190 | *~
191 | *.dbmdl
192 | *.dbproj.schemaview
193 | *.jfm
194 | *.pfx
195 | *.publishsettings
196 | node_modules/
197 | orleans.codegen.cs
198 |
199 | # Since there are multiple workflows, uncomment next line to ignore bower_components
200 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
201 | #bower_components/
202 |
203 | # RIA/Silverlight projects
204 | Generated_Code/
205 |
206 | # Backup & report files from converting an old project file
207 | # to a newer Visual Studio version. Backup files are not needed,
208 | # because we have git ;-)
209 | _UpgradeReport_Files/
210 | Backup*/
211 | UpgradeLog*.XML
212 | UpgradeLog*.htm
213 |
214 | # SQL Server files
215 | *.mdf
216 | *.ldf
217 |
218 | # Business Intelligence projects
219 | *.rdl.data
220 | *.bim.layout
221 | *.bim_*.settings
222 |
223 | # Microsoft Fakes
224 | FakesAssemblies/
225 |
226 | # GhostDoc plugin setting file
227 | *.GhostDoc.xml
228 |
229 | # Node.js Tools for Visual Studio
230 | .ntvs_analysis.dat
231 |
232 | # Visual Studio 6 build log
233 | *.plg
234 |
235 | # Visual Studio 6 workspace options file
236 | *.opt
237 |
238 | # Visual Studio LightSwitch build output
239 | **/*.HTMLClient/GeneratedArtifacts
240 | **/*.DesktopClient/GeneratedArtifacts
241 | **/*.DesktopClient/ModelManifest.xml
242 | **/*.Server/GeneratedArtifacts
243 | **/*.Server/ModelManifest.xml
244 | _Pvt_Extensions
245 |
246 | # Paket dependency manager
247 | .paket/paket.exe
248 | paket-files/
249 |
250 | # FAKE - F# Make
251 | .fake/
252 |
253 | # JetBrains Rider
254 | .idea/
255 | *.sln.iml
256 |
257 | # CodeRush
258 | .cr/
259 |
260 | # Python Tools for Visual Studio (PTVS)
261 | __pycache__/
262 | *.pyc
--------------------------------------------------------------------------------
/src/server/FantomasOnlineMain/FantomasOnlineMain.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | true
6 | Lambda
7 | exe
8 |
9 | true
10 | false
11 | true
12 | FantomasOnlineMain
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/src/server/FantomasOnlineMain/FormatCode.fs:
--------------------------------------------------------------------------------
1 | module FantomasOnlineMain.FormatCode
2 |
3 | open Fantomas.FCS.Text
4 | open Fantomas.Core
5 | open FantomasOnline.Shared
6 | open FantomasOnline.Server.Shared.Http
7 | open FantomasTools.Client
8 |
9 | let private mapFantomasOptionsToRecord options =
10 | let newValues =
11 | options
12 | |> Seq.map (function
13 | | BoolOption(_, _, v) -> box v
14 | | IntOption(_, _, v) -> box v
15 | | MultilineFormatterTypeOption(_, _, v) ->
16 | MultilineFormatterType.OfConfigString(v)
17 | |> Option.defaultValue CharacterWidth
18 | |> box
19 | | EndOfLineStyleOption(_, _, v) ->
20 | EndOfLineStyle.OfConfigString(v)
21 | |> Option.defaultValue EndOfLineStyle.CRLF
22 | |> box
23 | | MultilineBracketStyleOption(_, _, v) ->
24 | MultilineBracketStyle.OfConfigString(v)
25 | |> Option.defaultValue MultilineBracketStyle.Cramped
26 | |> box)
27 | |> Seq.toArray
28 |
29 | let formatConfigType = typeof
30 | Microsoft.FSharp.Reflection.FSharpValue.MakeRecord(formatConfigType, newValues) :?> FormatConfig
31 |
32 | let private format (fileName: string) code config =
33 | let isSignature = fileName.EndsWith(".fsi")
34 |
35 | async {
36 | let! result = CodeFormatter.FormatDocumentAsync(isSignature, code, config)
37 | return result.Code
38 | }
39 |
40 | let private validate (fileName: string) code =
41 | let isSignature = fileName.EndsWith(".fsi")
42 | let sourceCode = SourceText.ofString code
43 | let _, diagnostics = Fantomas.FCS.Parse.parseFile isSignature sourceCode []
44 |
45 | diagnostics
46 | |> List.map (fun e ->
47 | let range =
48 | match e.Range with
49 | | None ->
50 | { StartLine = 0
51 | StartColumn = 0
52 | EndLine = 0
53 | EndColumn = 0 }
54 | | Some r ->
55 | { StartLine = r.StartLine
56 | StartColumn = r.StartColumn
57 | EndLine = r.EndLine
58 | EndColumn = r.EndColumn }
59 |
60 | { SubCategory = e.SubCategory
61 | Range = range
62 | Severity = $"{e.Severity}".ToLower()
63 | ErrorNumber = Option.defaultValue -1 e.ErrorNumber
64 | Message = e.Message }
65 | : Diagnostic)
66 | |> fun errors -> async { return errors }
67 |
68 | let getVersion () =
69 | let date =
70 | let lastCommitInfo =
71 | sprintf
72 | "%s - %s"
73 | (System.Environment.GetEnvironmentVariable("LAST_COMMIT_TIMESTAMP"))
74 | (System.Environment.GetEnvironmentVariable("LAST_COMMIT_SHA"))
75 |
76 | if lastCommitInfo.Trim() <> "-" then
77 | lastCommitInfo
78 | else
79 | let assembly = typeof.Assembly
80 |
81 | System.IO.FileInfo assembly.Location
82 | |> fun f -> f.LastWriteTime.ToShortDateString()
83 |
84 | $"main branch at %s{date}"
85 |
86 | let getOptions () : string =
87 | Reflection.getRecordFields FormatConfig.Default
88 | |> Seq.indexed
89 | |> Seq.choose (fun (idx, (k: string, v: obj)) ->
90 | match v with
91 | | :? int as i -> FantomasOption.IntOption(idx, k, i) |> Some
92 | | :? bool as b -> FantomasOption.BoolOption(idx, k, b) |> Some
93 | | :? MultilineFormatterType as mft ->
94 | FantomasOption.MultilineFormatterTypeOption(idx, k, (MultilineFormatterType.ToConfigString mft))
95 | |> Some
96 | | :? EndOfLineStyle as eol ->
97 | FantomasOption.EndOfLineStyleOption(idx, k, (EndOfLineStyle.ToConfigString eol))
98 | |> Some
99 | | :? MultilineBracketStyle as mbs ->
100 | FantomasOption.MultilineBracketStyleOption(idx, k, (MultilineBracketStyle.ToConfigString mbs))
101 | |> Some
102 | | _ -> None)
103 | |> Seq.toList
104 | |> mapOptionsToJson
105 |
106 | let formatCode: string -> Async =
107 | formatCode mapFantomasOptionsToRecord format validate
108 |
--------------------------------------------------------------------------------
/src/server/FantomasOnlineMain/Lambda.fs:
--------------------------------------------------------------------------------
1 | module FantomasOnlineMain.Lambda
2 |
3 | open System.Net
4 | open Amazon.Lambda.APIGatewayEvents
5 | open Amazon.Lambda.Core
6 | open AWSLambdaExtensions
7 | open HttpConstants
8 | open FantomasOnline.Server.Shared.Http
9 | open FantomasOnlineMain.FormatCode
10 |
11 | // Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
12 | [)>]
13 | ()
14 |
15 | let GetVersion (_request: APIGatewayProxyRequest) (_context: ILambdaContext) =
16 | mkAPIGatewayProxyResponse (HttpStatusCode.OK, HeaderValues.TextPlain, getVersion ())
17 |
18 | let GetOptions (_request: APIGatewayProxyRequest) (_context: ILambdaContext) =
19 | mkAPIGatewayProxyResponse (HttpStatusCode.OK, HeaderValues.ApplicationJson, getOptions ())
20 |
21 | let PostFormat (request: APIGatewayProxyRequest) (_context: ILambdaContext) =
22 | async {
23 | let! response = formatCode request.Body
24 | return mapFormatResponseToAPIGatewayProxyResponse response
25 | }
26 | |> Async.StartAsTask
27 |
--------------------------------------------------------------------------------
/src/server/FantomasOnlineMain/Program.fs:
--------------------------------------------------------------------------------
1 | open Suave
2 | open Suave.Filters
3 | open Suave.Operators
4 | open Suave.Successful
5 | open Suave.RequestErrors
6 | open SuaveExtensions
7 | open FantomasOnline.Server.Shared.Http
8 |
9 | []
10 | let main argv =
11 | let mapFormatResponseToWebPart (response: FormatResponse) : WebPart =
12 | match response with
13 | | FormatResponse.Ok body -> (applicationJson >=> OK body)
14 | | FormatResponse.BadRequest error -> (applicationText >=> BAD_REQUEST error)
15 | | FormatResponse.InternalError error -> (applicationText >=> INTERNAL_SERVER_ERROR error)
16 |
17 | let formatWebPart =
18 | request (fun req ctx ->
19 | async {
20 | let json = req.BodyText
21 | let! formatResponse = FantomasOnlineMain.FormatCode.formatCode json
22 | return! (mapFormatResponseToWebPart formatResponse) ctx
23 | })
24 |
25 | let routes =
26 | [ GET >=> path "/fantomas/main/version" >=> textPlain >=> OK(FantomasOnlineMain.FormatCode.getVersion ())
27 | GET >=> path "/fantomas/main/options" >=> applicationJson >=> OK(FantomasOnlineMain.FormatCode.getOptions ())
28 | POST >=> path "/fantomas/main/format" >=> formatWebPart ]
29 |
30 | let port =
31 | match List.ofArray argv with
32 | | [ "--port"; port ] -> System.UInt16.Parse port
33 | | _ -> 11084us
34 |
35 | startFantomasTool port routes
36 |
37 | 0
38 |
--------------------------------------------------------------------------------
/src/server/FantomasOnlineMain/host.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0"
3 | }
--------------------------------------------------------------------------------
/src/server/FantomasOnlineMain/packages.lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 1,
3 | "dependencies": {
4 | "net8.0": {
5 | "Amazon.Lambda.APIGatewayEvents": {
6 | "type": "Direct",
7 | "requested": "[2.5.0, )",
8 | "resolved": "2.5.0",
9 | "contentHash": "u2M1e8e+eahgwSpa2jhBaakH37EgIZcHqmqpK/9DD/PMygxK5g7LPyUl6SRFnVnmVyD0zvjEh8lYnpYULY6WIQ=="
10 | },
11 | "Amazon.Lambda.Core": {
12 | "type": "Direct",
13 | "requested": "[2.1.0, )",
14 | "resolved": "2.1.0",
15 | "contentHash": "ok06UhfBZw6j6+PhiJR9C0EOMuJvnq8rMCHVkaFmPFrlI/q447ukwGZQKaAKqodV+MNTfpb/iPxjgUPVbSlVVw=="
16 | },
17 | "Amazon.Lambda.Serialization.SystemTextJson": {
18 | "type": "Direct",
19 | "requested": "[2.3.0, )",
20 | "resolved": "2.3.0",
21 | "contentHash": "qgFCDJp5lyUNqCq1z18U7fZ/+rmMyw6RJf9nKfnJrf79YupDj02klQAjxymEYN66NykWXyc68SGkow6fy53hfg==",
22 | "dependencies": {
23 | "Amazon.Lambda.Core": "2.1.0"
24 | }
25 | },
26 | "FSharp.Core": {
27 | "type": "Direct",
28 | "requested": "[8.0.400, )",
29 | "resolved": "8.0.400",
30 | "contentHash": "kHMdDDmlZl98tujgHCmL8/HNH9VKbxsRMC9s7wbwr4noR40SSa5D4d00yF8cMK52s8jabVuiLLcrUw9r+PkKDQ=="
31 | },
32 | "Microsoft.Net.Http.Headers": {
33 | "type": "Direct",
34 | "requested": "[2.2.8, )",
35 | "resolved": "2.2.8",
36 | "contentHash": "wHdwMv0QDDG2NWDSwax9cjkeQceGC1Qq53a31+31XpvTXVljKXRjWISlMoS/wZYKiqdqzuEvKFKwGHl+mt2jCA==",
37 | "dependencies": {
38 | "Microsoft.Extensions.Primitives": "2.2.0",
39 | "System.Buffers": "4.5.0"
40 | }
41 | },
42 | "Suave": {
43 | "type": "Direct",
44 | "requested": "[2.6.2, )",
45 | "resolved": "2.6.2",
46 | "contentHash": "JNTsgb3FrFnvsp7G93Y9XLIGVa47fG4GZ8un/+/iMMVBTRWl8l6Rlnqjo/PsQP6NojtkrLEM4SLz9AhfQIPNag==",
47 | "dependencies": {
48 | "FSharp.Core": "0.0.0"
49 | }
50 | },
51 | "Thoth.Json.Net": {
52 | "type": "Direct",
53 | "requested": "[8.0.0, )",
54 | "resolved": "8.0.0",
55 | "contentHash": "C/b+8g/xUTJTn7pbKC4bMAOy2tyolXAuHTXguT5TNzDKQ6sjnUfFa9B81fTt9PuUOdWFLyRKlXASuFhSQciJGQ==",
56 | "dependencies": {
57 | "FSharp.Core": "4.7.2",
58 | "Fable.Core": "[3.0.0, 4.0.0)",
59 | "Newtonsoft.Json": "11.0.2"
60 | }
61 | },
62 | "Fable.Core": {
63 | "type": "Transitive",
64 | "resolved": "3.0.0",
65 | "contentHash": "pkCOWJKAkCk36f5+q4F3XqlfsgCJL6i2lTLl4ZZVDswn8rjXo21EBG/gJ296a88LVBkI5LL2VwxQYqGZncomJw==",
66 | "dependencies": {
67 | "FSharp.Core": "4.5.2"
68 | }
69 | },
70 | "Microsoft.Extensions.Primitives": {
71 | "type": "Transitive",
72 | "resolved": "2.2.0",
73 | "contentHash": "azyQtqbm4fSaDzZHD/J+V6oWMFaf2tWP4WEGIYePLCMw3+b2RQdj9ybgbQyjCshcitQKQ4lEDOZjmSlTTrHxUg==",
74 | "dependencies": {
75 | "System.Memory": "4.5.1",
76 | "System.Runtime.CompilerServices.Unsafe": "4.5.1"
77 | }
78 | },
79 | "Microsoft.NETCore.Platforms": {
80 | "type": "Transitive",
81 | "resolved": "1.1.1",
82 | "contentHash": "TMBuzAHpTenGbGgk0SMTwyEkyijY/Eae4ZGsFNYJvAr/LDn1ku3Etp3FPxChmDp5HHF3kzJuoaa08N0xjqAJfQ=="
83 | },
84 | "Microsoft.NETCore.Targets": {
85 | "type": "Transitive",
86 | "resolved": "1.1.3",
87 | "contentHash": "3Wrmi0kJDzClwAC+iBdUBpEKmEle8FQNsCs77fkiOIw/9oYA07bL1EZNX0kQ2OMN3xpwvl0vAtOCYY3ndDNlhQ=="
88 | },
89 | "Newtonsoft.Json": {
90 | "type": "Transitive",
91 | "resolved": "11.0.2",
92 | "contentHash": "IvJe1pj7JHEsP8B8J8DwlMEx8UInrs/x+9oVY+oCD13jpLu4JbJU2WCIsMRn5C4yW9+DgkaO8uiVE5VHKjpmdQ=="
93 | },
94 | "System.Buffers": {
95 | "type": "Transitive",
96 | "resolved": "4.5.0",
97 | "contentHash": "pL2ChpaRRWI/p4LXyy4RgeWlYF2sgfj/pnVMvBqwNFr5cXg7CXNnWZWxrOONLg8VGdFB8oB+EG2Qw4MLgTOe+A=="
98 | },
99 | "System.Collections.Immutable": {
100 | "type": "Transitive",
101 | "resolved": "8.0.0",
102 | "contentHash": "AurL6Y5BA1WotzlEvVaIDpqzpIPvYnnldxru8oXJU2yFxFUy3+pNXjXd1ymO+RA0rq0+590Q8gaz2l3Sr7fmqg=="
103 | },
104 | "System.Diagnostics.DiagnosticSource": {
105 | "type": "Transitive",
106 | "resolved": "8.0.1",
107 | "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg=="
108 | },
109 | "System.Memory": {
110 | "type": "Transitive",
111 | "resolved": "4.6.0",
112 | "contentHash": "OEkbBQoklHngJ8UD8ez2AERSk2g+/qpAaSWWCBFbpH727HxDq5ydVkuncBaKcKfwRqXGWx64dS6G1SUScMsitg=="
113 | },
114 | "System.Runtime": {
115 | "type": "Transitive",
116 | "resolved": "4.3.1",
117 | "contentHash": "abhfv1dTK6NXOmu4bgHIONxHyEqFjW8HwXPmpY9gmll+ix9UNo4XDcmzJn6oLooftxNssVHdJC1pGT9jkSynQg==",
118 | "dependencies": {
119 | "Microsoft.NETCore.Platforms": "1.1.1",
120 | "Microsoft.NETCore.Targets": "1.1.3"
121 | }
122 | },
123 | "System.Runtime.CompilerServices.Unsafe": {
124 | "type": "Transitive",
125 | "resolved": "4.5.1",
126 | "contentHash": "Zh8t8oqolRaFa9vmOZfdQm/qKejdqz0J9kr7o2Fu0vPeoH3BL1EOXipKWwkWtLT1JPzjByrF19fGuFlNbmPpiw=="
127 | },
128 | "fantomas.core": {
129 | "type": "Project",
130 | "dependencies": {
131 | "FSharp.Core": "[8.0.100, )",
132 | "Fantomas.FCS": "[1.0.0, )"
133 | }
134 | },
135 | "fantomas.fcs": {
136 | "type": "Project",
137 | "dependencies": {
138 | "FSharp.Core": "[8.0.100, )",
139 | "System.Collections.Immutable": "[8.0.0, )",
140 | "System.Diagnostics.DiagnosticSource": "[8.0.1, )",
141 | "System.Memory": "[4.6.0, )",
142 | "System.Runtime": "[4.3.1, )"
143 | }
144 | }
145 | }
146 | }
147 | }
--------------------------------------------------------------------------------
/src/server/FantomasOnlinePreview/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # Azure Functions localsettings file
5 | local.settings.json
6 |
7 | # User-specific files
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Dd]ebugPublic/
19 | [Rr]elease/
20 | [Rr]eleases/
21 | x64/
22 | x86/
23 | bld/
24 | [Bb]in/
25 | [Oo]bj/
26 | [Ll]og/
27 |
28 | # Visual Studio 2015 cache/options directory
29 | .vs/
30 | # Uncomment if you have tasks that create the project's static files in wwwroot
31 | #wwwroot/
32 |
33 | # MSTest test Results
34 | [Tt]est[Rr]esult*/
35 | [Bb]uild[Ll]og.*
36 |
37 | # NUNIT
38 | *.VisualState.xml
39 | TestResult.xml
40 |
41 | # Build Results of an ATL Project
42 | [Dd]ebugPS/
43 | [Rr]eleasePS/
44 | dlldata.c
45 |
46 | # DNX
47 | project.lock.json
48 | project.fragment.lock.json
49 | artifacts/
50 |
51 | *_i.c
52 | *_p.c
53 | *_i.h
54 | *.ilk
55 | *.meta
56 | *.obj
57 | *.pch
58 | *.pdb
59 | *.pgc
60 | *.pgd
61 | *.rsp
62 | *.sbr
63 | *.tlb
64 | *.tli
65 | *.tlh
66 | *.tmp
67 | *.tmp_proj
68 | *.log
69 | *.vspscc
70 | *.vssscc
71 | .builds
72 | *.pidb
73 | *.svclog
74 | *.scc
75 |
76 | # Chutzpah Test files
77 | _Chutzpah*
78 |
79 | # Visual C++ cache files
80 | ipch/
81 | *.aps
82 | *.ncb
83 | *.opendb
84 | *.opensdf
85 | *.sdf
86 | *.cachefile
87 | *.VC.db
88 | *.VC.VC.opendb
89 |
90 | # Visual Studio profiler
91 | *.psess
92 | *.vsp
93 | *.vspx
94 | *.sap
95 |
96 | # TFS 2012 Local Workspace
97 | $tf/
98 |
99 | # Guidance Automation Toolkit
100 | *.gpState
101 |
102 | # ReSharper is a .NET coding add-in
103 | _ReSharper*/
104 | *.[Rr]e[Ss]harper
105 | *.DotSettings.user
106 |
107 | # JustCode is a .NET coding add-in
108 | .JustCode
109 |
110 | # TeamCity is a build add-in
111 | _TeamCity*
112 |
113 | # DotCover is a Code Coverage Tool
114 | *.dotCover
115 |
116 | # NCrunch
117 | _NCrunch_*
118 | .*crunch*.local.xml
119 | nCrunchTemp_*
120 |
121 | # MightyMoose
122 | *.mm.*
123 | AutoTest.Net/
124 |
125 | # Web workbench (sass)
126 | .sass-cache/
127 |
128 | # Installshield output folder
129 | [Ee]xpress/
130 |
131 | # DocProject is a documentation generator add-in
132 | DocProject/buildhelp/
133 | DocProject/Help/*.HxT
134 | DocProject/Help/*.HxC
135 | DocProject/Help/*.hhc
136 | DocProject/Help/*.hhk
137 | DocProject/Help/*.hhp
138 | DocProject/Help/Html2
139 | DocProject/Help/html
140 |
141 | # Click-Once directory
142 | publish/
143 |
144 | # Publish Web Output
145 | *.[Pp]ublish.xml
146 | *.azurePubxml
147 | #*.pubxml
148 | *.publishproj
149 |
150 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
151 | # checkin your Azure Web App publish settings, but sensitive information contained
152 | # in these scripts will be unencrypted
153 | PublishScripts/
154 |
155 | # NuGet Packages
156 | *.nupkg
157 | # The packages folder can be ignored because of Package Restore
158 | **/packages/*
159 | # except build/, which is used as an MSBuild target.
160 | !**/packages/build/
161 | # Uncomment if necessary however generally it will be regenerated when needed
162 | #!**/packages/repositories.config
163 | # NuGet v3's project.json files produces more ignoreable files
164 | *.nuget.props
165 | *.nuget.targets
166 |
167 | # Microsoft Azure Build Output
168 | csx/
169 | *.build.csdef
170 |
171 | # Microsoft Azure Emulator
172 | ecf/
173 | rcf/
174 |
175 | # Windows Store app package directories and files
176 | AppPackages/
177 | BundleArtifacts/
178 | Package.StoreAssociation.xml
179 | _pkginfo.txt
180 |
181 | # Visual Studio cache files
182 | # files ending in .cache can be ignored
183 | *.[Cc]ache
184 | # but keep track of directories ending in .cache
185 | !*.[Cc]ache/
186 |
187 | # Others
188 | ClientBin/
189 | ~$*
190 | *~
191 | *.dbmdl
192 | *.dbproj.schemaview
193 | *.jfm
194 | *.pfx
195 | *.publishsettings
196 | node_modules/
197 | orleans.codegen.cs
198 |
199 | # Since there are multiple workflows, uncomment next line to ignore bower_components
200 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
201 | #bower_components/
202 |
203 | # RIA/Silverlight projects
204 | Generated_Code/
205 |
206 | # Backup & report files from converting an old project file
207 | # to a newer Visual Studio version. Backup files are not needed,
208 | # because we have git ;-)
209 | _UpgradeReport_Files/
210 | Backup*/
211 | UpgradeLog*.XML
212 | UpgradeLog*.htm
213 |
214 | # SQL Server files
215 | *.mdf
216 | *.ldf
217 |
218 | # Business Intelligence projects
219 | *.rdl.data
220 | *.bim.layout
221 | *.bim_*.settings
222 |
223 | # Microsoft Fakes
224 | FakesAssemblies/
225 |
226 | # GhostDoc plugin setting file
227 | *.GhostDoc.xml
228 |
229 | # Node.js Tools for Visual Studio
230 | .ntvs_analysis.dat
231 |
232 | # Visual Studio 6 build log
233 | *.plg
234 |
235 | # Visual Studio 6 workspace options file
236 | *.opt
237 |
238 | # Visual Studio LightSwitch build output
239 | **/*.HTMLClient/GeneratedArtifacts
240 | **/*.DesktopClient/GeneratedArtifacts
241 | **/*.DesktopClient/ModelManifest.xml
242 | **/*.Server/GeneratedArtifacts
243 | **/*.Server/ModelManifest.xml
244 | _Pvt_Extensions
245 |
246 | # Paket dependency manager
247 | .paket/paket.exe
248 | paket-files/
249 |
250 | # FAKE - F# Make
251 | .fake/
252 |
253 | # JetBrains Rider
254 | .idea/
255 | *.sln.iml
256 |
257 | # CodeRush
258 | .cr/
259 |
260 | # Python Tools for Visual Studio (PTVS)
261 | __pycache__/
262 | *.pyc
--------------------------------------------------------------------------------
/src/server/FantomasOnlinePreview/FantomasOnlinePreview.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | true
6 | Lambda
7 | exe
8 |
9 | true
10 | false
11 | true
12 | FantomasOnlinePreview
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/src/server/FantomasOnlinePreview/FormatCode.fs:
--------------------------------------------------------------------------------
1 | module FantomasOnlinePreview.FormatCode
2 |
3 | open Fantomas.FCS.Text
4 | open Fantomas.Core
5 | open FantomasOnline.Shared
6 | open FantomasOnline.Server.Shared.Http
7 | open FantomasTools.Client
8 |
9 | let private mapFantomasOptionsToRecord options =
10 | let newValues =
11 | options
12 | |> Seq.map (function
13 | | BoolOption(_, _, v) -> box v
14 | | IntOption(_, _, v) -> box v
15 | | MultilineFormatterTypeOption(_, _, v) ->
16 | MultilineFormatterType.OfConfigString(v)
17 | |> Option.defaultValue CharacterWidth
18 | |> box
19 | | EndOfLineStyleOption(_, _, v) ->
20 | EndOfLineStyle.OfConfigString(v)
21 | |> Option.defaultValue EndOfLineStyle.CRLF
22 | |> box
23 | | MultilineBracketStyleOption(_, _, v) ->
24 | MultilineBracketStyle.OfConfigString(v)
25 | |> Option.defaultValue MultilineBracketStyle.Cramped
26 | |> box)
27 | |> Seq.toArray
28 |
29 | let formatConfigType = typeof
30 | Microsoft.FSharp.Reflection.FSharpValue.MakeRecord(formatConfigType, newValues) :?> FormatConfig
31 |
32 | let private format (fileName: string) code config =
33 | let isSignature = fileName.EndsWith(".fsi")
34 |
35 | async {
36 | let! result = CodeFormatter.FormatDocumentAsync(isSignature, code, config)
37 | return result.Code
38 | }
39 |
40 | let private validate (fileName: string) code =
41 | let isSignature = fileName.EndsWith(".fsi")
42 | let sourceCode = SourceText.ofString code
43 | let _, diagnostics = Fantomas.FCS.Parse.parseFile isSignature sourceCode []
44 |
45 | diagnostics
46 | |> List.map (fun e ->
47 | let range =
48 | match e.Range with
49 | | None -> Range.Zero
50 | | Some r ->
51 | { StartLine = r.StartLine
52 | StartColumn = r.StartColumn
53 | EndLine = r.EndLine
54 | EndColumn = r.EndColumn }
55 |
56 | { SubCategory = e.SubCategory
57 | Range = range
58 | Severity = $"{e.Severity}".ToLower()
59 | ErrorNumber = Option.defaultValue -1 e.ErrorNumber
60 | Message = e.Message }
61 | : Diagnostic)
62 | |> fun errors -> async { return errors }
63 |
64 | let getVersion () =
65 | let date =
66 | let lastCommitInfo =
67 | sprintf
68 | "%s - %s"
69 | (System.Environment.GetEnvironmentVariable("LAST_COMMIT_TIMESTAMP"))
70 | (System.Environment.GetEnvironmentVariable("LAST_COMMIT_SHA"))
71 |
72 | if lastCommitInfo.Trim() <> "-" then
73 | lastCommitInfo
74 | else
75 | let assembly = typeof.Assembly
76 |
77 | System.IO.FileInfo assembly.Location
78 | |> fun f -> f.LastWriteTime.ToShortDateString()
79 |
80 | $"main branch at %s{date}"
81 |
82 | let getOptions () : string =
83 | Reflection.getRecordFields FormatConfig.Default
84 | |> Seq.indexed
85 | |> Seq.choose (fun (idx, (k: string, v: obj)) ->
86 | match v with
87 | | :? int as i -> FantomasOption.IntOption(idx, k, i) |> Some
88 | | :? bool as b -> FantomasOption.BoolOption(idx, k, b) |> Some
89 | | :? MultilineFormatterType as mft ->
90 | FantomasOption.MultilineFormatterTypeOption(idx, k, (MultilineFormatterType.ToConfigString mft))
91 | |> Some
92 | | :? EndOfLineStyle as eol ->
93 | FantomasOption.EndOfLineStyleOption(idx, k, (EndOfLineStyle.ToConfigString eol))
94 | |> Some
95 | | :? MultilineBracketStyle as mbs ->
96 | FantomasOption.MultilineBracketStyleOption(idx, k, (MultilineBracketStyle.ToConfigString mbs))
97 | |> Some
98 | | _ -> None)
99 | |> Seq.toList
100 | |> mapOptionsToJson
101 |
102 | let formatCode: string -> Async =
103 | formatCode mapFantomasOptionsToRecord format validate
104 |
--------------------------------------------------------------------------------
/src/server/FantomasOnlinePreview/Lambda.fs:
--------------------------------------------------------------------------------
1 | module FantomasOnlinePreview.Lambda
2 |
3 | open System.Net
4 | open Amazon.Lambda.APIGatewayEvents
5 | open Amazon.Lambda.Core
6 | open AWSLambdaExtensions
7 | open HttpConstants
8 | open FantomasOnline.Server.Shared.Http
9 | open FantomasOnlinePreview.FormatCode
10 |
11 | // Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
12 | [)>]
13 | ()
14 |
15 | let GetVersion (_request: APIGatewayProxyRequest) (_context: ILambdaContext) =
16 | mkAPIGatewayProxyResponse (HttpStatusCode.OK, HeaderValues.TextPlain, getVersion ())
17 |
18 | let GetOptions (_request: APIGatewayProxyRequest) (_context: ILambdaContext) =
19 | mkAPIGatewayProxyResponse (HttpStatusCode.OK, HeaderValues.ApplicationJson, getOptions ())
20 |
21 | let PostFormat (request: APIGatewayProxyRequest) (_context: ILambdaContext) =
22 | async {
23 | let! response = formatCode request.Body
24 | return mapFormatResponseToAPIGatewayProxyResponse response
25 | }
26 | |> Async.StartAsTask
27 |
--------------------------------------------------------------------------------
/src/server/FantomasOnlinePreview/Program.fs:
--------------------------------------------------------------------------------
1 | open Suave
2 | open Suave.Filters
3 | open Suave.Operators
4 | open Suave.Successful
5 | open Suave.RequestErrors
6 | open SuaveExtensions
7 | open FantomasOnline.Server.Shared.Http
8 |
9 | []
10 | let main argv =
11 | let mapFormatResponseToWebPart (response: FormatResponse) : WebPart =
12 | match response with
13 | | FormatResponse.Ok body -> (applicationJson >=> OK body)
14 | | FormatResponse.BadRequest error -> (applicationText >=> BAD_REQUEST error)
15 | | FormatResponse.InternalError error -> (applicationText >=> INTERNAL_SERVER_ERROR error)
16 |
17 | let formatWebPart =
18 | request (fun req ctx ->
19 | async {
20 | let json = req.BodyText
21 | let! formatResponse = FantomasOnlinePreview.FormatCode.formatCode json
22 | return! (mapFormatResponseToWebPart formatResponse) ctx
23 | })
24 |
25 | let routes =
26 | [ GET >=> path "/fantomas/preview/version" >=> textPlain >=> OK(FantomasOnlinePreview.FormatCode.getVersion ())
27 | GET
28 | >=> path "/fantomas/preview/options"
29 | >=> applicationJson
30 | >=> OK(FantomasOnlinePreview.FormatCode.getOptions ())
31 | POST >=> path "/fantomas/preview/format" >=> formatWebPart ]
32 |
33 | let port =
34 | match List.ofArray argv with
35 | | [ "--port"; port ] -> System.UInt16.Parse port
36 | | _ -> 12007us
37 |
38 | startFantomasTool port routes
39 |
40 | 0
41 |
--------------------------------------------------------------------------------
/src/server/FantomasOnlinePreview/host.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0"
3 | }
--------------------------------------------------------------------------------
/src/server/FantomasOnlinePreview/packages.lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 1,
3 | "dependencies": {
4 | "net8.0": {
5 | "Amazon.Lambda.APIGatewayEvents": {
6 | "type": "Direct",
7 | "requested": "[2.5.0, )",
8 | "resolved": "2.5.0",
9 | "contentHash": "u2M1e8e+eahgwSpa2jhBaakH37EgIZcHqmqpK/9DD/PMygxK5g7LPyUl6SRFnVnmVyD0zvjEh8lYnpYULY6WIQ=="
10 | },
11 | "Amazon.Lambda.Core": {
12 | "type": "Direct",
13 | "requested": "[2.1.0, )",
14 | "resolved": "2.1.0",
15 | "contentHash": "ok06UhfBZw6j6+PhiJR9C0EOMuJvnq8rMCHVkaFmPFrlI/q447ukwGZQKaAKqodV+MNTfpb/iPxjgUPVbSlVVw=="
16 | },
17 | "Amazon.Lambda.Serialization.SystemTextJson": {
18 | "type": "Direct",
19 | "requested": "[2.3.0, )",
20 | "resolved": "2.3.0",
21 | "contentHash": "qgFCDJp5lyUNqCq1z18U7fZ/+rmMyw6RJf9nKfnJrf79YupDj02klQAjxymEYN66NykWXyc68SGkow6fy53hfg==",
22 | "dependencies": {
23 | "Amazon.Lambda.Core": "2.1.0"
24 | }
25 | },
26 | "FSharp.Core": {
27 | "type": "Direct",
28 | "requested": "[8.0.403, )",
29 | "resolved": "8.0.403",
30 | "contentHash": "t1Pvv2++3zMQKKNuiQc1Lz4TCdaBajgG4mLhOE8AoFzborHQ/JbjIaJr6Mrq8m2z15KLu4r6Qz7E3oeACpljTg=="
31 | },
32 | "Microsoft.Net.Http.Headers": {
33 | "type": "Direct",
34 | "requested": "[2.2.8, )",
35 | "resolved": "2.2.8",
36 | "contentHash": "wHdwMv0QDDG2NWDSwax9cjkeQceGC1Qq53a31+31XpvTXVljKXRjWISlMoS/wZYKiqdqzuEvKFKwGHl+mt2jCA==",
37 | "dependencies": {
38 | "Microsoft.Extensions.Primitives": "2.2.0",
39 | "System.Buffers": "4.5.0"
40 | }
41 | },
42 | "Suave": {
43 | "type": "Direct",
44 | "requested": "[2.6.2, )",
45 | "resolved": "2.6.2",
46 | "contentHash": "JNTsgb3FrFnvsp7G93Y9XLIGVa47fG4GZ8un/+/iMMVBTRWl8l6Rlnqjo/PsQP6NojtkrLEM4SLz9AhfQIPNag==",
47 | "dependencies": {
48 | "FSharp.Core": "0.0.0"
49 | }
50 | },
51 | "Thoth.Json.Net": {
52 | "type": "Direct",
53 | "requested": "[8.0.0, )",
54 | "resolved": "8.0.0",
55 | "contentHash": "C/b+8g/xUTJTn7pbKC4bMAOy2tyolXAuHTXguT5TNzDKQ6sjnUfFa9B81fTt9PuUOdWFLyRKlXASuFhSQciJGQ==",
56 | "dependencies": {
57 | "FSharp.Core": "4.7.2",
58 | "Fable.Core": "[3.0.0, 4.0.0)",
59 | "Newtonsoft.Json": "11.0.2"
60 | }
61 | },
62 | "Fable.Core": {
63 | "type": "Transitive",
64 | "resolved": "3.0.0",
65 | "contentHash": "pkCOWJKAkCk36f5+q4F3XqlfsgCJL6i2lTLl4ZZVDswn8rjXo21EBG/gJ296a88LVBkI5LL2VwxQYqGZncomJw==",
66 | "dependencies": {
67 | "FSharp.Core": "4.5.2"
68 | }
69 | },
70 | "Microsoft.Extensions.Primitives": {
71 | "type": "Transitive",
72 | "resolved": "2.2.0",
73 | "contentHash": "azyQtqbm4fSaDzZHD/J+V6oWMFaf2tWP4WEGIYePLCMw3+b2RQdj9ybgbQyjCshcitQKQ4lEDOZjmSlTTrHxUg==",
74 | "dependencies": {
75 | "System.Memory": "4.5.1",
76 | "System.Runtime.CompilerServices.Unsafe": "4.5.1"
77 | }
78 | },
79 | "Microsoft.NETCore.Platforms": {
80 | "type": "Transitive",
81 | "resolved": "1.1.1",
82 | "contentHash": "TMBuzAHpTenGbGgk0SMTwyEkyijY/Eae4ZGsFNYJvAr/LDn1ku3Etp3FPxChmDp5HHF3kzJuoaa08N0xjqAJfQ=="
83 | },
84 | "Microsoft.NETCore.Targets": {
85 | "type": "Transitive",
86 | "resolved": "1.1.3",
87 | "contentHash": "3Wrmi0kJDzClwAC+iBdUBpEKmEle8FQNsCs77fkiOIw/9oYA07bL1EZNX0kQ2OMN3xpwvl0vAtOCYY3ndDNlhQ=="
88 | },
89 | "Newtonsoft.Json": {
90 | "type": "Transitive",
91 | "resolved": "11.0.2",
92 | "contentHash": "IvJe1pj7JHEsP8B8J8DwlMEx8UInrs/x+9oVY+oCD13jpLu4JbJU2WCIsMRn5C4yW9+DgkaO8uiVE5VHKjpmdQ=="
93 | },
94 | "System.Buffers": {
95 | "type": "Transitive",
96 | "resolved": "4.5.0",
97 | "contentHash": "pL2ChpaRRWI/p4LXyy4RgeWlYF2sgfj/pnVMvBqwNFr5cXg7CXNnWZWxrOONLg8VGdFB8oB+EG2Qw4MLgTOe+A=="
98 | },
99 | "System.Collections.Immutable": {
100 | "type": "Transitive",
101 | "resolved": "8.0.0",
102 | "contentHash": "AurL6Y5BA1WotzlEvVaIDpqzpIPvYnnldxru8oXJU2yFxFUy3+pNXjXd1ymO+RA0rq0+590Q8gaz2l3Sr7fmqg=="
103 | },
104 | "System.Diagnostics.DiagnosticSource": {
105 | "type": "Transitive",
106 | "resolved": "8.0.1",
107 | "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg=="
108 | },
109 | "System.Memory": {
110 | "type": "Transitive",
111 | "resolved": "4.6.0",
112 | "contentHash": "OEkbBQoklHngJ8UD8ez2AERSk2g+/qpAaSWWCBFbpH727HxDq5ydVkuncBaKcKfwRqXGWx64dS6G1SUScMsitg=="
113 | },
114 | "System.Runtime": {
115 | "type": "Transitive",
116 | "resolved": "4.3.1",
117 | "contentHash": "abhfv1dTK6NXOmu4bgHIONxHyEqFjW8HwXPmpY9gmll+ix9UNo4XDcmzJn6oLooftxNssVHdJC1pGT9jkSynQg==",
118 | "dependencies": {
119 | "Microsoft.NETCore.Platforms": "1.1.1",
120 | "Microsoft.NETCore.Targets": "1.1.3"
121 | }
122 | },
123 | "System.Runtime.CompilerServices.Unsafe": {
124 | "type": "Transitive",
125 | "resolved": "4.5.1",
126 | "contentHash": "Zh8t8oqolRaFa9vmOZfdQm/qKejdqz0J9kr7o2Fu0vPeoH3BL1EOXipKWwkWtLT1JPzjByrF19fGuFlNbmPpiw=="
127 | },
128 | "fantomas.core": {
129 | "type": "Project",
130 | "dependencies": {
131 | "FSharp.Core": "[8.0.100, )",
132 | "Fantomas.FCS": "[1.0.0, )"
133 | }
134 | },
135 | "fantomas.fcs": {
136 | "type": "Project",
137 | "dependencies": {
138 | "FSharp.Core": "[8.0.100, )",
139 | "System.Collections.Immutable": "[8.0.0, )",
140 | "System.Diagnostics.DiagnosticSource": "[8.0.1, )",
141 | "System.Memory": "[4.6.0, )",
142 | "System.Runtime": "[4.3.1, )"
143 | }
144 | }
145 | }
146 | }
147 | }
--------------------------------------------------------------------------------
/src/server/FantomasOnlineV5/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # Azure Functions localsettings file
5 | local.settings.json
6 |
7 | # User-specific files
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Dd]ebugPublic/
19 | [Rr]elease/
20 | [Rr]eleases/
21 | x64/
22 | x86/
23 | bld/
24 | [Bb]in/
25 | [Oo]bj/
26 | [Ll]og/
27 |
28 | # Visual Studio 2015 cache/options directory
29 | .vs/
30 | # Uncomment if you have tasks that create the project's static files in wwwroot
31 | #wwwroot/
32 |
33 | # MSTest test Results
34 | [Tt]est[Rr]esult*/
35 | [Bb]uild[Ll]og.*
36 |
37 | # NUNIT
38 | *.VisualState.xml
39 | TestResult.xml
40 |
41 | # Build Results of an ATL Project
42 | [Dd]ebugPS/
43 | [Rr]eleasePS/
44 | dlldata.c
45 |
46 | # DNX
47 | project.lock.json
48 | project.fragment.lock.json
49 | artifacts/
50 |
51 | *_i.c
52 | *_p.c
53 | *_i.h
54 | *.ilk
55 | *.meta
56 | *.obj
57 | *.pch
58 | *.pdb
59 | *.pgc
60 | *.pgd
61 | *.rsp
62 | *.sbr
63 | *.tlb
64 | *.tli
65 | *.tlh
66 | *.tmp
67 | *.tmp_proj
68 | *.log
69 | *.vspscc
70 | *.vssscc
71 | .builds
72 | *.pidb
73 | *.svclog
74 | *.scc
75 |
76 | # Chutzpah Test files
77 | _Chutzpah*
78 |
79 | # Visual C++ cache files
80 | ipch/
81 | *.aps
82 | *.ncb
83 | *.opendb
84 | *.opensdf
85 | *.sdf
86 | *.cachefile
87 | *.VC.db
88 | *.VC.VC.opendb
89 |
90 | # Visual Studio profiler
91 | *.psess
92 | *.vsp
93 | *.vspx
94 | *.sap
95 |
96 | # TFS 2012 Local Workspace
97 | $tf/
98 |
99 | # Guidance Automation Toolkit
100 | *.gpState
101 |
102 | # ReSharper is a .NET coding add-in
103 | _ReSharper*/
104 | *.[Rr]e[Ss]harper
105 | *.DotSettings.user
106 |
107 | # JustCode is a .NET coding add-in
108 | .JustCode
109 |
110 | # TeamCity is a build add-in
111 | _TeamCity*
112 |
113 | # DotCover is a Code Coverage Tool
114 | *.dotCover
115 |
116 | # NCrunch
117 | _NCrunch_*
118 | .*crunch*.local.xml
119 | nCrunchTemp_*
120 |
121 | # MightyMoose
122 | *.mm.*
123 | AutoTest.Net/
124 |
125 | # Web workbench (sass)
126 | .sass-cache/
127 |
128 | # Installshield output folder
129 | [Ee]xpress/
130 |
131 | # DocProject is a documentation generator add-in
132 | DocProject/buildhelp/
133 | DocProject/Help/*.HxT
134 | DocProject/Help/*.HxC
135 | DocProject/Help/*.hhc
136 | DocProject/Help/*.hhk
137 | DocProject/Help/*.hhp
138 | DocProject/Help/Html2
139 | DocProject/Help/html
140 |
141 | # Click-Once directory
142 | publish/
143 |
144 | # Publish Web Output
145 | *.[Pp]ublish.xml
146 | *.azurePubxml
147 | #*.pubxml
148 | *.publishproj
149 |
150 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
151 | # checkin your Azure Web App publish settings, but sensitive information contained
152 | # in these scripts will be unencrypted
153 | PublishScripts/
154 |
155 | # NuGet Packages
156 | *.nupkg
157 | # The packages folder can be ignored because of Package Restore
158 | **/packages/*
159 | # except build/, which is used as an MSBuild target.
160 | !**/packages/build/
161 | # Uncomment if necessary however generally it will be regenerated when needed
162 | #!**/packages/repositories.config
163 | # NuGet v3's project.json files produces more ignoreable files
164 | *.nuget.props
165 | *.nuget.targets
166 |
167 | # Microsoft Azure Build Output
168 | csx/
169 | *.build.csdef
170 |
171 | # Microsoft Azure Emulator
172 | ecf/
173 | rcf/
174 |
175 | # Windows Store app package directories and files
176 | AppPackages/
177 | BundleArtifacts/
178 | Package.StoreAssociation.xml
179 | _pkginfo.txt
180 |
181 | # Visual Studio cache files
182 | # files ending in .cache can be ignored
183 | *.[Cc]ache
184 | # but keep track of directories ending in .cache
185 | !*.[Cc]ache/
186 |
187 | # Others
188 | ClientBin/
189 | ~$*
190 | *~
191 | *.dbmdl
192 | *.dbproj.schemaview
193 | *.jfm
194 | *.pfx
195 | *.publishsettings
196 | node_modules/
197 | orleans.codegen.cs
198 |
199 | # Since there are multiple workflows, uncomment next line to ignore bower_components
200 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
201 | #bower_components/
202 |
203 | # RIA/Silverlight projects
204 | Generated_Code/
205 |
206 | # Backup & report files from converting an old project file
207 | # to a newer Visual Studio version. Backup files are not needed,
208 | # because we have git ;-)
209 | _UpgradeReport_Files/
210 | Backup*/
211 | UpgradeLog*.XML
212 | UpgradeLog*.htm
213 |
214 | # SQL Server files
215 | *.mdf
216 | *.ldf
217 |
218 | # Business Intelligence projects
219 | *.rdl.data
220 | *.bim.layout
221 | *.bim_*.settings
222 |
223 | # Microsoft Fakes
224 | FakesAssemblies/
225 |
226 | # GhostDoc plugin setting file
227 | *.GhostDoc.xml
228 |
229 | # Node.js Tools for Visual Studio
230 | .ntvs_analysis.dat
231 |
232 | # Visual Studio 6 build log
233 | *.plg
234 |
235 | # Visual Studio 6 workspace options file
236 | *.opt
237 |
238 | # Visual Studio LightSwitch build output
239 | **/*.HTMLClient/GeneratedArtifacts
240 | **/*.DesktopClient/GeneratedArtifacts
241 | **/*.DesktopClient/ModelManifest.xml
242 | **/*.Server/GeneratedArtifacts
243 | **/*.Server/ModelManifest.xml
244 | _Pvt_Extensions
245 |
246 | # Paket dependency manager
247 | .paket/paket.exe
248 | paket-files/
249 |
250 | # FAKE - F# Make
251 | .fake/
252 |
253 | # JetBrains Rider
254 | .idea/
255 | *.sln.iml
256 |
257 | # CodeRush
258 | .cr/
259 |
260 | # Python Tools for Visual Studio (PTVS)
261 | __pycache__/
262 | *.pyc
--------------------------------------------------------------------------------
/src/server/FantomasOnlineV5/FantomasOnlineV5.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | true
6 | Lambda
7 | exe
8 |
9 | true
10 | true
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/src/server/FantomasOnlineV5/FormatCode.fs:
--------------------------------------------------------------------------------
1 | module FantomasOnlineV5.FormatCode
2 |
3 | open Fantomas.FCS.Parse
4 | open Fantomas.Core
5 | open Fantomas.Core.FormatConfig
6 | open FantomasOnline.Shared
7 | open FantomasOnline.Server.Shared.Http
8 | open FantomasTools.Client
9 |
10 | let private mapFantomasOptionsToRecord options =
11 | let newValues =
12 | options
13 | |> Seq.map (function
14 | | BoolOption(_, _, v) -> box v
15 | | IntOption(_, _, v) -> box v
16 | | MultilineFormatterTypeOption(_, _, v) ->
17 | MultilineFormatterType.OfConfigString(v)
18 | |> Option.defaultValue CharacterWidth
19 | |> box
20 | | EndOfLineStyleOption(_, _, v) ->
21 | EndOfLineStyle.OfConfigString(v)
22 | |> Option.defaultValue EndOfLineStyle.CRLF
23 | |> box
24 | | MultilineBracketStyleOption(_, _, v) ->
25 | MultilineBracketStyle.OfConfigString(v)
26 | |> Option.defaultValue MultilineBracketStyle.Cramped
27 | |> box)
28 | |> Seq.toArray
29 |
30 | let formatConfigType = typeof
31 | Microsoft.FSharp.Reflection.FSharpValue.MakeRecord(formatConfigType, newValues) :?> FormatConfig.FormatConfig
32 |
33 | let private format (fileName: string) code config =
34 | let isSignature = fileName.EndsWith(".fsi")
35 | CodeFormatter.FormatDocumentAsync(isSignature, code, config)
36 |
37 | let private validate (fileName: string) code =
38 | async {
39 | let isSignature = fileName.EndsWith(".fsi")
40 |
41 | let _tree, diagnostics =
42 | parseFile isSignature (FSharp.Compiler.Text.SourceText.ofString code) []
43 |
44 | return
45 | diagnostics
46 | |> List.map (fun (e: FSharpParserDiagnostic) ->
47 | let orZero f = Option.map f e.Range |> Option.defaultValue 0
48 |
49 | { SubCategory = e.SubCategory
50 | Range =
51 | { StartLine = orZero (fun r -> r.StartLine)
52 | StartColumn = orZero (fun r -> r.StartColumn)
53 | EndLine = orZero (fun r -> r.EndLine)
54 | EndColumn = orZero (fun r -> r.EndColumn) }
55 | Severity = $"{e.Severity}".ToLower()
56 | ErrorNumber = Option.defaultValue 0 e.ErrorNumber
57 | Message = e.Message }
58 | : Diagnostic)
59 | }
60 |
61 | let getVersion () =
62 | let assembly = typeof.Assembly
63 |
64 | let version = assembly.GetName().Version
65 | sprintf "%i.%i.%i" version.Major version.Minor version.Build
66 |
67 | let getOptions () : string =
68 | Reflection.getRecordFields FormatConfig.FormatConfig.Default
69 | |> Seq.indexed
70 | |> Seq.choose (fun (idx, (k: string, v: obj)) ->
71 | match v with
72 | | :? int as i -> FantomasOption.IntOption(idx, k, i) |> Some
73 | | :? bool as b -> FantomasOption.BoolOption(idx, k, b) |> Some
74 | | :? MultilineFormatterType as mft ->
75 | FantomasOption.MultilineFormatterTypeOption(idx, k, (MultilineFormatterType.ToConfigString mft))
76 | |> Some
77 | | :? EndOfLineStyle as eol ->
78 | FantomasOption.EndOfLineStyleOption(idx, k, (EndOfLineStyle.ToConfigString eol))
79 | |> Some
80 | | :? MultilineBracketStyle as mbs ->
81 | FantomasOption.MultilineBracketStyleOption(idx, k, (MultilineBracketStyle.ToConfigString mbs))
82 | |> Some
83 | | _ -> None)
84 | |> Seq.toList
85 | |> mapOptionsToJson
86 |
87 | let formatCode: string -> Async =
88 | formatCode mapFantomasOptionsToRecord format validate
89 |
--------------------------------------------------------------------------------
/src/server/FantomasOnlineV5/Lambda.fs:
--------------------------------------------------------------------------------
1 | module FantomasOnlineV5.Lambda
2 |
3 | open System.Net
4 | open Amazon.Lambda.APIGatewayEvents
5 | open Amazon.Lambda.Core
6 | open AWSLambdaExtensions
7 | open HttpConstants
8 | open FantomasOnline.Server.Shared.Http
9 | open FantomasOnlineV5.FormatCode
10 |
11 | // Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
12 | [)>]
13 | ()
14 |
15 | let GetVersion (_request: APIGatewayProxyRequest) (_context: ILambdaContext) =
16 | mkAPIGatewayProxyResponse (HttpStatusCode.OK, HeaderValues.TextPlain, getVersion ())
17 |
18 | let GetOptions (_request: APIGatewayProxyRequest) (_context: ILambdaContext) =
19 | mkAPIGatewayProxyResponse (HttpStatusCode.OK, HeaderValues.ApplicationJson, getOptions ())
20 |
21 | let PostFormat (request: APIGatewayProxyRequest) (_context: ILambdaContext) =
22 | async {
23 | let! response = formatCode request.Body
24 | return mapFormatResponseToAPIGatewayProxyResponse response
25 | }
26 | |> Async.StartAsTask
27 |
--------------------------------------------------------------------------------
/src/server/FantomasOnlineV5/Program.fs:
--------------------------------------------------------------------------------
1 | open Suave
2 | open Suave.Filters
3 | open Suave.Operators
4 | open Suave.Successful
5 | open Suave.RequestErrors
6 | open SuaveExtensions
7 | open FantomasOnline.Server.Shared.Http
8 |
9 | []
10 | let main argv =
11 | let mapFormatResponseToWebPart (response: FormatResponse) : WebPart =
12 | match response with
13 | | FormatResponse.Ok body -> (applicationJson >=> OK body)
14 | | FormatResponse.BadRequest error -> (applicationText >=> BAD_REQUEST error)
15 | | FormatResponse.InternalError error -> (applicationText >=> INTERNAL_SERVER_ERROR error)
16 |
17 | let formatWebPart =
18 | request (fun req ctx ->
19 | async {
20 | let json = req.BodyText
21 | let! formatResponse = FantomasOnlineV5.FormatCode.formatCode json
22 | return! (mapFormatResponseToWebPart formatResponse) ctx
23 | })
24 |
25 | let routes =
26 | [ GET >=> path "/fantomas/v5/version" >=> textPlain >=> OK(FantomasOnlineV5.FormatCode.getVersion ())
27 | GET >=> path "/fantomas/v5/options" >=> applicationJson >=> OK(FantomasOnlineV5.FormatCode.getOptions ())
28 | POST >=> path "/fantomas/v5/format" >=> formatWebPart ]
29 |
30 | let port =
31 | match List.ofArray argv with
32 | | [ "--port"; port ] -> System.UInt16.Parse port
33 | | _ -> 11009us
34 |
35 | startFantomasTool port routes
36 |
37 | 0
38 |
--------------------------------------------------------------------------------
/src/server/FantomasOnlineV5/host.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0"
3 | }
--------------------------------------------------------------------------------
/src/server/FantomasOnlineV5/packages.lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 1,
3 | "dependencies": {
4 | "net8.0": {
5 | "Amazon.Lambda.APIGatewayEvents": {
6 | "type": "Direct",
7 | "requested": "[2.5.0, )",
8 | "resolved": "2.5.0",
9 | "contentHash": "u2M1e8e+eahgwSpa2jhBaakH37EgIZcHqmqpK/9DD/PMygxK5g7LPyUl6SRFnVnmVyD0zvjEh8lYnpYULY6WIQ=="
10 | },
11 | "Amazon.Lambda.Core": {
12 | "type": "Direct",
13 | "requested": "[2.1.0, )",
14 | "resolved": "2.1.0",
15 | "contentHash": "ok06UhfBZw6j6+PhiJR9C0EOMuJvnq8rMCHVkaFmPFrlI/q447ukwGZQKaAKqodV+MNTfpb/iPxjgUPVbSlVVw=="
16 | },
17 | "Amazon.Lambda.Serialization.SystemTextJson": {
18 | "type": "Direct",
19 | "requested": "[2.3.0, )",
20 | "resolved": "2.3.0",
21 | "contentHash": "qgFCDJp5lyUNqCq1z18U7fZ/+rmMyw6RJf9nKfnJrf79YupDj02klQAjxymEYN66NykWXyc68SGkow6fy53hfg==",
22 | "dependencies": {
23 | "Amazon.Lambda.Core": "2.1.0"
24 | }
25 | },
26 | "Fantomas.Core": {
27 | "type": "Direct",
28 | "requested": "[5.2.4, )",
29 | "resolved": "5.2.4",
30 | "contentHash": "Siwn74SoJCIJ8Vqu8IqXYp0rJ2XqZnzvFQJpgoKWCDCnhCTyBPtC5BUSFEGAqqqPO/TZCRx/TnAet+edXwC4HQ==",
31 | "dependencies": {
32 | "FSharp.Core": "6.0.1",
33 | "Fantomas.FCS": "5.2.4"
34 | }
35 | },
36 | "FSharp.Core": {
37 | "type": "Direct",
38 | "requested": "[8.0.403, )",
39 | "resolved": "8.0.403",
40 | "contentHash": "t1Pvv2++3zMQKKNuiQc1Lz4TCdaBajgG4mLhOE8AoFzborHQ/JbjIaJr6Mrq8m2z15KLu4r6Qz7E3oeACpljTg=="
41 | },
42 | "Microsoft.Net.Http.Headers": {
43 | "type": "Direct",
44 | "requested": "[2.2.8, )",
45 | "resolved": "2.2.8",
46 | "contentHash": "wHdwMv0QDDG2NWDSwax9cjkeQceGC1Qq53a31+31XpvTXVljKXRjWISlMoS/wZYKiqdqzuEvKFKwGHl+mt2jCA==",
47 | "dependencies": {
48 | "Microsoft.Extensions.Primitives": "2.2.0",
49 | "System.Buffers": "4.5.0"
50 | }
51 | },
52 | "Suave": {
53 | "type": "Direct",
54 | "requested": "[2.6.2, )",
55 | "resolved": "2.6.2",
56 | "contentHash": "JNTsgb3FrFnvsp7G93Y9XLIGVa47fG4GZ8un/+/iMMVBTRWl8l6Rlnqjo/PsQP6NojtkrLEM4SLz9AhfQIPNag==",
57 | "dependencies": {
58 | "FSharp.Core": "0.0.0"
59 | }
60 | },
61 | "Thoth.Json.Net": {
62 | "type": "Direct",
63 | "requested": "[8.0.0, )",
64 | "resolved": "8.0.0",
65 | "contentHash": "C/b+8g/xUTJTn7pbKC4bMAOy2tyolXAuHTXguT5TNzDKQ6sjnUfFa9B81fTt9PuUOdWFLyRKlXASuFhSQciJGQ==",
66 | "dependencies": {
67 | "FSharp.Core": "4.7.2",
68 | "Fable.Core": "[3.0.0, 4.0.0)",
69 | "Newtonsoft.Json": "11.0.2"
70 | }
71 | },
72 | "Fable.Core": {
73 | "type": "Transitive",
74 | "resolved": "3.0.0",
75 | "contentHash": "pkCOWJKAkCk36f5+q4F3XqlfsgCJL6i2lTLl4ZZVDswn8rjXo21EBG/gJ296a88LVBkI5LL2VwxQYqGZncomJw==",
76 | "dependencies": {
77 | "FSharp.Core": "4.5.2"
78 | }
79 | },
80 | "Fantomas.FCS": {
81 | "type": "Transitive",
82 | "resolved": "5.2.4",
83 | "contentHash": "jqGhrUVHry4lU53vmrG1qTqv83tsyM8uZgYsFEc6tBhl5pSyQnrZCoiCRO19r28I6jPy0m6koabJLUXpJYZy2A==",
84 | "dependencies": {
85 | "FSharp.Core": "6.0.1",
86 | "System.Diagnostics.DiagnosticSource": "7.0.0",
87 | "System.Memory": "4.5.5",
88 | "System.Runtime": "4.3.1"
89 | }
90 | },
91 | "Microsoft.Extensions.Primitives": {
92 | "type": "Transitive",
93 | "resolved": "2.2.0",
94 | "contentHash": "azyQtqbm4fSaDzZHD/J+V6oWMFaf2tWP4WEGIYePLCMw3+b2RQdj9ybgbQyjCshcitQKQ4lEDOZjmSlTTrHxUg==",
95 | "dependencies": {
96 | "System.Memory": "4.5.1",
97 | "System.Runtime.CompilerServices.Unsafe": "4.5.1"
98 | }
99 | },
100 | "Microsoft.NETCore.Platforms": {
101 | "type": "Transitive",
102 | "resolved": "1.1.1",
103 | "contentHash": "TMBuzAHpTenGbGgk0SMTwyEkyijY/Eae4ZGsFNYJvAr/LDn1ku3Etp3FPxChmDp5HHF3kzJuoaa08N0xjqAJfQ=="
104 | },
105 | "Microsoft.NETCore.Targets": {
106 | "type": "Transitive",
107 | "resolved": "1.1.3",
108 | "contentHash": "3Wrmi0kJDzClwAC+iBdUBpEKmEle8FQNsCs77fkiOIw/9oYA07bL1EZNX0kQ2OMN3xpwvl0vAtOCYY3ndDNlhQ=="
109 | },
110 | "Newtonsoft.Json": {
111 | "type": "Transitive",
112 | "resolved": "11.0.2",
113 | "contentHash": "IvJe1pj7JHEsP8B8J8DwlMEx8UInrs/x+9oVY+oCD13jpLu4JbJU2WCIsMRn5C4yW9+DgkaO8uiVE5VHKjpmdQ=="
114 | },
115 | "System.Buffers": {
116 | "type": "Transitive",
117 | "resolved": "4.5.0",
118 | "contentHash": "pL2ChpaRRWI/p4LXyy4RgeWlYF2sgfj/pnVMvBqwNFr5cXg7CXNnWZWxrOONLg8VGdFB8oB+EG2Qw4MLgTOe+A=="
119 | },
120 | "System.Diagnostics.DiagnosticSource": {
121 | "type": "Transitive",
122 | "resolved": "7.0.0",
123 | "contentHash": "9W0ewWDuAyDqS2PigdTxk6jDKonfgscY/hP8hm7VpxYhNHZHKvZTdRckberlFk3VnCmr3xBUyMBut12Q+T2aOw=="
124 | },
125 | "System.Memory": {
126 | "type": "Transitive",
127 | "resolved": "4.5.5",
128 | "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw=="
129 | },
130 | "System.Runtime": {
131 | "type": "Transitive",
132 | "resolved": "4.3.1",
133 | "contentHash": "abhfv1dTK6NXOmu4bgHIONxHyEqFjW8HwXPmpY9gmll+ix9UNo4XDcmzJn6oLooftxNssVHdJC1pGT9jkSynQg==",
134 | "dependencies": {
135 | "Microsoft.NETCore.Platforms": "1.1.1",
136 | "Microsoft.NETCore.Targets": "1.1.3"
137 | }
138 | },
139 | "System.Runtime.CompilerServices.Unsafe": {
140 | "type": "Transitive",
141 | "resolved": "4.5.1",
142 | "contentHash": "Zh8t8oqolRaFa9vmOZfdQm/qKejdqz0J9kr7o2Fu0vPeoH3BL1EOXipKWwkWtLT1JPzjByrF19fGuFlNbmPpiw=="
143 | }
144 | }
145 | }
146 | }
--------------------------------------------------------------------------------
/src/server/FantomasOnlineV6/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # Azure Functions localsettings file
5 | local.settings.json
6 |
7 | # User-specific files
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Dd]ebugPublic/
19 | [Rr]elease/
20 | [Rr]eleases/
21 | x64/
22 | x86/
23 | bld/
24 | [Bb]in/
25 | [Oo]bj/
26 | [Ll]og/
27 |
28 | # Visual Studio 2015 cache/options directory
29 | .vs/
30 | # Uncomment if you have tasks that create the project's static files in wwwroot
31 | #wwwroot/
32 |
33 | # MSTest test Results
34 | [Tt]est[Rr]esult*/
35 | [Bb]uild[Ll]og.*
36 |
37 | # NUNIT
38 | *.VisualState.xml
39 | TestResult.xml
40 |
41 | # Build Results of an ATL Project
42 | [Dd]ebugPS/
43 | [Rr]eleasePS/
44 | dlldata.c
45 |
46 | # DNX
47 | project.lock.json
48 | project.fragment.lock.json
49 | artifacts/
50 |
51 | *_i.c
52 | *_p.c
53 | *_i.h
54 | *.ilk
55 | *.meta
56 | *.obj
57 | *.pch
58 | *.pdb
59 | *.pgc
60 | *.pgd
61 | *.rsp
62 | *.sbr
63 | *.tlb
64 | *.tli
65 | *.tlh
66 | *.tmp
67 | *.tmp_proj
68 | *.log
69 | *.vspscc
70 | *.vssscc
71 | .builds
72 | *.pidb
73 | *.svclog
74 | *.scc
75 |
76 | # Chutzpah Test files
77 | _Chutzpah*
78 |
79 | # Visual C++ cache files
80 | ipch/
81 | *.aps
82 | *.ncb
83 | *.opendb
84 | *.opensdf
85 | *.sdf
86 | *.cachefile
87 | *.VC.db
88 | *.VC.VC.opendb
89 |
90 | # Visual Studio profiler
91 | *.psess
92 | *.vsp
93 | *.vspx
94 | *.sap
95 |
96 | # TFS 2012 Local Workspace
97 | $tf/
98 |
99 | # Guidance Automation Toolkit
100 | *.gpState
101 |
102 | # ReSharper is a .NET coding add-in
103 | _ReSharper*/
104 | *.[Rr]e[Ss]harper
105 | *.DotSettings.user
106 |
107 | # JustCode is a .NET coding add-in
108 | .JustCode
109 |
110 | # TeamCity is a build add-in
111 | _TeamCity*
112 |
113 | # DotCover is a Code Coverage Tool
114 | *.dotCover
115 |
116 | # NCrunch
117 | _NCrunch_*
118 | .*crunch*.local.xml
119 | nCrunchTemp_*
120 |
121 | # MightyMoose
122 | *.mm.*
123 | AutoTest.Net/
124 |
125 | # Web workbench (sass)
126 | .sass-cache/
127 |
128 | # Installshield output folder
129 | [Ee]xpress/
130 |
131 | # DocProject is a documentation generator add-in
132 | DocProject/buildhelp/
133 | DocProject/Help/*.HxT
134 | DocProject/Help/*.HxC
135 | DocProject/Help/*.hhc
136 | DocProject/Help/*.hhk
137 | DocProject/Help/*.hhp
138 | DocProject/Help/Html2
139 | DocProject/Help/html
140 |
141 | # Click-Once directory
142 | publish/
143 |
144 | # Publish Web Output
145 | *.[Pp]ublish.xml
146 | *.azurePubxml
147 | #*.pubxml
148 | *.publishproj
149 |
150 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
151 | # checkin your Azure Web App publish settings, but sensitive information contained
152 | # in these scripts will be unencrypted
153 | PublishScripts/
154 |
155 | # NuGet Packages
156 | *.nupkg
157 | # The packages folder can be ignored because of Package Restore
158 | **/packages/*
159 | # except build/, which is used as an MSBuild target.
160 | !**/packages/build/
161 | # Uncomment if necessary however generally it will be regenerated when needed
162 | #!**/packages/repositories.config
163 | # NuGet v3's project.json files produces more ignoreable files
164 | *.nuget.props
165 | *.nuget.targets
166 |
167 | # Microsoft Azure Build Output
168 | csx/
169 | *.build.csdef
170 |
171 | # Microsoft Azure Emulator
172 | ecf/
173 | rcf/
174 |
175 | # Windows Store app package directories and files
176 | AppPackages/
177 | BundleArtifacts/
178 | Package.StoreAssociation.xml
179 | _pkginfo.txt
180 |
181 | # Visual Studio cache files
182 | # files ending in .cache can be ignored
183 | *.[Cc]ache
184 | # but keep track of directories ending in .cache
185 | !*.[Cc]ache/
186 |
187 | # Others
188 | ClientBin/
189 | ~$*
190 | *~
191 | *.dbmdl
192 | *.dbproj.schemaview
193 | *.jfm
194 | *.pfx
195 | *.publishsettings
196 | node_modules/
197 | orleans.codegen.cs
198 |
199 | # Since there are multiple workflows, uncomment next line to ignore bower_components
200 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
201 | #bower_components/
202 |
203 | # RIA/Silverlight projects
204 | Generated_Code/
205 |
206 | # Backup & report files from converting an old project file
207 | # to a newer Visual Studio version. Backup files are not needed,
208 | # because we have git ;-)
209 | _UpgradeReport_Files/
210 | Backup*/
211 | UpgradeLog*.XML
212 | UpgradeLog*.htm
213 |
214 | # SQL Server files
215 | *.mdf
216 | *.ldf
217 |
218 | # Business Intelligence projects
219 | *.rdl.data
220 | *.bim.layout
221 | *.bim_*.settings
222 |
223 | # Microsoft Fakes
224 | FakesAssemblies/
225 |
226 | # GhostDoc plugin setting file
227 | *.GhostDoc.xml
228 |
229 | # Node.js Tools for Visual Studio
230 | .ntvs_analysis.dat
231 |
232 | # Visual Studio 6 build log
233 | *.plg
234 |
235 | # Visual Studio 6 workspace options file
236 | *.opt
237 |
238 | # Visual Studio LightSwitch build output
239 | **/*.HTMLClient/GeneratedArtifacts
240 | **/*.DesktopClient/GeneratedArtifacts
241 | **/*.DesktopClient/ModelManifest.xml
242 | **/*.Server/GeneratedArtifacts
243 | **/*.Server/ModelManifest.xml
244 | _Pvt_Extensions
245 |
246 | # Paket dependency manager
247 | .paket/paket.exe
248 | paket-files/
249 |
250 | # FAKE - F# Make
251 | .fake/
252 |
253 | # JetBrains Rider
254 | .idea/
255 | *.sln.iml
256 |
257 | # CodeRush
258 | .cr/
259 |
260 | # Python Tools for Visual Studio (PTVS)
261 | __pycache__/
262 | *.pyc
--------------------------------------------------------------------------------
/src/server/FantomasOnlineV6/FantomasOnlineV6.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | true
6 | Lambda
7 | exe
8 |
9 | true
10 | true
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/src/server/FantomasOnlineV6/FormatCode.fs:
--------------------------------------------------------------------------------
1 | module FantomasOnlineV6.FormatCode
2 |
3 | open FantomasTools.Client
4 | open Fantomas.FCS
5 | open Fantomas.FCS.Parse
6 | open Fantomas.Core
7 | open FantomasOnline.Shared
8 | open FantomasOnline.Server.Shared.Http
9 |
10 | let private mapFantomasOptionsToRecord options =
11 | let newValues =
12 | options
13 | |> Seq.map (function
14 | | BoolOption(_, _, v) -> box v
15 | | IntOption(_, _, v) -> box v
16 | | MultilineFormatterTypeOption(_, _, v) ->
17 | MultilineFormatterType.OfConfigString(v)
18 | |> Option.defaultValue CharacterWidth
19 | |> box
20 | | EndOfLineStyleOption(_, _, v) ->
21 | EndOfLineStyle.OfConfigString(v)
22 | |> Option.defaultValue EndOfLineStyle.CRLF
23 | |> box
24 | | MultilineBracketStyleOption(_, _, v) ->
25 | MultilineBracketStyle.OfConfigString(v)
26 | |> Option.defaultValue MultilineBracketStyle.Cramped
27 | |> box)
28 | |> Seq.toArray
29 |
30 | let formatConfigType = typeof
31 | Microsoft.FSharp.Reflection.FSharpValue.MakeRecord(formatConfigType, newValues) :?> FormatConfig
32 |
33 | let private format (fileName: string) code config =
34 | let isSignature = fileName.EndsWith(".fsi")
35 |
36 | async {
37 | let! result = CodeFormatter.FormatDocumentAsync(isSignature, code, config)
38 | return result.Code
39 | }
40 |
41 | let private validate (fileName: string) code =
42 | async {
43 | let isSignature = fileName.EndsWith(".fsi")
44 |
45 | let _tree, diagnostics = parseFile isSignature (Text.SourceText.ofString code) []
46 |
47 | return
48 | diagnostics
49 | |> List.map (fun (e: FSharpParserDiagnostic) ->
50 | let orZero f = Option.map f e.Range |> Option.defaultValue 0
51 |
52 | { SubCategory = e.SubCategory
53 | Range =
54 | { StartLine = orZero (fun r -> r.StartLine)
55 | StartColumn = orZero (fun r -> r.StartColumn)
56 | EndLine = orZero (fun r -> r.EndLine)
57 | EndColumn = orZero (fun r -> r.EndColumn) }
58 | Severity = $"{e.Severity}".ToLower()
59 | ErrorNumber = Option.defaultValue 0 e.ErrorNumber
60 | Message = e.Message }
61 | : Diagnostic)
62 | }
63 |
64 | let getVersion () =
65 | let assembly = typeof.Assembly
66 |
67 | let version = assembly.GetName().Version
68 | sprintf "%i.%i.%i" version.Major version.Minor version.Build
69 |
70 | let getOptions () : string =
71 | Reflection.getRecordFields FormatConfig.Default
72 | |> Seq.indexed
73 | |> Seq.choose (fun (idx, (k: string, v: obj)) ->
74 | match v with
75 | | :? int as i -> FantomasOption.IntOption(idx, k, i) |> Some
76 | | :? bool as b -> FantomasOption.BoolOption(idx, k, b) |> Some
77 | | :? MultilineFormatterType as mft ->
78 | FantomasOption.MultilineFormatterTypeOption(idx, k, (MultilineFormatterType.ToConfigString mft))
79 | |> Some
80 | | :? EndOfLineStyle as eol ->
81 | FantomasOption.EndOfLineStyleOption(idx, k, (EndOfLineStyle.ToConfigString eol))
82 | |> Some
83 | | :? MultilineBracketStyle as mbs ->
84 | FantomasOption.MultilineBracketStyleOption(idx, k, (MultilineBracketStyle.ToConfigString mbs))
85 | |> Some
86 | | _ -> None)
87 | |> Seq.toList
88 | |> mapOptionsToJson
89 |
90 | let formatCode: string -> Async =
91 | formatCode mapFantomasOptionsToRecord format validate
92 |
--------------------------------------------------------------------------------
/src/server/FantomasOnlineV6/Lambda.fs:
--------------------------------------------------------------------------------
1 | module FantomasOnlineV6.Lambda
2 |
3 | open System.Net
4 | open Amazon.Lambda.APIGatewayEvents
5 | open Amazon.Lambda.Core
6 | open AWSLambdaExtensions
7 | open HttpConstants
8 | open FantomasOnline.Server.Shared.Http
9 | open FantomasOnlineV6.FormatCode
10 |
11 | // Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
12 | [)>]
13 | ()
14 |
15 | let GetVersion (_request: APIGatewayProxyRequest) (_context: ILambdaContext) =
16 | mkAPIGatewayProxyResponse (HttpStatusCode.OK, HeaderValues.TextPlain, getVersion ())
17 |
18 | let GetOptions (_request: APIGatewayProxyRequest) (_context: ILambdaContext) =
19 | mkAPIGatewayProxyResponse (HttpStatusCode.OK, HeaderValues.ApplicationJson, getOptions ())
20 |
21 | let PostFormat (request: APIGatewayProxyRequest) (_context: ILambdaContext) =
22 | async {
23 | let! response = formatCode request.Body
24 | return mapFormatResponseToAPIGatewayProxyResponse response
25 | }
26 | |> Async.StartAsTask
27 |
--------------------------------------------------------------------------------
/src/server/FantomasOnlineV6/Program.fs:
--------------------------------------------------------------------------------
1 | open Suave
2 | open Suave.Filters
3 | open Suave.Operators
4 | open Suave.Successful
5 | open Suave.RequestErrors
6 | open SuaveExtensions
7 | open FantomasOnline.Server.Shared.Http
8 |
9 | []
10 | let main argv =
11 | let mapFormatResponseToWebPart (response: FormatResponse) : WebPart =
12 | match response with
13 | | FormatResponse.Ok body -> (applicationJson >=> OK body)
14 | | FormatResponse.BadRequest error -> (applicationText >=> BAD_REQUEST error)
15 | | FormatResponse.InternalError error -> (applicationText >=> INTERNAL_SERVER_ERROR error)
16 |
17 | let formatWebPart =
18 | request (fun req ctx ->
19 | async {
20 | let json = req.BodyText
21 | let! formatResponse = FantomasOnlineV6.FormatCode.formatCode json
22 | return! (mapFormatResponseToWebPart formatResponse) ctx
23 | })
24 |
25 | let routes =
26 | [ GET >=> path "/fantomas/v6/version" >=> textPlain >=> OK(FantomasOnlineV6.FormatCode.getVersion ())
27 | GET >=> path "/fantomas/v6/options" >=> applicationJson >=> OK(FantomasOnlineV6.FormatCode.getOptions ())
28 | POST >=> path "/fantomas/v6/format" >=> formatWebPart ]
29 |
30 | let port =
31 | match List.ofArray argv with
32 | | [ "--port"; port ] -> System.UInt16.Parse port
33 | | _ -> 13042us
34 |
35 | startFantomasTool port routes
36 |
37 | 0
38 |
--------------------------------------------------------------------------------
/src/server/FantomasOnlineV6/host.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0"
3 | }
--------------------------------------------------------------------------------
/src/server/FantomasOnlineV6/packages.lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 1,
3 | "dependencies": {
4 | "net8.0": {
5 | "Amazon.Lambda.APIGatewayEvents": {
6 | "type": "Direct",
7 | "requested": "[2.5.0, )",
8 | "resolved": "2.5.0",
9 | "contentHash": "u2M1e8e+eahgwSpa2jhBaakH37EgIZcHqmqpK/9DD/PMygxK5g7LPyUl6SRFnVnmVyD0zvjEh8lYnpYULY6WIQ=="
10 | },
11 | "Amazon.Lambda.Core": {
12 | "type": "Direct",
13 | "requested": "[2.1.0, )",
14 | "resolved": "2.1.0",
15 | "contentHash": "ok06UhfBZw6j6+PhiJR9C0EOMuJvnq8rMCHVkaFmPFrlI/q447ukwGZQKaAKqodV+MNTfpb/iPxjgUPVbSlVVw=="
16 | },
17 | "Amazon.Lambda.Serialization.SystemTextJson": {
18 | "type": "Direct",
19 | "requested": "[2.3.0, )",
20 | "resolved": "2.3.0",
21 | "contentHash": "qgFCDJp5lyUNqCq1z18U7fZ/+rmMyw6RJf9nKfnJrf79YupDj02klQAjxymEYN66NykWXyc68SGkow6fy53hfg==",
22 | "dependencies": {
23 | "Amazon.Lambda.Core": "2.1.0"
24 | }
25 | },
26 | "Fantomas.Core": {
27 | "type": "Direct",
28 | "requested": "[6.3.16, )",
29 | "resolved": "6.3.16",
30 | "contentHash": "t4x1Y5CKzFe8gXGe8QzrJKsmJszX8E5/o+LTUNCqoA7WX6hUyX69PxL1rGBM1sndEP5ThOVa5f5E6CFR7+Ce+w==",
31 | "dependencies": {
32 | "FSharp.Core": "6.0.1",
33 | "Fantomas.FCS": "6.3.16"
34 | }
35 | },
36 | "FSharp.Core": {
37 | "type": "Direct",
38 | "requested": "[8.0.403, )",
39 | "resolved": "8.0.403",
40 | "contentHash": "t1Pvv2++3zMQKKNuiQc1Lz4TCdaBajgG4mLhOE8AoFzborHQ/JbjIaJr6Mrq8m2z15KLu4r6Qz7E3oeACpljTg=="
41 | },
42 | "Microsoft.Net.Http.Headers": {
43 | "type": "Direct",
44 | "requested": "[2.2.8, )",
45 | "resolved": "2.2.8",
46 | "contentHash": "wHdwMv0QDDG2NWDSwax9cjkeQceGC1Qq53a31+31XpvTXVljKXRjWISlMoS/wZYKiqdqzuEvKFKwGHl+mt2jCA==",
47 | "dependencies": {
48 | "Microsoft.Extensions.Primitives": "2.2.0",
49 | "System.Buffers": "4.5.0"
50 | }
51 | },
52 | "Suave": {
53 | "type": "Direct",
54 | "requested": "[2.6.2, )",
55 | "resolved": "2.6.2",
56 | "contentHash": "JNTsgb3FrFnvsp7G93Y9XLIGVa47fG4GZ8un/+/iMMVBTRWl8l6Rlnqjo/PsQP6NojtkrLEM4SLz9AhfQIPNag==",
57 | "dependencies": {
58 | "FSharp.Core": "0.0.0"
59 | }
60 | },
61 | "Thoth.Json.Net": {
62 | "type": "Direct",
63 | "requested": "[8.0.0, )",
64 | "resolved": "8.0.0",
65 | "contentHash": "C/b+8g/xUTJTn7pbKC4bMAOy2tyolXAuHTXguT5TNzDKQ6sjnUfFa9B81fTt9PuUOdWFLyRKlXASuFhSQciJGQ==",
66 | "dependencies": {
67 | "FSharp.Core": "4.7.2",
68 | "Fable.Core": "[3.0.0, 4.0.0)",
69 | "Newtonsoft.Json": "11.0.2"
70 | }
71 | },
72 | "Fable.Core": {
73 | "type": "Transitive",
74 | "resolved": "3.0.0",
75 | "contentHash": "pkCOWJKAkCk36f5+q4F3XqlfsgCJL6i2lTLl4ZZVDswn8rjXo21EBG/gJ296a88LVBkI5LL2VwxQYqGZncomJw==",
76 | "dependencies": {
77 | "FSharp.Core": "4.5.2"
78 | }
79 | },
80 | "Fantomas.FCS": {
81 | "type": "Transitive",
82 | "resolved": "6.3.16",
83 | "contentHash": "rKkiQsqtGZTqxTg4YwXvRWJJ56GTQ//8ceSggMDeBrqgKSuDndZefp3+bLKqSPzMj2++ZW9XGWgoJkYbGIYe+Q==",
84 | "dependencies": {
85 | "FSharp.Core": "6.0.1",
86 | "System.Collections.Immutable": "7.0.0",
87 | "System.Diagnostics.DiagnosticSource": "7.0.0",
88 | "System.Memory": "4.5.5",
89 | "System.Runtime": "4.3.1"
90 | }
91 | },
92 | "Microsoft.Extensions.Primitives": {
93 | "type": "Transitive",
94 | "resolved": "2.2.0",
95 | "contentHash": "azyQtqbm4fSaDzZHD/J+V6oWMFaf2tWP4WEGIYePLCMw3+b2RQdj9ybgbQyjCshcitQKQ4lEDOZjmSlTTrHxUg==",
96 | "dependencies": {
97 | "System.Memory": "4.5.1",
98 | "System.Runtime.CompilerServices.Unsafe": "4.5.1"
99 | }
100 | },
101 | "Microsoft.NETCore.Platforms": {
102 | "type": "Transitive",
103 | "resolved": "1.1.1",
104 | "contentHash": "TMBuzAHpTenGbGgk0SMTwyEkyijY/Eae4ZGsFNYJvAr/LDn1ku3Etp3FPxChmDp5HHF3kzJuoaa08N0xjqAJfQ=="
105 | },
106 | "Microsoft.NETCore.Targets": {
107 | "type": "Transitive",
108 | "resolved": "1.1.3",
109 | "contentHash": "3Wrmi0kJDzClwAC+iBdUBpEKmEle8FQNsCs77fkiOIw/9oYA07bL1EZNX0kQ2OMN3xpwvl0vAtOCYY3ndDNlhQ=="
110 | },
111 | "Newtonsoft.Json": {
112 | "type": "Transitive",
113 | "resolved": "11.0.2",
114 | "contentHash": "IvJe1pj7JHEsP8B8J8DwlMEx8UInrs/x+9oVY+oCD13jpLu4JbJU2WCIsMRn5C4yW9+DgkaO8uiVE5VHKjpmdQ=="
115 | },
116 | "System.Buffers": {
117 | "type": "Transitive",
118 | "resolved": "4.5.0",
119 | "contentHash": "pL2ChpaRRWI/p4LXyy4RgeWlYF2sgfj/pnVMvBqwNFr5cXg7CXNnWZWxrOONLg8VGdFB8oB+EG2Qw4MLgTOe+A=="
120 | },
121 | "System.Collections.Immutable": {
122 | "type": "Transitive",
123 | "resolved": "7.0.0",
124 | "contentHash": "dQPcs0U1IKnBdRDBkrCTi1FoajSTBzLcVTpjO4MBCMC7f4pDOIPzgBoX8JjG7X6uZRJ8EBxsi8+DR1JuwjnzOQ=="
125 | },
126 | "System.Diagnostics.DiagnosticSource": {
127 | "type": "Transitive",
128 | "resolved": "7.0.0",
129 | "contentHash": "9W0ewWDuAyDqS2PigdTxk6jDKonfgscY/hP8hm7VpxYhNHZHKvZTdRckberlFk3VnCmr3xBUyMBut12Q+T2aOw=="
130 | },
131 | "System.Memory": {
132 | "type": "Transitive",
133 | "resolved": "4.5.5",
134 | "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw=="
135 | },
136 | "System.Runtime": {
137 | "type": "Transitive",
138 | "resolved": "4.3.1",
139 | "contentHash": "abhfv1dTK6NXOmu4bgHIONxHyEqFjW8HwXPmpY9gmll+ix9UNo4XDcmzJn6oLooftxNssVHdJC1pGT9jkSynQg==",
140 | "dependencies": {
141 | "Microsoft.NETCore.Platforms": "1.1.1",
142 | "Microsoft.NETCore.Targets": "1.1.3"
143 | }
144 | },
145 | "System.Runtime.CompilerServices.Unsafe": {
146 | "type": "Transitive",
147 | "resolved": "4.5.1",
148 | "contentHash": "Zh8t8oqolRaFa9vmOZfdQm/qKejdqz0J9kr7o2Fu0vPeoH3BL1EOXipKWwkWtLT1JPzjByrF19fGuFlNbmPpiw=="
149 | }
150 | }
151 | }
152 | }
--------------------------------------------------------------------------------
/src/server/FantomasOnlineV7/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # Azure Functions localsettings file
5 | local.settings.json
6 |
7 | # User-specific files
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Dd]ebugPublic/
19 | [Rr]elease/
20 | [Rr]eleases/
21 | x64/
22 | x86/
23 | bld/
24 | [Bb]in/
25 | [Oo]bj/
26 | [Ll]og/
27 |
28 | # Visual Studio 2015 cache/options directory
29 | .vs/
30 | # Uncomment if you have tasks that create the project's static files in wwwroot
31 | #wwwroot/
32 |
33 | # MSTest test Results
34 | [Tt]est[Rr]esult*/
35 | [Bb]uild[Ll]og.*
36 |
37 | # NUNIT
38 | *.VisualState.xml
39 | TestResult.xml
40 |
41 | # Build Results of an ATL Project
42 | [Dd]ebugPS/
43 | [Rr]eleasePS/
44 | dlldata.c
45 |
46 | # DNX
47 | project.lock.json
48 | project.fragment.lock.json
49 | artifacts/
50 |
51 | *_i.c
52 | *_p.c
53 | *_i.h
54 | *.ilk
55 | *.meta
56 | *.obj
57 | *.pch
58 | *.pdb
59 | *.pgc
60 | *.pgd
61 | *.rsp
62 | *.sbr
63 | *.tlb
64 | *.tli
65 | *.tlh
66 | *.tmp
67 | *.tmp_proj
68 | *.log
69 | *.vspscc
70 | *.vssscc
71 | .builds
72 | *.pidb
73 | *.svclog
74 | *.scc
75 |
76 | # Chutzpah Test files
77 | _Chutzpah*
78 |
79 | # Visual C++ cache files
80 | ipch/
81 | *.aps
82 | *.ncb
83 | *.opendb
84 | *.opensdf
85 | *.sdf
86 | *.cachefile
87 | *.VC.db
88 | *.VC.VC.opendb
89 |
90 | # Visual Studio profiler
91 | *.psess
92 | *.vsp
93 | *.vspx
94 | *.sap
95 |
96 | # TFS 2012 Local Workspace
97 | $tf/
98 |
99 | # Guidance Automation Toolkit
100 | *.gpState
101 |
102 | # ReSharper is a .NET coding add-in
103 | _ReSharper*/
104 | *.[Rr]e[Ss]harper
105 | *.DotSettings.user
106 |
107 | # JustCode is a .NET coding add-in
108 | .JustCode
109 |
110 | # TeamCity is a build add-in
111 | _TeamCity*
112 |
113 | # DotCover is a Code Coverage Tool
114 | *.dotCover
115 |
116 | # NCrunch
117 | _NCrunch_*
118 | .*crunch*.local.xml
119 | nCrunchTemp_*
120 |
121 | # MightyMoose
122 | *.mm.*
123 | AutoTest.Net/
124 |
125 | # Web workbench (sass)
126 | .sass-cache/
127 |
128 | # Installshield output folder
129 | [Ee]xpress/
130 |
131 | # DocProject is a documentation generator add-in
132 | DocProject/buildhelp/
133 | DocProject/Help/*.HxT
134 | DocProject/Help/*.HxC
135 | DocProject/Help/*.hhc
136 | DocProject/Help/*.hhk
137 | DocProject/Help/*.hhp
138 | DocProject/Help/Html2
139 | DocProject/Help/html
140 |
141 | # Click-Once directory
142 | publish/
143 |
144 | # Publish Web Output
145 | *.[Pp]ublish.xml
146 | *.azurePubxml
147 | #*.pubxml
148 | *.publishproj
149 |
150 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
151 | # checkin your Azure Web App publish settings, but sensitive information contained
152 | # in these scripts will be unencrypted
153 | PublishScripts/
154 |
155 | # NuGet Packages
156 | *.nupkg
157 | # The packages folder can be ignored because of Package Restore
158 | **/packages/*
159 | # except build/, which is used as an MSBuild target.
160 | !**/packages/build/
161 | # Uncomment if necessary however generally it will be regenerated when needed
162 | #!**/packages/repositories.config
163 | # NuGet v3's project.json files produces more ignoreable files
164 | *.nuget.props
165 | *.nuget.targets
166 |
167 | # Microsoft Azure Build Output
168 | csx/
169 | *.build.csdef
170 |
171 | # Microsoft Azure Emulator
172 | ecf/
173 | rcf/
174 |
175 | # Windows Store app package directories and files
176 | AppPackages/
177 | BundleArtifacts/
178 | Package.StoreAssociation.xml
179 | _pkginfo.txt
180 |
181 | # Visual Studio cache files
182 | # files ending in .cache can be ignored
183 | *.[Cc]ache
184 | # but keep track of directories ending in .cache
185 | !*.[Cc]ache/
186 |
187 | # Others
188 | ClientBin/
189 | ~$*
190 | *~
191 | *.dbmdl
192 | *.dbproj.schemaview
193 | *.jfm
194 | *.pfx
195 | *.publishsettings
196 | node_modules/
197 | orleans.codegen.cs
198 |
199 | # Since there are multiple workflows, uncomment next line to ignore bower_components
200 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
201 | #bower_components/
202 |
203 | # RIA/Silverlight projects
204 | Generated_Code/
205 |
206 | # Backup & report files from converting an old project file
207 | # to a newer Visual Studio version. Backup files are not needed,
208 | # because we have git ;-)
209 | _UpgradeReport_Files/
210 | Backup*/
211 | UpgradeLog*.XML
212 | UpgradeLog*.htm
213 |
214 | # SQL Server files
215 | *.mdf
216 | *.ldf
217 |
218 | # Business Intelligence projects
219 | *.rdl.data
220 | *.bim.layout
221 | *.bim_*.settings
222 |
223 | # Microsoft Fakes
224 | FakesAssemblies/
225 |
226 | # GhostDoc plugin setting file
227 | *.GhostDoc.xml
228 |
229 | # Node.js Tools for Visual Studio
230 | .ntvs_analysis.dat
231 |
232 | # Visual Studio 6 build log
233 | *.plg
234 |
235 | # Visual Studio 6 workspace options file
236 | *.opt
237 |
238 | # Visual Studio LightSwitch build output
239 | **/*.HTMLClient/GeneratedArtifacts
240 | **/*.DesktopClient/GeneratedArtifacts
241 | **/*.DesktopClient/ModelManifest.xml
242 | **/*.Server/GeneratedArtifacts
243 | **/*.Server/ModelManifest.xml
244 | _Pvt_Extensions
245 |
246 | # Paket dependency manager
247 | .paket/paket.exe
248 | paket-files/
249 |
250 | # FAKE - F# Make
251 | .fake/
252 |
253 | # JetBrains Rider
254 | .idea/
255 | *.sln.iml
256 |
257 | # CodeRush
258 | .cr/
259 |
260 | # Python Tools for Visual Studio (PTVS)
261 | __pycache__/
262 | *.pyc
--------------------------------------------------------------------------------
/src/server/FantomasOnlineV7/FantomasOnlineV7.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | true
6 | Lambda
7 | exe
8 |
9 | true
10 | true
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/src/server/FantomasOnlineV7/FormatCode.fs:
--------------------------------------------------------------------------------
1 | module FantomasOnlineV7.FormatCode
2 |
3 | open FantomasTools.Client
4 | open Fantomas.FCS
5 | open Fantomas.FCS.Parse
6 | open Fantomas.Core
7 | open FantomasOnline.Shared
8 | open FantomasOnline.Server.Shared.Http
9 |
10 | let private mapFantomasOptionsToRecord options =
11 | let newValues =
12 | options
13 | |> Seq.map (function
14 | | BoolOption(_, _, v) -> box v
15 | | IntOption(_, _, v) -> box v
16 | | MultilineFormatterTypeOption(_, _, v) ->
17 | MultilineFormatterType.OfConfigString(v)
18 | |> Option.defaultValue CharacterWidth
19 | |> box
20 | | EndOfLineStyleOption(_, _, v) ->
21 | EndOfLineStyle.OfConfigString(v)
22 | |> Option.defaultValue EndOfLineStyle.CRLF
23 | |> box
24 | | MultilineBracketStyleOption(_, _, v) ->
25 | MultilineBracketStyle.OfConfigString(v)
26 | |> Option.defaultValue MultilineBracketStyle.Cramped
27 | |> box)
28 | |> Seq.toArray
29 |
30 | let formatConfigType = typeof
31 | Microsoft.FSharp.Reflection.FSharpValue.MakeRecord(formatConfigType, newValues) :?> FormatConfig
32 |
33 | let private format (fileName: string) code config =
34 | let isSignature = fileName.EndsWith(".fsi")
35 |
36 | async {
37 | let! result = CodeFormatter.FormatDocumentAsync(isSignature, code, config)
38 | return result.Code
39 | }
40 |
41 | let private validate (fileName: string) code =
42 | async {
43 | let isSignature = fileName.EndsWith(".fsi")
44 |
45 | let _tree, diagnostics = parseFile isSignature (Text.SourceText.ofString code) []
46 |
47 | return
48 | diagnostics
49 | |> List.map (fun (e: FSharpParserDiagnostic) ->
50 | let orZero f = Option.map f e.Range |> Option.defaultValue 0
51 |
52 | { SubCategory = e.SubCategory
53 | Range =
54 | { StartLine = orZero (fun r -> r.StartLine)
55 | StartColumn = orZero (fun r -> r.StartColumn)
56 | EndLine = orZero (fun r -> r.EndLine)
57 | EndColumn = orZero (fun r -> r.EndColumn) }
58 | Severity = $"{e.Severity}".ToLower()
59 | ErrorNumber = Option.defaultValue 0 e.ErrorNumber
60 | Message = e.Message }
61 | : Diagnostic)
62 | }
63 |
64 | let getVersion () =
65 | let assembly = typeof.Assembly
66 |
67 | let version = assembly.GetName().Version
68 | sprintf "%i.%i.%i" version.Major version.Minor version.Build
69 |
70 | let getOptions () : string =
71 | Reflection.getRecordFields FormatConfig.Default
72 | |> Seq.indexed
73 | |> Seq.choose (fun (idx, (k: string, v: obj)) ->
74 | match v with
75 | | :? int as i -> FantomasOption.IntOption(idx, k, i) |> Some
76 | | :? bool as b -> FantomasOption.BoolOption(idx, k, b) |> Some
77 | | :? MultilineFormatterType as mft ->
78 | FantomasOption.MultilineFormatterTypeOption(idx, k, (MultilineFormatterType.ToConfigString mft))
79 | |> Some
80 | | :? EndOfLineStyle as eol ->
81 | FantomasOption.EndOfLineStyleOption(idx, k, (EndOfLineStyle.ToConfigString eol))
82 | |> Some
83 | | :? MultilineBracketStyle as mbs ->
84 | FantomasOption.MultilineBracketStyleOption(idx, k, (MultilineBracketStyle.ToConfigString mbs))
85 | |> Some
86 | | _ -> None)
87 | |> Seq.toList
88 | |> mapOptionsToJson
89 |
90 | let formatCode: string -> Async =
91 | formatCode mapFantomasOptionsToRecord format validate
92 |
--------------------------------------------------------------------------------
/src/server/FantomasOnlineV7/Lambda.fs:
--------------------------------------------------------------------------------
1 | module FantomasOnlineV7.Lambda
2 |
3 | open System.Net
4 | open Amazon.Lambda.APIGatewayEvents
5 | open Amazon.Lambda.Core
6 | open AWSLambdaExtensions
7 | open HttpConstants
8 | open FantomasOnline.Server.Shared.Http
9 | open FantomasOnlineV7.FormatCode
10 |
11 | // Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
12 | [)>]
13 | ()
14 |
15 | let GetVersion (_request: APIGatewayProxyRequest) (_context: ILambdaContext) =
16 | mkAPIGatewayProxyResponse (HttpStatusCode.OK, HeaderValues.TextPlain, getVersion ())
17 |
18 | let GetOptions (_request: APIGatewayProxyRequest) (_context: ILambdaContext) =
19 | mkAPIGatewayProxyResponse (HttpStatusCode.OK, HeaderValues.ApplicationJson, getOptions ())
20 |
21 | let PostFormat (request: APIGatewayProxyRequest) (_context: ILambdaContext) =
22 | async {
23 | let! response = formatCode request.Body
24 | return mapFormatResponseToAPIGatewayProxyResponse response
25 | }
26 | |> Async.StartAsTask
27 |
--------------------------------------------------------------------------------
/src/server/FantomasOnlineV7/Program.fs:
--------------------------------------------------------------------------------
1 | open Suave
2 | open Suave.Filters
3 | open Suave.Operators
4 | open Suave.Successful
5 | open Suave.RequestErrors
6 | open SuaveExtensions
7 | open FantomasOnline.Server.Shared.Http
8 |
9 | []
10 | let main argv =
11 | let mapFormatResponseToWebPart (response: FormatResponse) : WebPart =
12 | match response with
13 | | FormatResponse.Ok body -> (applicationJson >=> OK body)
14 | | FormatResponse.BadRequest error -> (applicationText >=> BAD_REQUEST error)
15 | | FormatResponse.InternalError error -> (applicationText >=> INTERNAL_SERVER_ERROR error)
16 |
17 | let formatWebPart =
18 | request (fun req ctx ->
19 | async {
20 | let json = req.BodyText
21 | let! formatResponse = FantomasOnlineV7.FormatCode.formatCode json
22 | return! (mapFormatResponseToWebPart formatResponse) ctx
23 | })
24 |
25 | let routes =
26 | [ GET >=> path "/fantomas/v7/version" >=> textPlain >=> OK(FantomasOnlineV7.FormatCode.getVersion ())
27 | GET >=> path "/fantomas/v7/options" >=> applicationJson >=> OK(FantomasOnlineV7.FormatCode.getOptions ())
28 | POST >=> path "/fantomas/v7/format" >=> formatWebPart ]
29 |
30 | let port =
31 | match List.ofArray argv with
32 | | [ "--port"; port ] -> System.UInt16.Parse port
33 | | _ -> 10707us
34 |
35 | startFantomasTool port routes
36 |
37 | 0
38 |
--------------------------------------------------------------------------------
/src/server/FantomasOnlineV7/host.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0"
3 | }
--------------------------------------------------------------------------------
/src/server/HttpConstants.fs:
--------------------------------------------------------------------------------
1 | module HttpConstants
2 |
3 | []
4 | module HeaderValues =
5 | []
6 | let ApplicationText = "application/text"
7 |
8 | []
9 | let TextPlain = "text/plain"
10 |
11 | []
12 | let ApplicationJson = "application/json"
13 |
--------------------------------------------------------------------------------
/src/server/OakViewer/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # Azure Functions localsettings file
5 | local.settings.json
6 |
7 | # User-specific files
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Dd]ebugPublic/
19 | [Rr]elease/
20 | [Rr]eleases/
21 | x64/
22 | x86/
23 | bld/
24 | [Bb]in/
25 | [Oo]bj/
26 | [Ll]og/
27 |
28 | # Visual Studio 2015 cache/options directory
29 | .vs/
30 | # Uncomment if you have tasks that create the project's static files in wwwroot
31 | #wwwroot/
32 |
33 | # MSTest test Results
34 | [Tt]est[Rr]esult*/
35 | [Bb]uild[Ll]og.*
36 |
37 | # NUNIT
38 | *.VisualState.xml
39 | TestResult.xml
40 |
41 | # Build Results of an ATL Project
42 | [Dd]ebugPS/
43 | [Rr]eleasePS/
44 | dlldata.c
45 |
46 | # DNX
47 | project.lock.json
48 | project.fragment.lock.json
49 | artifacts/
50 |
51 | *_i.c
52 | *_p.c
53 | *_i.h
54 | *.ilk
55 | *.meta
56 | *.obj
57 | *.pch
58 | *.pdb
59 | *.pgc
60 | *.pgd
61 | *.rsp
62 | *.sbr
63 | *.tlb
64 | *.tli
65 | *.tlh
66 | *.tmp
67 | *.tmp_proj
68 | *.log
69 | *.vspscc
70 | *.vssscc
71 | .builds
72 | *.pidb
73 | *.svclog
74 | *.scc
75 |
76 | # Chutzpah Test files
77 | _Chutzpah*
78 |
79 | # Visual C++ cache files
80 | ipch/
81 | *.aps
82 | *.ncb
83 | *.opendb
84 | *.opensdf
85 | *.sdf
86 | *.cachefile
87 | *.VC.db
88 | *.VC.VC.opendb
89 |
90 | # Visual Studio profiler
91 | *.psess
92 | *.vsp
93 | *.vspx
94 | *.sap
95 |
96 | # TFS 2012 Local Workspace
97 | $tf/
98 |
99 | # Guidance Automation Toolkit
100 | *.gpState
101 |
102 | # ReSharper is a .NET coding add-in
103 | _ReSharper*/
104 | *.[Rr]e[Ss]harper
105 | *.DotSettings.user
106 |
107 | # JustCode is a .NET coding add-in
108 | .JustCode
109 |
110 | # TeamCity is a build add-in
111 | _TeamCity*
112 |
113 | # DotCover is a Code Coverage Tool
114 | *.dotCover
115 |
116 | # NCrunch
117 | _NCrunch_*
118 | .*crunch*.local.xml
119 | nCrunchTemp_*
120 |
121 | # MightyMoose
122 | *.mm.*
123 | AutoTest.Net/
124 |
125 | # Web workbench (sass)
126 | .sass-cache/
127 |
128 | # Installshield output folder
129 | [Ee]xpress/
130 |
131 | # DocProject is a documentation generator add-in
132 | DocProject/buildhelp/
133 | DocProject/Help/*.HxT
134 | DocProject/Help/*.HxC
135 | DocProject/Help/*.hhc
136 | DocProject/Help/*.hhk
137 | DocProject/Help/*.hhp
138 | DocProject/Help/Html2
139 | DocProject/Help/html
140 |
141 | # Click-Once directory
142 | publish/
143 |
144 | # Publish Web Output
145 | *.[Pp]ublish.xml
146 | *.azurePubxml
147 | # TODO: Comment the next line if you want to checkin your web deploy settings
148 | # but database connection strings (with potential passwords) will be unencrypted
149 | #*.pubxml
150 | *.publishproj
151 |
152 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
153 | # checkin your Azure Web App publish settings, but sensitive information contained
154 | # in these scripts will be unencrypted
155 | PublishScripts/
156 |
157 | # NuGet Packages
158 | *.nupkg
159 | # The packages folder can be ignored because of Package Restore
160 | **/packages/*
161 | # except build/, which is used as an MSBuild target.
162 | !**/packages/build/
163 | # Uncomment if necessary however generally it will be regenerated when needed
164 | #!**/packages/repositories.config
165 | # NuGet v3's project.json files produces more ignoreable files
166 | *.nuget.props
167 | *.nuget.targets
168 |
169 | # Microsoft Azure Build Output
170 | csx/
171 | *.build.csdef
172 |
173 | # Microsoft Azure Emulator
174 | ecf/
175 | rcf/
176 |
177 | # Windows Store app package directories and files
178 | AppPackages/
179 | BundleArtifacts/
180 | Package.StoreAssociation.xml
181 | _pkginfo.txt
182 |
183 | # Visual Studio cache files
184 | # files ending in .cache can be ignored
185 | *.[Cc]ache
186 | # but keep track of directories ending in .cache
187 | !*.[Cc]ache/
188 |
189 | # Others
190 | ClientBin/
191 | ~$*
192 | *~
193 | *.dbmdl
194 | *.dbproj.schemaview
195 | *.jfm
196 | *.pfx
197 | *.publishsettings
198 | node_modules/
199 | orleans.codegen.cs
200 |
201 | # Since there are multiple workflows, uncomment next line to ignore bower_components
202 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
203 | #bower_components/
204 |
205 | # RIA/Silverlight projects
206 | Generated_Code/
207 |
208 | # Backup & report files from converting an old project file
209 | # to a newer Visual Studio version. Backup files are not needed,
210 | # because we have git ;-)
211 | _UpgradeReport_Files/
212 | Backup*/
213 | UpgradeLog*.XML
214 | UpgradeLog*.htm
215 |
216 | # SQL Server files
217 | *.mdf
218 | *.ldf
219 |
220 | # Business Intelligence projects
221 | *.rdl.data
222 | *.bim.layout
223 | *.bim_*.settings
224 |
225 | # Microsoft Fakes
226 | FakesAssemblies/
227 |
228 | # GhostDoc plugin setting file
229 | *.GhostDoc.xml
230 |
231 | # Node.js Tools for Visual Studio
232 | .ntvs_analysis.dat
233 |
234 | # Visual Studio 6 build log
235 | *.plg
236 |
237 | # Visual Studio 6 workspace options file
238 | *.opt
239 |
240 | # Visual Studio LightSwitch build output
241 | **/*.HTMLClient/GeneratedArtifacts
242 | **/*.DesktopClient/GeneratedArtifacts
243 | **/*.DesktopClient/ModelManifest.xml
244 | **/*.Server/GeneratedArtifacts
245 | **/*.Server/ModelManifest.xml
246 | _Pvt_Extensions
247 |
248 | # Paket dependency manager
249 | .paket/paket.exe
250 | paket-files/
251 |
252 | # FAKE - F# Make
253 | .fake/
254 |
255 | # JetBrains Rider
256 | .idea/
257 | *.sln.iml
258 |
259 | # CodeRush
260 | .cr/
261 |
262 | # Python Tools for Visual Studio (PTVS)
263 | __pycache__/
264 | *.pyc
--------------------------------------------------------------------------------
/src/server/OakViewer/Decoders.fs:
--------------------------------------------------------------------------------
1 | module OakViewer.Server.Decoders
2 |
3 | open Thoth.Json.Net
4 | open OakViewer
5 |
6 | let private parseRequestDecoder: Decoder =
7 | Decode.object (fun get ->
8 | { SourceCode = get.Required.Field "sourceCode" Decode.string
9 | Defines = get.Required.Field "defines" (Decode.array Decode.string)
10 | IsFsi = get.Required.Field "isFsi" Decode.bool })
11 |
12 | let decodeParseRequest value = Decode.fromString parseRequestDecoder value
13 |
--------------------------------------------------------------------------------
/src/server/OakViewer/Encoders.fs:
--------------------------------------------------------------------------------
1 | module internal OakViewer.Encoders
2 |
3 | open Thoth.Json.Net
4 | open Fantomas.FCS.Diagnostics
5 | open Fantomas.FCS.Text
6 | open Fantomas.FCS.Parse
7 | open Fantomas.Core
8 | open Fantomas.Core.SyntaxOak
9 |
10 | let encodeRange (m: range) =
11 | Encode.object
12 | [ "startLine", Encode.int m.StartLine
13 | "startColumn", Encode.int m.StartColumn
14 | "endLine", Encode.int m.EndLine
15 | "endColumn", Encode.int m.EndColumn ]
16 |
17 | let encodeTriviaNode (triviaNode: TriviaNode) : JsonValue =
18 | let contentType, content =
19 | match triviaNode.Content with
20 | | CommentOnSingleLine comment -> "commentOnSingleLine", Some comment
21 | | LineCommentAfterSourceCode comment -> "lineCommentAfterSourceCode", Some comment
22 | | BlockComment(comment, _, _) -> "blockComment", Some comment
23 | | Newline -> "newline", None
24 | | Directive directive -> "directive", Some directive
25 | | Cursor -> "cursor", None
26 |
27 | Encode.object
28 | [ "range", encodeRange triviaNode.Range
29 | "type", Encode.string contentType
30 | "content", Encode.option Encode.string content ]
31 |
32 | let rec encodeNode (node: Node) (continuation: JsonValue -> JsonValue) : JsonValue =
33 | let continuations = List.map encodeNode (Array.toList node.Children)
34 |
35 | let text =
36 | match node with
37 | | :? SingleTextNode as stn ->
38 | if stn.Text.Length < 13 then
39 | stn.Text
40 | else
41 | sprintf "%s.." (stn.Text.Substring(0, 10))
42 | |> Some
43 | | _ -> None
44 |
45 | let finalContinuation (children: JsonValue list) =
46 | Encode.object
47 | [ "type", Encode.string (node.GetType().Name)
48 | "text", Encode.option Encode.string text
49 | "range", encodeRange node.Range
50 | "contentBefore", Encode.seq (Seq.map encodeTriviaNode node.ContentBefore)
51 | "children", Encode.list children
52 | "contentAfter", Encode.seq (Seq.map encodeTriviaNode node.ContentAfter) ]
53 | |> continuation
54 |
55 | Continuation.sequence continuations finalContinuation
56 |
57 | let mkRange (range: Range) : FantomasTools.Client.Range =
58 | { StartLine = range.StartLine
59 | StartColumn = range.StartColumn
60 | EndLine = range.EndLine
61 | EndColumn = range.EndColumn }
62 |
63 | let fsharpErrorInfoSeverity =
64 | function
65 | | FSharpDiagnosticSeverity.Warning -> "warning"
66 | | FSharpDiagnosticSeverity.Error -> "error"
67 | | FSharpDiagnosticSeverity.Hidden -> "hidden"
68 | | FSharpDiagnosticSeverity.Info -> "info"
69 |
70 | let encodeFSharpErrorInfo (info: FSharpParserDiagnostic) =
71 | ({ SubCategory = info.SubCategory
72 | Range =
73 | match info.Range with
74 | | None -> mkRange Range.Zero
75 | | Some r -> mkRange r
76 | Severity = fsharpErrorInfoSeverity info.Severity
77 | ErrorNumber = Option.defaultValue 0 info.ErrorNumber
78 | Message = info.Message }
79 | : FantomasTools.Client.Diagnostic)
80 | |> FantomasTools.Client.Diagnostic.Encode
81 |
82 | let encode (root: Node) (diagnostics: FSharpParserDiagnostic list) =
83 | Encode.object
84 | [ "oak", encodeNode root id
85 | "diagnostics", Encode.list (List.map encodeFSharpErrorInfo diagnostics) ]
86 |
--------------------------------------------------------------------------------
/src/server/OakViewer/Encoders.fsi:
--------------------------------------------------------------------------------
1 | module internal OakViewer.Encoders
2 |
3 | open Thoth.Json.Net
4 | open Fantomas.FCS.Parse
5 | open Fantomas.Core.SyntaxOak
6 |
7 | val encode: root: Node -> diagnostics: FSharpParserDiagnostic list -> JsonValue
8 |
--------------------------------------------------------------------------------
/src/server/OakViewer/GetOak.fs:
--------------------------------------------------------------------------------
1 | module OakViewer.GetOak
2 |
3 | open Fantomas.Core
4 | open OakViewer.Server
5 |
6 | let getVersion () : string =
7 | let assembly = Fantomas.FCS.Parse.parseFile.GetType().Assembly
8 | let version = assembly.GetName().Version
9 | $"%i{version.Major}.%i{version.Minor}.%i{version.Revision}"
10 |
11 | let private parseAST source defines isFsi = Fantomas.FCS.Parse.parseFile isFsi source defines
12 |
13 | []
14 | type GetOakResponse =
15 | | Ok of text: string
16 | | BadRequest of body: string
17 |
18 | let getOak json : GetOakResponse =
19 | let parseRequest = Decoders.decodeParseRequest json
20 |
21 | match parseRequest with
22 | | Ok pr ->
23 | let { SourceCode = content
24 | Defines = defines
25 | IsFsi = isFsi } =
26 | pr
27 |
28 | let oakResult =
29 | try
30 | let ast, diagnostics =
31 | Fantomas.FCS.Parse.parseFile
32 | isFsi
33 | (Fantomas.FCS.Text.SourceText.ofString content)
34 | (List.ofArray defines)
35 |
36 | let oak = CodeFormatter.TransformAST(ast, content)
37 | Ok(oak, diagnostics)
38 | with ex ->
39 | Error ex
40 |
41 | match oakResult with
42 | | Error ex -> GetOakResponse.BadRequest(ex.Message)
43 | | Ok(oak, diagnostics) ->
44 | let responseText =
45 | Encoders.encode oak diagnostics |> Thoth.Json.Net.Encode.toString 4
46 |
47 | GetOakResponse.Ok responseText
48 |
49 | | Error err -> GetOakResponse.BadRequest(string err)
50 |
--------------------------------------------------------------------------------
/src/server/OakViewer/Lambda.fs:
--------------------------------------------------------------------------------
1 | module OakViewer.Lambda
2 |
3 | open System.Net
4 | open Amazon.Lambda.APIGatewayEvents
5 | open Amazon.Lambda.Core
6 | open AWSLambdaExtensions
7 | open HttpConstants
8 | open OakViewer.GetOak
9 |
10 | // Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
11 | [)>]
12 | ()
13 |
14 | let GetVersion (_request: APIGatewayProxyRequest) (_context: ILambdaContext) =
15 | let version = getVersion ()
16 | mkAPIGatewayProxyResponse (HttpStatusCode.OK, HeaderValues.TextPlain, version)
17 |
18 | let GetOak (request: APIGatewayProxyRequest) (_context: ILambdaContext) =
19 | let oakResponse = getOak request.Body
20 |
21 | let responseData =
22 | match oakResponse with
23 | | GetOakResponse.Ok body -> HttpStatusCode.OK, HeaderValues.ApplicationText, body
24 | | GetOakResponse.BadRequest body -> HttpStatusCode.BadRequest, HeaderValues.ApplicationText, body
25 |
26 | mkAPIGatewayProxyResponse responseData
27 |
--------------------------------------------------------------------------------
/src/server/OakViewer/OakViewer.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | true
6 | Lambda
7 | exe
8 |
9 | true
10 | false
11 | true
12 | true
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/src/server/OakViewer/Program.fs:
--------------------------------------------------------------------------------
1 | open Suave
2 | open Suave.Filters
3 | open Suave.Operators
4 | open Suave.Successful
5 | open Suave.RequestErrors
6 | open SuaveExtensions
7 | open OakViewer.GetOak
8 |
9 | []
10 | let main argv =
11 | let mapOakResponseToWebPart (response: GetOakResponse) : WebPart =
12 | match response with
13 | | GetOakResponse.Ok body -> (applicationText >=> OK body)
14 | | GetOakResponse.BadRequest errors -> (applicationText >=> BAD_REQUEST errors)
15 |
16 | let getOakWebPart =
17 | request (fun req ctx ->
18 | async {
19 | let json = req.BodyText
20 | let astResponse = getOak json
21 | return! (mapOakResponseToWebPart astResponse) ctx
22 | })
23 |
24 | let routes =
25 | [ GET >=> path "/oak-viewer/version" >=> textPlain >=> OK(getVersion ())
26 | POST >=> path "/oak-viewer/get-oak" >=> getOakWebPart ]
27 |
28 | let port =
29 | match List.ofArray argv with
30 | | [ "--port"; port ] -> System.UInt16.Parse port
31 | | _ -> 8904us
32 |
33 | startFantomasTool port routes
34 |
35 | 0
36 |
--------------------------------------------------------------------------------
/src/server/OakViewer/host.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0"
3 | }
--------------------------------------------------------------------------------
/src/server/OakViewer/packages.lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 1,
3 | "dependencies": {
4 | "net8.0": {
5 | "Amazon.Lambda.APIGatewayEvents": {
6 | "type": "Direct",
7 | "requested": "[2.5.0, )",
8 | "resolved": "2.5.0",
9 | "contentHash": "u2M1e8e+eahgwSpa2jhBaakH37EgIZcHqmqpK/9DD/PMygxK5g7LPyUl6SRFnVnmVyD0zvjEh8lYnpYULY6WIQ=="
10 | },
11 | "Amazon.Lambda.Core": {
12 | "type": "Direct",
13 | "requested": "[2.1.0, )",
14 | "resolved": "2.1.0",
15 | "contentHash": "ok06UhfBZw6j6+PhiJR9C0EOMuJvnq8rMCHVkaFmPFrlI/q447ukwGZQKaAKqodV+MNTfpb/iPxjgUPVbSlVVw=="
16 | },
17 | "Amazon.Lambda.Serialization.SystemTextJson": {
18 | "type": "Direct",
19 | "requested": "[2.3.0, )",
20 | "resolved": "2.3.0",
21 | "contentHash": "qgFCDJp5lyUNqCq1z18U7fZ/+rmMyw6RJf9nKfnJrf79YupDj02klQAjxymEYN66NykWXyc68SGkow6fy53hfg==",
22 | "dependencies": {
23 | "Amazon.Lambda.Core": "2.1.0"
24 | }
25 | },
26 | "FSharp.Core": {
27 | "type": "Direct",
28 | "requested": "[8.0.403, )",
29 | "resolved": "8.0.403",
30 | "contentHash": "t1Pvv2++3zMQKKNuiQc1Lz4TCdaBajgG4mLhOE8AoFzborHQ/JbjIaJr6Mrq8m2z15KLu4r6Qz7E3oeACpljTg=="
31 | },
32 | "Microsoft.Net.Http.Headers": {
33 | "type": "Direct",
34 | "requested": "[2.2.8, )",
35 | "resolved": "2.2.8",
36 | "contentHash": "wHdwMv0QDDG2NWDSwax9cjkeQceGC1Qq53a31+31XpvTXVljKXRjWISlMoS/wZYKiqdqzuEvKFKwGHl+mt2jCA==",
37 | "dependencies": {
38 | "Microsoft.Extensions.Primitives": "2.2.0",
39 | "System.Buffers": "4.5.0"
40 | }
41 | },
42 | "Suave": {
43 | "type": "Direct",
44 | "requested": "[2.6.2, )",
45 | "resolved": "2.6.2",
46 | "contentHash": "JNTsgb3FrFnvsp7G93Y9XLIGVa47fG4GZ8un/+/iMMVBTRWl8l6Rlnqjo/PsQP6NojtkrLEM4SLz9AhfQIPNag==",
47 | "dependencies": {
48 | "FSharp.Core": "0.0.0"
49 | }
50 | },
51 | "Thoth.Json.Net": {
52 | "type": "Direct",
53 | "requested": "[8.0.0, )",
54 | "resolved": "8.0.0",
55 | "contentHash": "C/b+8g/xUTJTn7pbKC4bMAOy2tyolXAuHTXguT5TNzDKQ6sjnUfFa9B81fTt9PuUOdWFLyRKlXASuFhSQciJGQ==",
56 | "dependencies": {
57 | "FSharp.Core": "4.7.2",
58 | "Fable.Core": "[3.0.0, 4.0.0)",
59 | "Newtonsoft.Json": "11.0.2"
60 | }
61 | },
62 | "Fable.Core": {
63 | "type": "Transitive",
64 | "resolved": "3.0.0",
65 | "contentHash": "pkCOWJKAkCk36f5+q4F3XqlfsgCJL6i2lTLl4ZZVDswn8rjXo21EBG/gJ296a88LVBkI5LL2VwxQYqGZncomJw==",
66 | "dependencies": {
67 | "FSharp.Core": "4.5.2"
68 | }
69 | },
70 | "Microsoft.Extensions.Primitives": {
71 | "type": "Transitive",
72 | "resolved": "2.2.0",
73 | "contentHash": "azyQtqbm4fSaDzZHD/J+V6oWMFaf2tWP4WEGIYePLCMw3+b2RQdj9ybgbQyjCshcitQKQ4lEDOZjmSlTTrHxUg==",
74 | "dependencies": {
75 | "System.Memory": "4.5.1",
76 | "System.Runtime.CompilerServices.Unsafe": "4.5.1"
77 | }
78 | },
79 | "Microsoft.NETCore.Platforms": {
80 | "type": "Transitive",
81 | "resolved": "1.1.1",
82 | "contentHash": "TMBuzAHpTenGbGgk0SMTwyEkyijY/Eae4ZGsFNYJvAr/LDn1ku3Etp3FPxChmDp5HHF3kzJuoaa08N0xjqAJfQ=="
83 | },
84 | "Microsoft.NETCore.Targets": {
85 | "type": "Transitive",
86 | "resolved": "1.1.3",
87 | "contentHash": "3Wrmi0kJDzClwAC+iBdUBpEKmEle8FQNsCs77fkiOIw/9oYA07bL1EZNX0kQ2OMN3xpwvl0vAtOCYY3ndDNlhQ=="
88 | },
89 | "Newtonsoft.Json": {
90 | "type": "Transitive",
91 | "resolved": "11.0.2",
92 | "contentHash": "IvJe1pj7JHEsP8B8J8DwlMEx8UInrs/x+9oVY+oCD13jpLu4JbJU2WCIsMRn5C4yW9+DgkaO8uiVE5VHKjpmdQ=="
93 | },
94 | "System.Buffers": {
95 | "type": "Transitive",
96 | "resolved": "4.5.0",
97 | "contentHash": "pL2ChpaRRWI/p4LXyy4RgeWlYF2sgfj/pnVMvBqwNFr5cXg7CXNnWZWxrOONLg8VGdFB8oB+EG2Qw4MLgTOe+A=="
98 | },
99 | "System.Collections.Immutable": {
100 | "type": "Transitive",
101 | "resolved": "8.0.0",
102 | "contentHash": "AurL6Y5BA1WotzlEvVaIDpqzpIPvYnnldxru8oXJU2yFxFUy3+pNXjXd1ymO+RA0rq0+590Q8gaz2l3Sr7fmqg=="
103 | },
104 | "System.Diagnostics.DiagnosticSource": {
105 | "type": "Transitive",
106 | "resolved": "8.0.1",
107 | "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg=="
108 | },
109 | "System.Memory": {
110 | "type": "Transitive",
111 | "resolved": "4.6.0",
112 | "contentHash": "OEkbBQoklHngJ8UD8ez2AERSk2g+/qpAaSWWCBFbpH727HxDq5ydVkuncBaKcKfwRqXGWx64dS6G1SUScMsitg=="
113 | },
114 | "System.Runtime": {
115 | "type": "Transitive",
116 | "resolved": "4.3.1",
117 | "contentHash": "abhfv1dTK6NXOmu4bgHIONxHyEqFjW8HwXPmpY9gmll+ix9UNo4XDcmzJn6oLooftxNssVHdJC1pGT9jkSynQg==",
118 | "dependencies": {
119 | "Microsoft.NETCore.Platforms": "1.1.1",
120 | "Microsoft.NETCore.Targets": "1.1.3"
121 | }
122 | },
123 | "System.Runtime.CompilerServices.Unsafe": {
124 | "type": "Transitive",
125 | "resolved": "4.5.1",
126 | "contentHash": "Zh8t8oqolRaFa9vmOZfdQm/qKejdqz0J9kr7o2Fu0vPeoH3BL1EOXipKWwkWtLT1JPzjByrF19fGuFlNbmPpiw=="
127 | },
128 | "fantomas.core": {
129 | "type": "Project",
130 | "dependencies": {
131 | "FSharp.Core": "[8.0.100, )",
132 | "Fantomas.FCS": "[1.0.0, )"
133 | }
134 | },
135 | "fantomas.fcs": {
136 | "type": "Project",
137 | "dependencies": {
138 | "FSharp.Core": "[8.0.100, )",
139 | "System.Collections.Immutable": "[8.0.0, )",
140 | "System.Diagnostics.DiagnosticSource": "[8.0.1, )",
141 | "System.Memory": "[4.6.0, )",
142 | "System.Runtime": "[4.3.1, )"
143 | }
144 | }
145 | }
146 | }
147 | }
--------------------------------------------------------------------------------
/src/server/SuaveExtensions.fs:
--------------------------------------------------------------------------------
1 | module SuaveExtensions
2 |
3 | open System.Net
4 | open System.Net.Sockets
5 | open Suave
6 | open Suave.Operators
7 | open Suave.Writers
8 | open Suave.Response
9 | open Suave.Filters
10 | open Suave.Successful
11 | open Suave.RequestErrors
12 | open HttpConstants
13 |
14 | type HttpRequest with
15 |
16 | member this.BodyText = System.Text.Encoding.UTF8.GetString this.rawForm
17 |
18 | let applicationJson = setMimeType HeaderValues.ApplicationJson
19 | let applicationText = setMimeType HeaderValues.ApplicationText
20 | let textPlain = setMimeType HeaderValues.TextPlain
21 | let private getBytes (v: string) = System.Text.Encoding.UTF8.GetBytes v
22 | let REQUEST_ENTITY_TOO_LARGE (body: string) = response HTTP_413 (getBytes body)
23 | let INTERNAL_SERVER_ERROR (body: string) = response HTTP_500 (getBytes body)
24 |
25 | let setCORSHeaders =
26 | addHeader "Access-Control-Allow-Origin" "*"
27 | >=> addHeader "Access-Control-Allow-Headers" "*"
28 | >=> addHeader "Access-Control-Allow-Methods" "*"
29 |
30 | let startFantomasTool port routes =
31 | try
32 | setCORSHeaders
33 | >=> choose [ OPTIONS >=> no_content; yield! routes; NOT_FOUND "Not found" ]
34 | |> startWebServer
35 | { defaultConfig with
36 | bindings = [ HttpBinding.create HTTP IPAddress.Loopback port ] }
37 | with :? SocketException ->
38 | printfn $"Port {port} is already in use"
39 |
--------------------------------------------------------------------------------
/src/shared/ASTViewerShared.fs:
--------------------------------------------------------------------------------
1 | module ASTViewer.Shared
2 |
3 | open FantomasTools.Client
4 |
5 | type Response =
6 | { Ast: string
7 | Diagnostics: Diagnostic array }
8 |
9 | type Request =
10 | { SourceCode: string
11 | Defines: string array
12 | IsFsi: bool
13 | Expand: bool }
14 |
--------------------------------------------------------------------------------
/src/shared/FantomasOnlineShared.fs:
--------------------------------------------------------------------------------
1 | module FantomasOnline.Shared
2 |
3 | open FantomasTools.Client
4 |
5 | type FantomasOption =
6 | | IntOption of order: int * name: string * value: int
7 | | BoolOption of order: int * name: string * value: bool
8 | | MultilineFormatterTypeOption of order: int * name: string * value: string
9 | | EndOfLineStyleOption of order: int * name: string * value: string
10 | | MultilineBracketStyleOption of order: int * name: string * value: string
11 |
12 | let sortByOption =
13 | function
14 | | IntOption(o, _, _)
15 | | BoolOption(o, _, _)
16 | | MultilineFormatterTypeOption(o, _, _)
17 | | EndOfLineStyleOption(o, _, _)
18 | | MultilineBracketStyleOption(o, _, _) -> o
19 |
20 | let getOptionKey =
21 | function
22 | | IntOption(_, k, _)
23 | | BoolOption(_, k, _)
24 | | MultilineFormatterTypeOption(_, k, _)
25 | | EndOfLineStyleOption(_, k, _)
26 | | MultilineBracketStyleOption(_, k, _) -> k
27 |
28 | let optionValue =
29 | function
30 | | IntOption(_, _, i) -> i.ToString()
31 | | BoolOption(_, _, b) -> b.ToString()
32 | | MultilineFormatterTypeOption(_, _, v)
33 | | EndOfLineStyleOption(_, _, v)
34 | | MultilineBracketStyleOption(_, _, v) -> v
35 |
36 | let tryGetUserOptionValue userOptions key castFunc =
37 | userOptions |> Map.tryFind key |> Option.map (optionValue >> castFunc)
38 |
39 | let tryGetDefaultOptionValue defaultOptions key castFunc =
40 | defaultOptions
41 | |> List.tryFind (fun o -> (getOptionKey o) = key)
42 | |> Option.map (optionValue >> castFunc)
43 |
44 | let tryGetOptionValue userOptions defaultOptions key castFunc =
45 | let userOption = tryGetUserOptionValue userOptions key castFunc
46 |
47 | match userOption with
48 | | Some n -> Some n
49 | | None -> tryGetDefaultOptionValue defaultOptions key castFunc
50 |
51 | type FormatRequest =
52 | { SourceCode: string
53 | Options: FantomasOption list
54 | IsFsi: bool }
55 |
56 | type FormatResponse =
57 | { FirstFormat: string
58 | FirstValidation: Diagnostic array
59 | SecondFormat: string option
60 | SecondValidation: Diagnostic array }
61 |
62 | let private supportedProperties =
63 | set [| "max_line_length"; "indent_size"; "end_of_line" |]
64 |
65 | let toEditorConfigName value =
66 | value
67 | |> Seq.map (fun c ->
68 | if System.Char.IsUpper(c) then
69 | sprintf "_%s" (c.ToString().ToLower())
70 | else
71 | c.ToString())
72 | |> String.concat ""
73 | |> fun s -> s.TrimStart([| '_' |])
74 | |> fun name ->
75 | if Set.contains name supportedProperties then
76 | name
77 | else
78 | sprintf "fsharp_%s" name
79 |
--------------------------------------------------------------------------------
/src/shared/OakShared.fs:
--------------------------------------------------------------------------------
1 | namespace OakViewer
2 |
3 | type ParseRequest =
4 | { SourceCode: string
5 | Defines: string array
6 | IsFsi: bool }
7 |
--------------------------------------------------------------------------------
/src/shared/Types.fs:
--------------------------------------------------------------------------------
1 | namespace FantomasTools.Client
2 |
3 | #if FABLE_COMPILER
4 | open Thoth.Json
5 | #else
6 | open Thoth.Json.Net
7 | #endif
8 |
9 | type Range =
10 | { StartLine: int
11 | StartColumn: int
12 | EndLine: int
13 | EndColumn: int }
14 |
15 | static member Zero =
16 | { StartLine = 0
17 | StartColumn = 0
18 | EndLine = 0
19 | EndColumn = 0 }
20 |
21 | #if FABLE_COMPILER
22 | static member Decode: Decoder =
23 | Decode.object (fun get ->
24 | { StartLine = get.Required.Field "startLine" Decode.int
25 | StartColumn = get.Required.Field "startColumn" Decode.int
26 | EndLine = get.Required.Field "endLine" Decode.int
27 | EndColumn = get.Required.Field "endColumn" Decode.int })
28 | #else
29 | static member Encode(range: Range) : JsonValue =
30 | Encode.object
31 | [ "startLine", Encode.int range.StartLine
32 | "startColumn", Encode.int range.StartColumn
33 | "endLine", Encode.int range.EndLine
34 | "endColumn", Encode.int range.EndColumn ]
35 | #endif
36 |
37 | type Diagnostic =
38 | { SubCategory: string
39 | Range: Range
40 | Severity: string
41 | ErrorNumber: int
42 | Message: string }
43 |
44 | #if FABLE_COMPILER
45 | static member Decode: Decoder =
46 | Decode.object (fun get ->
47 | { SubCategory = get.Required.Field "subcategory" Decode.string
48 | Range = get.Required.Field "range" Range.Decode
49 | Severity = get.Required.Field "severity" Decode.string
50 | ErrorNumber = get.Required.Field "errorNumber" Decode.int
51 | Message = get.Required.Field "message" Decode.string })
52 | #else
53 | static member Encode(diagnostic: Diagnostic) : JsonValue =
54 | Encode.object
55 | [ "subcategory", Encode.string diagnostic.SubCategory
56 | "range", Range.Encode diagnostic.Range
57 | "severity", Encode.string diagnostic.Severity
58 | "errorNumber", Encode.int diagnostic.ErrorNumber
59 | "message", Encode.string diagnostic.Message ]
60 | #endif
61 |
--------------------------------------------------------------------------------