Full Code of fsharp/emacs-fsharp-mode for AI

master 5212c9359180 cached
54 files
318.6 KB
87.1k tokens
1 requests
Download .txt
Showing preview only (340K chars total). Download the full file or copy to clipboard to get everything.
Repository: fsharp/emacs-fsharp-mode
Branch: master
Commit: 5212c9359180
Files: 54
Total size: 318.6 KB

Directory structure:
gitextract__62fiqaz/

├── .dir-locals.el
├── .github/
│   ├── pull_request_template.md
│   └── workflows/
│       └── test.yml
├── .gitignore
├── CHANGELOG.md
├── Eldev
├── ISSUE_TEMPLATE.md
├── LICENSE
├── README.org
├── eglot-fsharp.el
├── fsharp-mode-font.el
├── fsharp-mode-structure.el
├── fsharp-mode-util.el
├── fsharp-mode.el
├── inf-fsharp-mode.el
└── test/
    ├── CompileCommandData/
    │   ├── Directory With Spaces/
    │   │   ├── noproj/
    │   │   │   └── test.fs
    │   │   └── proj/
    │   │       ├── test.fs
    │   │       └── test.fsproj
    │   ├── noproj/
    │   │   └── test.fs
    │   └── proj/
    │       ├── Makefile
    │       ├── test.fs
    │       └── test.fsproj
    ├── FindSlnData/
    │   ├── bar.sln
    │   ├── noproj/
    │   │   └── test.fs
    │   ├── sln/
    │   │   └── foo.sln
    │   └── test.fsproj
    ├── StructureTest/
    │   ├── Blocks.fs
    │   ├── BracketIndent.fs
    │   ├── ContinuationLines.fs
    │   ├── Literals.fs
    │   ├── Nesting.fs
    │   └── Relative.fs
    ├── Test1/
    │   ├── Error.fs
    │   ├── FileTwo.fs
    │   ├── NoProject.fs
    │   ├── Pervasive.fs
    │   ├── Program.fs
    │   ├── Script.fsx
    │   └── Test1.fsproj
    ├── Test2/
    │   ├── Main.fs
    │   └── Test2.fsproj
    ├── apps/
    │   ├── FQuake3/
    │   │   ├── NativeMappings.fs
    │   │   └── NativeMappings.fs.faceup
    │   ├── FSharp.Compatibility/
    │   │   ├── Format.fs
    │   │   └── Format.fs.faceup
    │   └── RecordHighlighting/
    │       ├── Test.fsx
    │       └── Test.fsx.faceup
    ├── eglot-fsharp-integration-util.el
    ├── expression.fsx
    ├── fsharp-mode-font-tests.el
    ├── fsharp-mode-structure-tests.el
    ├── fsi-tests.el
    ├── integration-tests.el
    └── nuget.fsx

================================================
FILE CONTENTS
================================================

================================================
FILE: .dir-locals.el
================================================
;;; Directory Local Variables
;;; For more information see (info "(emacs) Directory Variables")

((emacs-lisp-mode . ((indent-tabs-mode . nil)
		     (fill-column . 120))))


================================================
FILE: .github/pull_request_template.md
================================================
## Description

<!-- Add a small description of the PR that you're submitting -->

## How to test

<!-- Please describe how one can test/verify that the PR is working (when applicable) -->

## Related issues

<!-- Point to the relevant issues addressed by this PR -->


================================================
FILE: .github/workflows/test.yml
================================================
name: "CI"
on:
  pull_request:
  push:
    branches:
      - master
jobs:
  gnu-build:
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, macos-latest]
        dotnet: [9.0.x]
        emacs_version:
          - 28.2
          - 29.4
          - 30.2
          - snapshot
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-dotnet@v4
        with:
          dotnet-version: ${{ matrix.dotnet }}
      - uses: purcell/setup-emacs@master
        with:
          version: ${{ matrix.emacs_version }}
      - name: Install Eldev
        run: curl -fsSL https://raw.github.com/doublep/eldev/master/webinstall/github-eldev | sh
      - name: Show dotnet sdks
        run: dotnet --list-sdks
      - name: Show dotnet version
        run: dotnet --info
      - name: Eldev archives
        run: |
          echo "Archives:"
          eldev archives
      - name: Eldev dependencies
        run: |
          echo "Dependencies:"
          eldev -v dependencies
      - name: Test
        run: |
          echo "Testing:"
          eldev -dtT test
          
  windows-build:
    runs-on: windows-latest
    strategy:
      fail-fast: false
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-dotnet@v4
        with:
          dotnet-version: 9.0.x
      - name: Show dotnet sdks
        run: dotnet --list-sdks
      - name: Show dotnet version
        run: dotnet --info
      - name: Set up Emacs on Windows
        uses: jcs090218/setup-emacs-windows@master
        with:
          version: 29.4
      - name: Install Eldev
        run: curl.exe -fsSL https://raw.github.com/doublep/eldev/master/webinstall/eldev.bat | cmd /Q
      - name: Eldev archives
        run: |
          echo "Archives:"
          ~/.local/bin/eldev.bat archives
      - name: Eldev dependencies
        run: |
          echo "Dependencies:"
          ~/.local/bin/eldev.bat dependencies
      - name: Test
        run: |
          echo "Testing:"
          ~/.local/bin/eldev.bat -p -dtT test


================================================
FILE: .gitignore
================================================
*.elc
bin
tmp
*~

# Useful for doing releases
emacs-fsharp-mode-bin/

# Dependency archive
fsautocomplete-*.zip

# Development
obj/
.ionide/


================================================
FILE: CHANGELOG.md
================================================
## 1.10 (2019.12-01)

Features:
  - #210: Remove old FsAutoComplete support (use LSP)
	- provide eglot (Emacs LSP client) integration and add eglot
      integration tests (using Emacs buttercup)
    - Use Cask instead to automate the package development cycle;
      development, dependencies, testing, building, packaging
    - Make project.el aware of F# projects
  - Use Emacs org-mode for README
Bugfixes:
    - #68: Indentation Cleanup / SMIE mode not being applied properly
      (Gastove)

## 1.9.14 (2019-06-09)

Features:
  - #207: Update to FsAutoComplete 0.38.1
  - #206: Set default build command to msbuild if found
Bugfixes:
  - #198: Use buffer-local version of company-quickhelp-mode

## 1.9.13 (2018)

Features:
  - #193: Update to FSAC 0.36
	 - Fixes #183: Load .Net Core projects that reference other projects
	 - Fixes #182: .fs files not parsed
Bugfixes:
  - #190: Fix attribute locking, improve imenu support
  - #189: Fix bug in font locking for active patterns
  - #187: Fix Infinite loop when file begins with a comment preceded
    by whitespace
  - #180: Use scoop instead of  Chocolatey package for Appveyor testing
  - #179: Use portable (Windows support) Makefile
  - #176: Add F# Tools 10.1 SDK directory to search dirs
  - #175: Paths with characters outside ASCII gives error FS2302 (Windows)
  - #171: Fix for phrase detection for if/then/else constructs

## 1.9.12 (2018-05-18)

Features:
  - #170: Flycheck verify (Improved fsautocomplete diagnostics)

Bugfixes:
  - #167: Fix error when visiting a new F# script file and
    fsautocomplete is not started
  - #168: Add Flycheck predicate function to prevent error when
    fsautocomplete is not running
  - #162: Stop matching [ as part of normal residue
  - #157: Don't change global value of `comment-indent-function'
  - #153: Add access control keywords to declaration regexes

## 1.9.11 (2017-10-21)

Features:
  - #151: Correctly find MSBuild from VS2017

Bugfixes:
  - #152: Handle failure to find build commands gracefully

## 1.9.10 (2017-09-18)

Bugfixes:
  - #146: Understand FSAC 0.34 error msgs

## 1.9.9 (2017-09-15)

Features
  - #143: Update to FsAutoComplete 0.34.0

Bugfixes:
  - #139: Disable flycheck and fsharp-doc-mode when fsharp-ac-intellisense-enabled is nil

## 1.9.8 (2017-06-17)

Features:
  - #134: Improved logging
  - #137: fsharp-shift-region-[left,right]: change bindings to 'C-c <' and 'C-c >'

Bugfixes:
  - #136: Use correct F# interactive prompt regex

## 1.9.7 (2017-06-06)

Bugfixes:
  - #131: Don't panic on malformed JSON (debug messages)
  - #133: Update faceup to capture font-locking <|

## 1.9.6 (2017-04-16)

Features:
  - #127: Update to FsAutoComplete 0.32.0 (.NET Core project support)

Bugfixes:
  - #125: Small fixes to try to prevent fsharp-mode to freeze all emacs 
  - #122: Make fsharp-doc-mode hook buffer-local

## 1.9.5 (2017-01-21)

Bugfixes:
  - #117: Fix `type` locking
  - #118: Don't change company-idle-delay
  - #120: Fix FSAC hanging issue

## 1.9.4 (2016-11-30)

Features
  - #116: Improve Active Pattern font locking, eval-when-compile the main font-lock-keywords form
  - #114: Clean up font-locking code

## 1.9.3 (2016-10-31)
Features
  - #111: Update to FsAutoComplete 0.32.0
  - #109: Define inferior-fsharp-mode as variant of comint mode

Bugfixes:
  - #110: Dont change default indent region function
  - #105: Don't send trailing newline to fsautocomplete
  - #104: Dont change `company-minimum-prefix-length'

## 1.9.2 (2016-09-30)
Features
  - #98: Enable imenu support

## 1.9.1 (2016-07-19)

Features:
  - Update to FsAutoComplete 0.29.0.

## 1.9.0 (2016-07-09)

Features:
  - #71: fontify the doc string (@nosami).
  - #77: Use new typesig command for fsharp-doc mode (@rneatherway).
  - #88: Use flycheck for error reporting (@juergenhoetzel).

Bugfixes:
  - #75: Do not change current buffer when starting FSI (@rneatherway).
  - #76: Record type highlighting (@rneatherway).
  - #79: Overlays should not grow when typing (@rneatherway).
  - #82: Inferior fsi: #silentcd to local directory in Tramp (@juergenhoetzel).
  - #83: Fix completion of type annotated symbols (@juergenhoetzel).
  - #85: Don't modify company-transformers (@nosami).
  - #86: Don't clobber company-backends (@nosami).

## 1.8.1 (2016-04-14)

Features:
  - #66: Tramp support (@juergenhoetzel).
  - #69: Prefer exact case sort in completion list (@nosami).

## 1.8.0 (2016-04-05)

Features:
  - Update to FsAutoComplete 0.28.0 to support #65.
  - #65: Faster completions (thanks to @nosami).
  - #56: Use FsAutoComplete "startswith" filter (thanks to @juergenhoetzel).

Bugfixes:
  - #67: Fix use of popup (thanks to @drvink)
  - #60: Unbreak company support on non-graphic displays (thanks to @drvink)
  - #58: Handle buffers not visiting a file (thanks to @juergenhoetzel).

## 1.7.4 (2016-02-05)

Features:
  - #49: Use company for completions (thanks to @nosami).

Bugfixes:
  - Update to FsAutoComplete 0.27.2, fixes project cracking for files
    with spaces in the path.

## 1.7.3 (2016-01-26)

Bugfixes:
  - Update to FsAutoComplete 0.27.1, fixes Windows VS2015-only support.

## 1.7.2 (2016-01-08)

Bugfixes:
  - #50: Inhibit electric-indent for fsharp-mode buffers (thanks to @joranvar).

## 1.7.1 (2015-11-24)

Features:
  - #45: Update FSAC to 0.27, enable project cracking logs.

## 1.7.0 (2015-11-24)

Features:
  - #34: Switch to SMIE-based indentation engine (thanks to m00nlight).
  - #31: Add highlighting of other usages of symbol at point.

## 1.6.3 (2015-10-24)

Bugfixes:
  - Update to FsAutoComplete 0.26.1, which fixes Windows support.

## 1.6.2 (2015-10-20)

Bugfixes:
  - Update to FsAutoComplete 0.26.0.
  - #30: Allow use of symbols containing '%'
  - #28: Fix FSI usage in buffers whose name differs from filename
  - #27: Fix test of fsharp-ac-debug

## 1.6.1 (2015-09-02)

Bugfixes:
  - Update to FsAutoComplete 0.23.1. Fixed MSBuild v14 on non-English
    systems.

## 1.6.0 (2015-09-01)

Features:
  - Update to FSharp.AutoComplete 0.23.0. Contains many improvements,
    which can be found in the changelog at
    https://github.com/fsharp/FsAutoComplete/releases
  - #20: Add C-x C-e as default keybinding for eval.
  - #22: Allow .fsx files to be compiled as well.

Bugfixes:
  - #16: Remove BOM from process output.

## 1.5.4 (2015-06-04)

Features:
  - #4: Update to FSharp.AutoComplete 0.18.0. All unsaved buffer
    contents (not just the current buffer) will now be used for type
    checking.

Bugfixes:
  - #9: Correct quoting of path to fsi.exe on Windows.

## 1.5.3 (2015-05-26)

Note that in since 1.5.2 fsharp-mode has been migrated from
https://github.com/fsharp/fsharpbinding to a
[separate repository](https://github.com/fsharp/emacs-fsharp-mode).
The issue number `#2` below, and all future issue numbers, refer to the
new repository.

Features:
  - #993: Push the mark before going to definition (using etags)

Bugfixes:
  - #1005: Fix issue with compile-command quoting
  - #2: Add `do!` as a keyword.

## 1.5.2 (2015-03-20)

Bugfixes:
  - #973: Force comint-process-echoes to nil to avoid hangs

## 1.5.1 (2015-01-14)

Bugfixes:
  - #923: Autocompletion not working on Emacs 24.4+ on Windows

## 1.5.0 (2014-11-25)

Incorporate FSharp.AutoComplete version 0.13.3, which has corrected help text for the parse command and uses FCS 0.0.81.

Features:
  - #235: Support multiple projects simultaneously

Bugfixes:
  - #824: Emacs should give a better error message if fsautocomplete not found
  - #808: C-c C-p gives an error if no project file above current file's directory
  - #790: Can't make fsac requests in indirect buffers
  - #754: Compiler warnings when installing fsharp-mode from MELPA

## 1.4.2 (2014-10-30)

Incorporate FSharp.AutoComplete version 0.13.2, which returns more information if the project parsing fails.

Features:
  - #811: Return exception message on project parsing fail

## 1.4.1 (2014-10-30)

Incorporate FSharp.AutoComplete version 0.13.1, which contains a fix for goto definition.

Bugfixes:
  - #787: Correct off-by-one error in fsac goto definition

## 1.4.0 (2014-10-26)

The main feature of this release is that the project parsing logic has
been moved to FSharp.Compiler.Service as part of fixing #728.

Features:
  - #319: Better error feedback when no completion data available
  - #720: Rationalise emacs testing, also fixed #453

Bugfixes:
  - #765: Do not offer completions in irrelevant locations (strings/comments)
  - #721: Tests for Emacs syntax highlighting, and resultant fixes
  - #248: Run executable file now uses output from FSharp.AutoComplete
  - #728: Fix project support on Windows

## 1.3.0 (2014-08-28)

Changes by @rneatherway unless otherwise noted.

Major changes in this release are performance improvements thanks to @juergenhoetzel (avoiding parsing the current buffer unless necessary), and
fixes for syntax highlighting.


Features:
  - #481: Only parse the current buffer if it is was modified (@juergenhoetzel)

Bugfixes:
  - #619: Disable FSI syntax highlighting
  - #670: Prevent double dots appearing during completion
  - #485: Fetch SSL certs before building exe in emacs dir
  - #496: Corrections to emacs syntax highlighting
  - #597: Highlight preprocessor and async
  - #605: Add FSI directives to syntax highlighting of emacs
  - #571: Correct range-check for emacs support
  - #572: Ensure fsi prompt is readonly
  - #452: Fetch SSL certs before building exe in emacs dir


================================================
FILE: Eldev
================================================
; -*- mode: emacs-lisp; lexical-binding: t -*-

(setq package-lint-main-file "eglot-fsharp.el")
(setq eldev-project-main-file "eglot-fsharp.el")

(eldev-use-package-archive 'melpa-unstable)
(eldev-use-package-archive 'gnu)
(eldev-use-plugin 'autoloads)
(setq package-archive-priorities
      '(("melpa-unstable" . 400)
	("gnu" . 300)))





================================================
FILE: ISSUE_TEMPLATE.md
================================================

### Description

Please provide a succinct description of your issue.

### Repro steps

Please provide the steps required to reproduce the problem

1. Step A

2. Step B

### Expected behavior

Please provide a description of the behaviour you expect.

### Actual behavior

Please provide a description of the actual behaviour you observe. 

### Known workarounds

Please provide a description of any known workarounds.

### Related information 

* Operating system
* Branch
* Emacs version
* .NET Runtime, CoreCLR or Mono Version
* Performance information, links to performance testing scripts


================================================
FILE: LICENSE
================================================

                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright [yyyy] [name of copyright owner]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.


================================================
FILE: README.org
================================================
[[http://melpa.org/#/fsharp-mode][file:http://melpa.org/packages/fsharp-mode-badge.svg]]
[[https://stable.melpa.org/#/fsharp-mode][file:https://stable.melpa.org/packages/fsharp-mode-badge.svg]]
[[https://github.com/fsharp/emacs-fsharp-mode/actions][file:https://github.com/fsharp/emacs-fsharp-mode/workflows/CI/badge.svg]]
* fsharp-mode

Provides support for the F# language in Emacs. Includes the following features:

- Syntax highlighting and indentation
- Support for F# Interactive
- Via [[https://github.com/joaotavora/eglot/issues][Eglot]] LSP-client integration:
  - Displays type signatures and tooltips
  - Flymake
  - Completion
  - Jump to definition [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Xref.html][Find Identifier References]] (Xref)


** Project Status

This implementation is based on a very old OCaml-mode codebase and has become increasingly difficult to maintain.
Future development efforts should focus on [[https://github.com/bbatsov/fsharp-ts-mode][fsharp-ts-mode · GitHub]]
which is built on modern Emacs features, leveraging Tree-Sitter support.

** LSP mode

The current version of =fsharp-mode= installs =fsautocomplete.exe=
automatically via =eglot-fsharp.el= (part of this mono repo, [[https://melpa.org/#/eglot-fsharp][eglot-fsharp
on melpa]]) or [[https://github.com/emacs-lsp/lsp-mode][lsp-mode]] (untested).

=fsharp-mode= is tested with Emacs 27.1+ and NET Core 6 (LTS)

** Installation

*** Package

=fsharp-mode= is available on [[https://melpa.org][MELPA]] and can
be installed using the built-in package manager.

If you're not already using MELPA, add the following to your init.el:

#+BEGIN_SRC elisp
  ;;; Initialize MELPA
  (require 'package)
  (add-to-list 'package-archives '("melpa" . "http://melpa.org/packages/"))
  (unless package-archive-contents (package-refresh-contents))
  (package-initialize)

  ;;; Install fsharp-mode
  (unless (package-installed-p 'fsharp-mode)
    (package-install 'fsharp-mode))

  (require 'fsharp-mode)
#+END_SRC

If you are a user of [[https://github.com/jwiegley/use-package][use-package]] you can instead do

#+BEGIN_SRC elisp
(use-package fsharp-mode
  :defer t
  :ensure t)
#+END_SRC

*** From source

I recommend to use [[https://cask.github.io/why-cask.html][Cask]]. Add this to your =Cask= file:

#+BEGIN_SRC elisp
(depends-on "fsharp-mode" :git "https://github.com/fsharp/emacs-fsharp-mode.git")
#+END_SRC

** Eglot integration

The =eglot-fsharp= integration is not part of [[https://melpa.org/#/fsharp-mode][fsharp-mode on melpa]].

It is available via the seperate package [[https://melpa.org/#/eglot-fsharp][eglot-fsharp on melpa]].

Add to your config:
#+BEGIN_SRC elisp
(require 'eglot-fsharp)
#+END_SRC

and execute =M-x eglot=

With eglot running use `xref-find-definitions` (bound to =M-.= pr. default) to go to definition. Completions are accessable via. `completion-at-point` (or a completion backend ex. company-mode [[https://melpa.org/#/company]])


** Projects

=fsharp-mode= has support for Emacs build-in project management via =project.el=

** Configuration

*** Compiler and REPL paths

The F# compiler and interpreter should be set to good defaults for
your OS as long as the relevant executables can be found on your PATH
or in other standard locations. If you have a non-standard setup you
may need to configure these paths manually.

On Windows:

#+BEGIN_SRC elisp
(setq inferior-fsharp-program "c:\\Path\\To\\Fsi.exe")
#+END_SRC

On Unix-like systems, you must use the *--readline-* flag to ensure F#
Interactive will work correctly with Emacs. Typically =fsi= and =fsc= are
invoked through the shell scripts =fsharpi= and =fsharpc=:

#+BEGIN_SRC elisp
(setq inferior-fsharp-program "path/to/fsharpi --readline-")
#+END_SRC

***  Key Bindings

If you are new to Emacs, you might want to use the menu (call
=menu-bar-mode= if you don't see it). However, it's usually faster to learn
a few useful bindings:

| Key binding      | Description                               |
|------------------+-------------------------------------------|
| =C-c C-r=        | Evaluate region                           |
| =C-c C-f=        | Load current buffer into toplevel         |
| =C-c C-e=        | Evaluate current toplevel phrase          |
| =C-M-x=          | Evaluate current toplevel phrase          |
| =C-M-h=          | Mark current toplevel phrase              |
| =C-c C-s=        | Show interactive buffer                   |
| =C-c C-c=        | Compile with fsc                          |
| =C-c x=          | Run the executable                        |
| =C-c C-a=        | Open alternate file (.fsi or .fs)         |
| =C-c l=          | Shift region to left                      |
| =C-c r=          | Shift region to right                     |
| =C-c <up>=       | Move cursor to the beginning of the block |
| =C-c C-d=, =M-.= | Jump to definition of symbol at point     |
| =C-c C-b=, =M-,= | Return to where point was before jump.    |


To interrupt the interactive mode, use =C-c C-c=. This is useful if your
code does an infinite loop or a very long computation.

If you want to shift the region by 2 spaces, use: =M-2 C-c r=

In the interactive buffer, use ==M-RET= to send the code without
explicitly adding the =;;= thing.


** Editor

In order to change tab size it is possible to put this in emacs profile:

#+BEGIN_SRC elisp
(setq-default fsharp-indent-offset 2)
#+END_SRC

Because the F# language is sensitive to indentation, you might wan't to highlight indentation:

#+BEGIN_SRC elisp
(add-hook 'fsharp-mode-hook 'highlight-indentation-mode)
#+END_SRC

** Troubleshooting

=fsharp-mode= is still under development, so you may encounter some
issues. Please report them so we can improve things! Open an issue on [[https://github.com/fsharp/emacs-fsharp-mode/][Github]].

*** No autocompletion in FSX files

The root cause is documented in this Ionide issue:  [[https://github.com/ionide/ionide-vscode-fsharp/issues/1244][4.2.0 - No auto complete or typechecking in FSX files]]

As a workaround can add a reference to the facade netstandard assembly (path is platform/SDK-dependent).

On Arch Linux using [[https://aur.archlinux.org/packages/dotnet-sdk-lts-bin][dotnet sdk lts]] add this to your =fsx= file:
#+BEGIN_SRC fsharp
#r "/opt/dotnet/sdk/2.1.801/ref/netstandard.dll"
#+END_SRC

*** Project file issues

If your project file does not seem to be being parsed correctly, so
that you have missing references or other incorrect intellisense
results, it is possible to obtain a detailed log of LSP events in this buffers:


- =*EGLOT (PROJECT/fsharp-mode) stderr*=
- =*EGLOT (PROJECT/fsharp-mode) output*=
- =*EGLOT (PROJECT/fsharp-mode) events*=

** Contributing

This project is maintained by the
[[http://fsharp.org/][F# Software Foundation]], with the repository hosted
on [[https://github.com/fsharp/emacs-fsharp-mode][GitHub]].

Pull requests are welcome. Please run the test-suite with [[https://doublep.github.io/eldev/][Eldev]] =eldev -dtT test=
before submitting a pull request.

*** Maintainers

The maintainers of this repository appointed by the F# Core Engineering Group are:

 - [[https://github.com/juergenhoetzel][Jürgen Hötzel]], [[http://github.com/forki][Steffen Forkmann]], [[http://github.com/kjnilsson][Karl Nilsson]] and [[http://github.com/guillermooo][Guillermo López-Anglada]]
 - The primary maintainer for this repository is [[https://github.com/juergenhoetzel][Jürgen Hötzel]]

Previous maintainers:
 - [[https://github.com/rneatherway][Robin Neatherway]]






================================================
FILE: eglot-fsharp.el
================================================
;;; eglot-fsharp.el --- fsharp-mode eglot integration                     -*- lexical-binding: t; -*-

;; Copyright (C) 2019-2024  Jürgen Hötzel

;; Author: Jürgen Hötzel <juergen@hoetzel.info>
;; Package-Requires: ((emacs "27.1") (eglot "1.4") (fsharp-mode "1.10") (jsonrpc "1.0.14"))
;; Version: 1.10
;; Keywords: languages
;; URL: https://github.com/fsharp/emacs-fsharp-mode

;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with this program.  If not, see <https://www.gnu.org/licenses/>.

;;; Commentary:

;; Lua eglot introduced

;;; Code:

(require 'eglot)
(require 'fsharp-mode)
(require 'gnutls)

(defgroup eglot-fsharp nil
  "LSP support for the F# Programming Language, using F# compiler service."
  :link '(url-link "https://github.com/fsharp/FsAutoComplete")
  :group 'eglot)

(defcustom eglot-fsharp-server-path "~/.dotnet/tools/"
  "Path to the location of FsAutoComplete."
  :group 'eglot-fsharp
  :risky t)

(defcustom eglot-fsharp-server-install-dir
  (locate-user-emacs-file "FsAutoComplete/")
  "Install directory for FsAutoComplete."
  :group 'eglot-fsharp
  :risky t
  :type '(choice directory (const :tag "Use dotnet default for tool-path" nil)))

(defcustom eglot-fsharp-server-version 'latest
  "FsAutoComplete version to install or update."
  :group 'eglot-fsharp
  :risky t
  :type '(choice
          (const :tag "Latest release" latest)
          (string :tag "Version string")))

(defcustom eglot-fsharp-server-args '("--adaptive-lsp-server-enabled")
  "Arguments for the fsautocomplete command when using `eglot-fsharp'."
  :type '(repeat string))

(defcustom eglot-fsharp-fsautocomplete-args '(
                                              :automaticWorkspaceInit t
                                              :abstractClassStubGeneration t
				              :abstractClassStubGenerationMethodBody
				              "failwith \"Not Implemented\""
				              :abstractClassStubGenerationObjectIdentifier "this"
				              :addFsiWatcher nil
				              :codeLenses (:references (:enabled t)
							               :signature (:enabled t))
				              :disableFailedProjectNotifications nil
				              :dotnetRoot ""
				              :enableAdaptiveLspServer t
				              :enableAnalyzers nil
				              :enableMSBuildProjectGraph nil
				              :enableReferenceCodeLens t
				              :excludeProjectDirectories [".git" "paket-files" ".fable" "packages" "node_modules"]
				              :externalAutocomplete nil
				              :fsac (:attachDebugger nil
                                                                     :cachedTypeCheckCount 200
				                                     :conserveMemory nil
				                                     :dotnetArgs nil
				                                     :netCoreDllPath ""
				                                     :parallelReferenceResolution nil
				                                     :silencedLogs nil)
				              :fsiExtraParameters nil
				              :fsiSdkFilePath ""
				              :generateBinlog nil
				              :indentationSize 4
				              :inlayHints (:disableLongTooltip nil
								               :enabled t
								               :parameterNames t
								               :typeAnnotations t)
				              :inlineValues (:enabled nil
							              :prefix "//")
				              :interfaceStubGeneration t
				              :interfaceStubGenerationMethodBody "failwith \"Not Implemented\""
				              :interfaceStubGenerationObjectIdentifier "this"
				              :keywordsAutocomplete t
				              :lineLens (:enabled "replaceCodeLens"
					                          :prefix " // ")
				              :linter t
				              :pipelineHints (:enabled t
							               :prefix " // ")
				              :recordStubGeneration t
				              :recordStubGenerationBody "failwith \"Not Implemented\""
				              :resolveNamespaces t
				              :saveOnSendLastSelection nil
				              :simplifyNameAnalyzer t
				              :smartIndent nil
				              :suggestGitignore t
				              :suggestSdkScripts t
				              :unionCaseStubGeneration t
				              :unionCaseStubGenerationBody "failwith \"Not Implemented\""
				              :unusedDeclarationsAnalyzer t
				              :unusedOpensAnalyzer t
				              :verboseLogging nil
				              :workspaceModePeekDeepLevel 4
				              :workspacePath "")
  "Arguments for the fsautocomplete workspace configuration."
  :group 'eglot-fsharp
  :risky t
  )

(defun eglot-fsharp--path-to-server ()
  "Return FsAutoComplete path."
  (let ((base (if eglot-fsharp-server-install-dir
                  (concat eglot-fsharp-server-install-dir "netcore/")
                eglot-fsharp-server-path)))
    (expand-file-name (concat base "fsautocomplete" (if (eq system-type 'windows-nt) ".exe" "")))))

;; cache to prevent repetitive queries
(defvar eglot-fsharp--latest-version nil "Latest fsautocomplete.exe version string.")

(defun eglot-fsharp--latest-version ()
  "Return latest fsautocomplete.exe version."
  (let* ((json (with-temp-buffer (url-insert-file-contents "https://azuresearch-usnc.nuget.org/query?q=fsautocomplete&prerelease=false&packageType=DotnetTool")
			         (json-parse-buffer)))
         (versions (gethash "versions" (aref (gethash "data" json) 0))))
    (gethash "version" (aref versions (1- (length versions))))))

(defun eglot-fsharp--installed-version ()
  "Return version string of fsautocomplete."
  (with-temp-buffer
    (if eglot-fsharp-server-install-dir
        (process-file "dotnet" nil t nil "tool" "list" "--tool-path" (file-name-directory (eglot-fsharp--path-to-server)))
      (process-file "dotnet" nil t nil "tool" "list" "-g"))
    (goto-char (point-min))
    (when (search-forward-regexp "^fsautocomplete[[:space:]]+\\([0-9\.]*\\)[[:space:]]+" nil t)
      (match-string 1))))

(defun eglot-fsharp-current-version-p (version)
  "Return t if the installation is up-to-date compared to VERSION string."
  (and (file-exists-p (concat (file-remote-p default-directory) (eglot-fsharp--path-to-server)))
       (equal version (eglot-fsharp--installed-version))))

(defun eglot-fsharp--install-core (version)
  "Download and install fsautocomplete as a dotnet tool at version VERSION in `eglot-fsharp-server-install-dir'."
  (let* ((default-directory (concat (file-remote-p default-directory)
                                    (file-name-directory (eglot-fsharp--path-to-server))))
         (stderr-file (make-temp-file "dotnet_stderr"))
         (local-tool-path (or (file-remote-p default-directory 'localname) default-directory))
         (process-file-uninstall-args (if eglot-fsharp-server-install-dir
                                          (list "dotnet" nil `(nil ,stderr-file) nil "tool" "uninstall" "fsautocomplete" "--tool-path" local-tool-path)
                                        (list "dotnet" nil `(nil ,stderr-file) nil "tool" "uninstall" "-g" "fsautocomplete")))
         (process-file-install-args (if eglot-fsharp-server-install-dir
                                        (list "dotnet" nil `(nil ,stderr-file) nil "tool" "install" "fsautocomplete" "--tool-path" local-tool-path "--version" version)
                                      (list "dotnet" nil `(nil ,stderr-file) nil "tool" "install" "fsautocomplete" "-g" "--version" version))))
    (make-directory default-directory t)
    (condition-case err
        (progn
          (unless (or (eglot-fsharp-current-version-p version) (not (eglot-fsharp--installed-version)))
            (message "Uninstalling fsautocomplete version %s" (eglot-fsharp--installed-version))
            (unless (zerop (apply #'process-file process-file-uninstall-args))
              (error  "'dotnet tool uninstall fsautocomplete ... failed")))
          (unless (zerop (apply #'process-file process-file-install-args))
            (error "'dotnet tool install fsautocomplete --tool-path %s --version %s' failed" default-directory  version)))
      (error
       (let ((stderr (with-temp-buffer
                       (insert-file-contents stderr-file)
                       (buffer-string))))
         (delete-file stderr-file)
         (signal (car err) (format "%s: %s" (cdr err) stderr)))))
    (message "Installed fsautocomplete to %s" (eglot-fsharp--path-to-server))))

(defun eglot-fsharp--maybe-install (&optional version)
  "Downloads F# compiler service, and install in `eglot-fsharp-server-install-dir'."
  (unless eglot-fsharp-server-install-dir
    (make-directory (concat (file-remote-p default-directory)
                            (file-name-directory (eglot-fsharp--path-to-server))) t))
  (let* ((version (or version (if (eq eglot-fsharp-server-version 'latest)
				  (eglot-fsharp--latest-version)
				eglot-fsharp-server-version))))
    (unless (eglot-fsharp-current-version-p version)
      (eglot-fsharp--install-core version))))

;;;###autoload
(defun eglot-fsharp (interactive)
  "Return `eglot' contact when FsAutoComplete is installed.
Ensure FsAutoComplete is installed (when called INTERACTIVE)."
  (when interactive (eglot-fsharp--maybe-install))
  (cons 'eglot-fsautocomplete
        (if (file-remote-p default-directory)
            `("sh" ,shell-command-switch ,(concat "cat|"  (mapconcat #'shell-quote-argument
                                                                     (cons (eglot-fsharp--path-to-server) eglot-fsharp-server-args) " ")))
          (cons (eglot-fsharp--path-to-server) eglot-fsharp-server-args))))


(defclass eglot-fsautocomplete (eglot-lsp-server) ()
  :documentation "F# FsAutoComplete langserver.")

(cl-defmethod eglot-initialization-options ((_server eglot-fsautocomplete))
  "Passes through required FsAutoComplete initialization options."
  eglot-fsharp-fsautocomplete-args)

;; FIXME: this should be fixed in FsAutocomplete
(cl-defmethod xref-backend-definitions :around ((_type symbol) _identifier)
  "FsAutoComplete breaks spec and and returns error instead of empty list."
  (if (eq major-mode 'fsharp-mode)
      (condition-case err
          (cl-call-next-method)
        (jsonrpc-error
         (not (equal (cadddr err) '(jsonrpc-error-message . "Could not find declaration")))))
    (when (cl-next-method-p)
      (cl-call-next-method))))

(add-to-list 'eglot-server-programs `(fsharp-mode . eglot-fsharp))

(provide 'eglot-fsharp)
;;; eglot-fsharp.el ends here


================================================
FILE: fsharp-mode-font.el
================================================
;;; fsharp-mode-font.el --- Syntax highlighting for F#

;; Copyright (C) 1997 INRIA

;; Author: 1993-1997 Xavier Leroy, Jacques Garrigue and Ian T Zimmerman
;;         2010-2011 Laurent Le Brun <laurent@le-brun.eu>
;; Maintainer: Robin Neatherway <robin.neatherway@gmail.com>
;; Keywords: languages

;; This file is not part of GNU Emacs.

;; This file is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 3, or (at your option)
;; any later version.

;; This file is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING.  If not, write to
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.

;;; Commentary:

;;; Code:

(defgroup fsharp-ui nil
  "F# UI group for the defcustom interface."
  :prefix "fsharp-ui-"
  :group 'fsharp
  :package-version '(fsharp-mode . "1.9.2"))

(defface fsharp-ui-generic-face
  '((t (:inherit default)))
  "Preprocessor face"
  :group 'fsharp-ui)

(defface fsharp-ui-operator-face
  '((t (:foreground "LightSkyBlue")))
  "Preprocessor face"
  :group 'fsharp-ui)

(defface fsharp-ui-warning-face
  '((t (:inherit font-lock-warning-face)))
  "Face for warnings."
  :group 'fsharp-ui)

(defface fsharp-ui-error-face
  '((t (:inherit font-lock-error-face :underline t)))
  "Face for errors"
  :group 'fsharp-ui)

(defmacro def-fsharp-compiled-var (sym init &optional docstring)
  "Defines a SYMBOL as a constant inside an eval-and-compile form
with initial value INITVALUE and optional DOCSTRING."
  `(eval-and-compile
     (defvar ,sym ,init ,docstring)))

(def-fsharp-compiled-var fsharp-shebang-regexp
                         "\\(^#!.*?\\)\\([A-Za-z0-9_-]+\\)$"
                         "Capture the #! and path of a shebag in one group and the
  executable in another.")

(def-fsharp-compiled-var fsharp-access-control-regexp
                         "private\\s-+\\|internal\\s-+\\|public\\s-+"
                         "Match `private', `internal', or `public', followed by a space,
  with no capture.")

(def-fsharp-compiled-var fsharp-access-control-regexp-noncapturing
                         (format "\\(?:%s\\)" fsharp-access-control-regexp)
                         "Same as `fsharp-access-control-regexp', but captures")

(def-fsharp-compiled-var fsharp-inline-rec-regexp
                         "inline\\s-+\\|rec\\s-+"
                         "Match `inline' or `rec', followed by a space.")

(def-fsharp-compiled-var fsharp-inline-rec-regexp-noncapturing
                         (format "\\(?:%s\\)" fsharp-inline-rec-regexp)
                         "Match `inline' or `rec', followed by a space, with no capture.")

(def-fsharp-compiled-var fsharp-valid-identifier-regexp
                         "[A-Za-z0-9_']+"
                         "Match a normal, valid F# identifier -- alphanumeric characters
  plus ' and underbar. Does not capture")

(def-fsharp-compiled-var fsharp-function-def-regexp
                         (concat "\\<\\(?:let\\|and\\|with\\)\\s-+"
                                 fsharp-inline-rec-regexp-noncapturing "?"
                                 fsharp-access-control-regexp-noncapturing "*"
                                 (format "\\(%s\\)" fsharp-valid-identifier-regexp)
                                 "\\(?:\\s-+[A-Za-z_]\\|\\s-*(\\)" ;; matches function arguments or open-paren; unclear why 0-9 not in class
                                 ))

(def-fsharp-compiled-var fsharp-pattern-function-regexp
                         (concat "\\<\\(?:let\\|and\\)\\s-+"
                                 fsharp-inline-rec-regexp-noncapturing "?"
                                 fsharp-access-control-regexp-noncapturing "*"
                                 (format "\\(%s\\)" fsharp-valid-identifier-regexp)
                                 "\\s-*=\\s-*function")
                         "Matches an implicit matcher, eg let foo m = function | \"cat\" -> etc.")

;; Note that this regexp is used for iMenu. To font-lock active patterns, we
;; need to use an anchored match in fsharp-font-lock-keywords.
(def-fsharp-compiled-var fsharp-active-pattern-regexp
                         (concat "\\<\\(?:let\\|and\\)\\s-+"
                                 fsharp-inline-rec-regexp-noncapturing "?"
                                 fsharp-access-control-regexp-noncapturing "*"
                                 "(\\(|[A-Za-z0-9_'|]+|\\))\\(?:\\s-+[A-Za-z_]\\|\\s-*(\\)"))

(def-fsharp-compiled-var fsharp-member-access-regexp
                         "\\<\\(?:override\\|member\\|abstract\\)\\s-+"
                         "Matches members declarations and modifiers on classes.")

(def-fsharp-compiled-var fsharp-member-function-regexp
                         (concat fsharp-member-access-regexp
                                 fsharp-inline-rec-regexp-noncapturing "?"
                                 fsharp-access-control-regexp-noncapturing "*"
                                 "\\(?:" fsharp-valid-identifier-regexp "\\.\\)?"
                                 "\\(" fsharp-valid-identifier-regexp "\\)")
                         "Captures the final identifier in a member function declaration.")

(def-fsharp-compiled-var fsharp-overload-operator-regexp
                         (concat fsharp-member-access-regexp
                                 fsharp-inline-rec-regexp-noncapturing "?"
                                 fsharp-access-control-regexp-noncapturing "*"
                                 "\\(([!%&*+-./<=>?@^|~]+)\\)")
                         "Match operators when overloaded by a type/class.")

(def-fsharp-compiled-var fsharp-constructor-regexp
                         (concat "^\\s-*"
                                 fsharp-access-control-regexp-noncapturing "*"
                                 "\\<\\(new\\) *(.*)[^=]*=")
                         "Matches the `new' keyword in a constructor")

(def-fsharp-compiled-var fsharp-type-def-regexp
                         (concat "^\\s-*\\<\\(?:type\\|inherit\\)\\s-+"
                                 fsharp-access-control-regexp-noncapturing "*" ;; match access control 0 or more times
                                 "\\([A-Za-z0-9_'.]+\\)"))

(def-fsharp-compiled-var fsharp-var-or-arg-regexp
                         "\\_<\\([A-Za-z_][A-Za-z0-9_']*\\)\\_>")

(def-fsharp-compiled-var fsharp-explicit-field-regexp
                         (concat "^\\s-*\\(?:val\\|abstract\\)\\s-*\\(?:mutable\\s-+\\)?"
                                 fsharp-access-control-regexp-noncapturing "*" ;; match access control 0 or more times
                                 "\\([A-Za-z_][A-Za-z0-9_']*\\)\\s-*:\\s-*\\([A-Za-z_][A-Za-z0-9_'<> \t]*\\)"))

(def-fsharp-compiled-var fsharp-attributes-regexp
                         "\\(\\[<[A-Za-z0-9_]+[( ]?\\)\\(\".*\"\\)?\\()?>\\]\\)"
                         "Match attributes like [<EntryPoint>]; separately groups contained strings in attributes like [<Attribute(\"property\")>]")

;; F# makes extensive use of operators, many of which have some kind of
;; structural significance.
;;
;; In particular:
;; (| ... |)                 -- banana clips for Active Patterns (handled separately)
;; <@ ... @> and <@@ ... @@> -- quoted expressions
;; <| and |>                 -- left and right pipe (also <||, <|||, ||>, |||>)
;; << and >>                 -- function composition
;; |                         -- match / type expressions

(def-fsharp-compiled-var fsharp-operator-quote-regexp
                         "\\(<@\\{1,2\\}\\)\\(?:.*\\)\\(@\\{1,2\\}>\\)"
                         "Font lock <@/<@@ and @>/@@> operators.")

(def-fsharp-compiled-var fsharp-operator-pipe-regexp
                         "<|\\{1,3\\}\\||\\{1,3\\}>"
                         "Match the full range of pipe operators -- |>, ||>, |||>, etc.")

(def-fsharp-compiled-var fsharp-custom-operator-with-pipe-regexp
                         (let ((op-chars "!%&\\*\\+\\-\\./<=>@\\^~") ;; all F# custom operator chars except for `|`
                               (backward-pipe "<|\\{1,3\\}")
                               (forward-pipe "|\\{1,3\\}>")
                               (alt "\\|"))
                           (concat "[" op-chars "|]*" backward-pipe "[" op-chars  "]+"
                                   alt "[" op-chars "|]+" backward-pipe "[" op-chars  "]*"
                                   alt "[" op-chars  "]*" forward-pipe  "[" op-chars "|]+"
                                   alt "[" op-chars  "]+" forward-pipe  "[" op-chars "|]*"))
                         "Match operators that contains pipe sequence -- <|>, |>>, <<|, etc.")

(def-fsharp-compiled-var fsharp-operator-case-regexp
                         "\\s-+\\(|\\)[A-Za-z0-9_' ]"
                         "Match literal | in contexts like match and type declarations.")

(defvar fsharp-imenu-generic-expression
  `((nil                 ,(concat "^\\s-*" fsharp-function-def-regexp) 1)
    (nil                 ,(concat "^\\s-*" fsharp-pattern-function-regexp) 1)
    ("Active Pattern"    ,(concat "^\\s-*" fsharp-active-pattern-regexp) 1)
    ("Member"            ,(concat "^\\s-*" fsharp-member-function-regexp) 1)
    ("Overload Operator" ,(concat "^\\s-*" fsharp-overload-operator-regexp) 1)
    ("Constructor"       ,fsharp-constructor-regexp 1)
    ("Type"              ,fsharp-type-def-regexp 1)
    ("Module"            ,(concat "\\s-*module " fsharp-var-or-arg-regexp) 1))

  "Provide iMenu support through font-locking regexen.")

(defun fsharp-imenu-load-index ()
  "Hook up the provided regexen to enable imenu support."
  (setq imenu-generic-expression fsharp-imenu-generic-expression))

(add-hook 'fsharp-mode-hook #'fsharp-imenu-load-index)

(defun fsharp-var-pre-form ()
  (save-excursion
    (re-search-forward "\\(:\\s-*\\w[^)]*\\)?=" nil t)
    (match-beginning 0)))

(defun fsharp-fun-pre-form ()
  (save-excursion
    (search-forward "->")))

;; Preprocessor directives (3.3)
(def-fsharp-compiled-var fsharp-ui-preproessor-directives
                         '("#if" "#else" "#endif" "#light"))

;; Compiler directives (12.4)
(def-fsharp-compiled-var fsharp-ui-compiler-directives
                         '("#nowarn" "#load" "#r" "#reference" "#I"
                           "#Include" "#q" "#quit" "#time" "#help"))

;; Lexical matters (18.4)
(def-fsharp-compiled-var fsharp-ui-lexical-matters
                         '("#indent"))

;; Line Directives (3.9)
(def-fsharp-compiled-var fsharp-ui-line-directives
                         '("#line"))

;; Identifier replacements (3.11)
(def-fsharp-compiled-var fsharp-ui-identifier-replacements
                         '("__SOURCE_DIRECTORY__" "__SOURCE_FILE__" "__LINE__"))

;; F# keywords (5.0)
(def-fsharp-compiled-var fsharp-ui-fsharp-threefour-keywords
                         '("abstract" "and" "and!" "as" "assert" "base" "begin"
                           "class" "default" "delegate" "do" "do!" "done"
                           "downcast" "downto" "elif" "else" "end"
                           "exception" "extern" "false" "finally" "for" "fun"
                           "function" "global" "if" "in" "inherit" "inline"
                           "interface" "internal" "lazy" "let" "let!"
                           "match" "match!" "member" "module" "mutable" "namespace"
                           "new" "not" "null" "of" "open" "or" "override"
                           "private" "public" "rec" "return" "return!"
                           "select" "static" "struct" "then" "to" "true"
                           "try" "type" "upcast" "use" "use!"  "val" "void"
                           "when" "while" "with" "yield" "yield!"))

;; "Reserved because they are reserved in OCaml"
(def-fsharp-compiled-var fsharp-ui-ocaml-reserved-words
                         '("asr" "land" "lor" "lsl" "lsr" "lxor" "mod" "sig"))

;; F# reserved words for future use
(def-fsharp-compiled-var fsharp-ui-reserved-words
                         '("atomic" "break" "checked" "component" "const"
                           "constraint" "constructor" "continue" "eager"
                           "event" "external" "fixed" "functor" "include"
                           "method" "mixin" "object" "parallel" "process"
                           "protected" "pure" "sealed" "tailcall" "trait"
                           "virtual" "volatile"))

;; RMD 2016-09-30 -- This was pulled out separately with the following comment
;; when I got here. Not clear to me why it's on it's own, or even precisely what
;; the comment means. But: `async' is a valid F# keyword and needs to go someplace,
;; so I've left it here. For now.
;;
;; Workflows not yet handled by fsautocomplete but async
;; always present
(def-fsharp-compiled-var fsharp-ui-async-words
                         '("async")
                         "Just the word async, in a list.")

(def-fsharp-compiled-var fsharp-ui-word-list-regexp
                         (regexp-opt
                          `(,@fsharp-ui-async-words
                            ,@fsharp-ui-compiler-directives
                            ,@fsharp-ui-fsharp-threefour-keywords
                            ,@fsharp-ui-identifier-replacements
                            ,@fsharp-ui-lexical-matters
                            ,@fsharp-ui-ocaml-reserved-words
                            ,@fsharp-ui-preproessor-directives
                            ,@fsharp-ui-reserved-words
                            ,@fsharp-ui-line-directives)
                          'symbols))

(defconst fsharp-font-lock-keywords
  (eval-when-compile
    `((,fsharp-ui-word-list-regexp 0 font-lock-keyword-face)
      ;; shebang
      (,fsharp-shebang-regexp
       (1 font-lock-comment-face)
       (2 font-lock-keyword-face))
      ;; attributes
      (,fsharp-attributes-regexp
       (1 font-lock-preprocessor-face)
       (2 font-lock-string-face nil t)
       (3 font-lock-preprocessor-face))
      ;; ;; type defines
      (,fsharp-type-def-regexp 1 font-lock-type-face)
      (,fsharp-function-def-regexp 1 font-lock-function-name-face)
      (,fsharp-pattern-function-regexp 1 font-lock-function-name-face)
      ;; Active Pattern
      ("(|" (0 'fsharp-ui-operator-face)
       ("\\([A-Za-z'_]+\\)\\(|)?\\)"
        nil nil
        (1 font-lock-function-name-face)
        (2 'fsharp-ui-operator-face)))
      (,fsharp-custom-operator-with-pipe-regexp . 'fsharp-ui-generic-face)
      (,fsharp-operator-pipe-regexp . 'fsharp-ui-operator-face)
      (,fsharp-member-function-regexp 1 font-lock-function-name-face)
      (,fsharp-overload-operator-regexp 1 font-lock-function-name-face)
      (,fsharp-constructor-regexp 1 font-lock-function-name-face)
      (,fsharp-operator-case-regexp 1 'fsharp-ui-operator-face)
      (,fsharp-operator-quote-regexp  (1 'fsharp-ui-operator-face)
                                      (2 'fsharp-ui-operator-face))
      ("[^:]:\\s-*\\(\\<[A-Za-z0-9_' ]*[^ ;\n,)}=<-]\\)\\(<[^>]*>\\)?"
       (1 font-lock-type-face)
       ;; 'prevent generic type arguments from being rendered in variable face
       (2 'fsharp-ui-generic-face nil t))
      (,(format "^\\s-*\\<\\(let\\|use\\|override\\|member\\|and\\|\\(?:%snew\\)\\)\\_>"
                (concat fsharp-access-control-regexp "*"))
       (0 font-lock-keyword-face) ; let binding and function arguments
       (,fsharp-var-or-arg-regexp
        (fsharp-var-pre-form) nil
        (1 font-lock-variable-name-face nil t)))
      ("\\<fun\\>"
       (0 font-lock-keyword-face) ; lambda function arguments
       (,fsharp-var-or-arg-regexp
        (fsharp-fun-pre-form) nil
        (1 font-lock-variable-name-face nil t)))
      (,fsharp-type-def-regexp
       (0 'font-lock-keyword-face) ; implicit constructor arguments
       (,fsharp-var-or-arg-regexp
        (fsharp-var-pre-form) nil
        (1 font-lock-variable-name-face nil t)))
      (,fsharp-explicit-field-regexp
       (1 font-lock-variable-name-face)
       (2 font-lock-type-face))

      ;; open namespace
      ("\\<open\s\\([A-Za-z0-9_.]+\\)" 1 font-lock-type-face)

      ;; module/namespace
      ("\\_<\\(?:module\\|namespace\\)\s\\([A-Za-z0-9_.]+\\)" 1 font-lock-type-face)
      )))

(defun fsharp-ui-setup-font-lock ()
  "Set up font locking for F# Mode."
  (setq font-lock-defaults
        '(fsharp-font-lock-keywords)))

(add-hook 'fsharp-mode-hook #'fsharp-ui-setup-font-lock)

(defun fsharp--syntax-propertize-function (start end)
  (goto-char start)
  (fsharp--syntax-string end)
  (funcall (syntax-propertize-rules
            ("\\(@\\)\"" (1 (prog1 "|" (fsharp--syntax-string end)))) ; verbatim string
            ("\\(\"\\)\"\"" (1 (prog1 "|" (fsharp--syntax-string end)))) ; triple-quoted string
            ("\\('\\)\\(?:[^\n\t\r\b\a\f\v\\\\]\\|\\\\[\"'ntrbafv\\\\]\\|\\\\u[0-9A-Fa-f]\\{4\\}\\|\\\\[0-9]\\{3\\}\\)\\('\\)"
             (1 "|") (2 "|")) ; character literal
            ("\\((\\)/" (1 "()"))
            ("\\(\(\\)\\*[!%&*+-\\./<=>@^|~?]*[\n\t\r\b\a\f\v ]*\)" (1 "()")) ; symbolic operator starting (* is not a comment
            ("\\(/\\)\\*" (1 ".")))
           start end))

(defun fsharp--syntax-string (end)
  (let* ((pst (syntax-ppss))
         (instr (nth 3 pst))
         (start (nth 8 pst)))
    (when (eq t instr) ; Then we are in a custom string
      (cond
       ((eq ?@ (char-after start)) ; Then we are in a verbatim string
        (while
            (when (re-search-forward "\"\"?" end 'move)
              (if (> (- (match-end 0) (match-beginning 0)) 1)
                  t ;; Skip this "" and keep looking further.
                (put-text-property (- (match-beginning 0) 1) (- (match-end 0) 1)
                                   'syntax-table (string-to-syntax "."))
                (put-text-property (match-beginning 0) (match-end 0)
                                   'syntax-table (string-to-syntax "|"))
                nil))))

       (t ; Then we are in a triple-quoted string
        (when (re-search-forward "\"\"\"" end 'move)
          (put-text-property (- (match-beginning 0) 1) (match-beginning 0)
                             'syntax-table (string-to-syntax "."))
          (put-text-property (match-beginning 0) (match-end 0)
                             'syntax-table (string-to-syntax "|"))))))))

(provide 'fsharp-mode-font)

;;; fsharp-mode-font.el ends here


================================================
FILE: fsharp-mode-structure.el
================================================
;;; fsharp-mode-indent.el --- Stucture Definition, Mark, and Motion for F#

;; Copyright (C) 2010 Laurent Le Brun

;; Author: 2010-2011 Laurent Le Brun <laurent@le-brun.eu>
;; Maintainer: Jürgen Hötzel <juergen@hoetzel.info>
;; Keywords: languages

;; This file is not part of GNU Emacs.

;; This file is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 3, or (at your option)
;; any later version.

;; This file is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING.  If not, write to
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.

;;; Commentary:
;; This module defines variables and functions related to the structure of F#
;; code, and motion around and through that code. SMIE is used to set certain
;; default configurations. In particular, `smie' expects to set
;; `forward-sexp-function' and `indent-line-function', the latter of which we
;; currently override.
;;
;; SMIE configs by m00nlight Wang <dot.wangyushi@gmail.com>, 2015
;; Last major update by Ross Donaldson <@gastove>, 2019

;;; Code:

(require 'comint)
(require 'custom)
(require 'compile)
(require 'smie)

;;-------------------------- Customization Variables --------------------------;;

(defcustom fsharp-tab-always-indent t
  "*Non-nil means TAB in Fsharp mode should always reindent the current line,
regardless of where in the line point is when the TAB command is used."
  :type 'boolean
  :group 'fsharp)

(defcustom fsharp-indent-offset 4
  "*Amount of offset per level of indentation.
`\\[fsharp-guess-indent-offset]' can usually guess a good value when
you're editing someone else's Fsharp code."
  :type 'integer
  :group 'fsharp)

(defalias 'fsharp-indent-level 'fsharp-indent-offset
  "Backwards-compatibility alias. `fsharp-indent-level' was
  configuring the same thing as `fsharp-indent-offset', but less
  clearly and in a different file, and free from update by
  functions like offset-guessing.")

(defcustom fsharp-continuation-offset 4
  "*Additional amount of offset to give for some continuation lines.
Continuation lines are those that immediately follow a backslash
terminated line.  Only those continuation lines for a block opening
statement are given this extra offset."
  :type 'integer
  :group 'fsharp)

(defcustom fsharp-conservative-indentation-after-bracket nil
  "Indent by fsharp-continuation-offset also after an opening bracket.
The default indentation depth on a new line after an opening
bracket is one column further from the opening bracket. Indenting much less is
allowed, because brackets reset the current offside column."
  :type 'boolean
  :group 'fsharp)

(defcustom fsharp-smart-indentation t
  "*Should `fsharp-mode' try to automagically set some indentation variables?
When this variable is non-nil, two things happen when a buffer is set
to `fsharp-mode':

    1. `fsharp-indent-offset' is guessed from existing code in the buffer.
       Only guessed values between 2 and 8 are considered.  If a valid
       guess can't be made (perhaps because you are visiting a new
       file), then the value in `fsharp-indent-offset' is used.

    2. `indent-tabs-mode' is turned off if `fsharp-indent-offset' does not
       equal `tab-width' (`indent-tabs-mode' is never turned on by
       Fsharp mode).  This means that for newly written code, tabs are
       only inserted in indentation if one tab is one indentation
       level, otherwise only spaces are used.

Note that both these settings occur *after* `fsharp-mode-hook' is run,
so if you want to defeat the automagic configuration, you must also
set `fsharp-smart-indentation' to nil in your `fsharp-mode-hook'."
  :type 'boolean
  :group 'fsharp)

(defcustom fsharp-honor-comment-indentation t
  "*Controls how comment lines influence subsequent indentation.

When nil, all comment lines are skipped for indentation purposes, and
if possible, a faster algorithm is used (i.e. X/Emacs 19 and beyond).

When t, lines that begin with a single `//' are a hint to subsequent
line indentation.  If the previous line is such a comment line (as
opposed to one that starts with `fsharp-block-comment-prefix'), then its
indentation is used as a hint for this line's indentation.  Lines that
begin with `fsharp-block-comment-prefix' are ignored for indentation
purposes.

When not nil or t, comment lines that begin with a single `//' are used
as indentation hints, unless the comment character is in column zero."
  :type '(choice
          (const :tag "Skip all comment lines (fast)" nil)
          (const :tag "Single // `sets' indentation for next line" t)
          (const :tag "Single // `sets' indentation except at column zero"
                 other)
          )
  :group 'fsharp)

(defcustom fsharp-backspace-function 'backward-delete-char-untabify
  "*Function called by `fsharp-electric-backspace' when deleting backwards."
  :type 'function
  :group 'fsharp)

(defcustom fsharp-delete-function 'delete-char
  "*Function called by `fsharp-electric-delete' when deleting forwards."
  :type 'function
  :group 'fsharp)


;;--------------------------------- Constants ---------------------------------;;
;; TODO[gastove|2019-10-30] So much:
;;   - No SQTQ in F#
;;   - No raw strings either
;;   - But there *are* verbatim strings that begin with @
;;   - And can use \ to escape a newline
;;   - But *can* contain newlines
;; It's a good thing this isn't called often, because it is a mess and wrong.
(defconst fsharp-stringlit-re
  (concat
   ;; These fail if backslash-quote ends the string (not worth
   ;; fixing?).  They precede the short versions so that the first two
   ;; quotes don't look like an empty short string.
   ;;
   ;; (maybe raw), long single quoted triple quoted strings (SQTQ),
   ;; with potential embedded single quotes
   "[rR]?'''[^']*\\(\\('[^']\\|''[^']\\)[^']*\\)*'''"
   "\\|"
   ;; (maybe raw), long double quoted triple quoted strings (DQTQ),
   ;; with potential embedded double quotes
   "[rR]?\"\"\"[^\"]*\\(\\(\"[^\"]\\|\"\"[^\"]\\)[^\"]*\\)*\"\"\""
   "\\|"
   "[rR]?'\\([^'\n\\]\\|\\\\.\\)*'"     ; single-quoted
   "\\|"                                ; or
   "[rR]?\"\\([^\"\n\\]\\|\\\\.\\)*\""  ; double-quoted
   )
  "Regular expression matching a Fsharp string literal.")


(defconst fsharp--hanging-operator-re
  (concat ".*\\(" (mapconcat 'identity
                             '("+" "-" "*" "/")
                             "\\|")
          "\\)$")
  "Regular expression matching unterminated algebra expressions.")


;; TODO[gastove|2019-10-22] This doesn't match (* long comments *), but it *does* capture.
(defconst fsharp-blank-or-comment-re "[ \t]*\\(//.*\\)?"
  "Regular expression matching a blank or comment line.")

(defconst fsharp-outdent-re
  (concat "\\(" (mapconcat 'identity
                           '("else"
                             "with"
                             "finally"
                             "end"
                             "done"
                             "elif"
                             "}")
                           "\\|")
          "\\)")
  "Regular expression matching statements to be dedented one level.")


(defconst fsharp-block-closing-keywords-re
  "\\(end\\|done\\|raise\\|failwith\\|failwithf\\|rethrow\\|exit\\)"
  "Regular expression matching keywords which typically close a block.")


(defconst fsharp-no-outdent-re
  (concat
   "\\("
   (mapconcat 'identity
              (list "try"
                    "while\\s +.*"
                    "for\\s +.*"
                    "then"
                    (concat fsharp-block-closing-keywords-re "[ \t\n]")
                    )
              "\\|")
   "\\)")
  "Regular expression matching lines not to dedent after.")


(defconst fsharp-block-opening-re
  (concat "\\(" (mapconcat 'identity
                           '("then"
                             "else"
                             "with"
                             "finally"
                             "class"
                             "struct"
                             "="        ; for example: let f x =
                             "->"
                             "do"
                             "try"
                             "function")
                           "\\|")
          "\\)")
  "Regular expression matching expressions which begin a block")


;; TODO: this regexp looks transparently like a python regexp. That means it's almost certainly wrong.
(defvar fsharp-parse-state-re
  (concat
   "^[ \t]*\\(elif\\|else\\|while\\|def\\|class\\)\\>"
   "\\|"
   "^[^ /\t\n]"))


(defsubst fsharp-point (position)
  "Returns the value of point at certain commonly referenced POSITIONs.
POSITION can be one of the following symbols:

  bol  -- beginning of line
  eol  -- end of line
  bod  -- beginning of def or class
  eod  -- end of def or class
  bob  -- beginning of buffer
  eob  -- end of buffer
  boi  -- back to indentation
  bos  -- beginning of statement

This function preserves point and mark."
  (save-mark-and-excursion
    (cond
     ((eq position 'bol) (beginning-of-line))
     ((eq position 'eol) (end-of-line))
     ((eq position 'bod) (fsharp-beginning-of-def-or-class 'either))
     ((eq position 'eod) (fsharp-end-of-def-or-class 'either))
     ((eq position 'bob) (point-min))
     ((eq position 'eob) (point-max))
     ((eq position 'boi) (back-to-indentation))
     ((eq position 'bos) (fsharp-goto-initial-line))
     (t (error "Unknown buffer position requested: %s" position)))

    (point)))


;;-------------------------------- Predicates --------------------------------;;

(defun fsharp-in-literal-p (&optional lim)
  "Return non-nil if point is in a Fsharp literal (a comment or
string). The return value is specifically one of the symbols
\\='comment or \\='string. Optional argument LIM indicates the
beginning of the containing form, i.e. the limit on how far back
to scan."
  ;; NOTE: Watch out for infinite recursion between this function and
  ;; `fsharp-point'.
  (let* ((lim (or lim (fsharp-point 'bod)))
         (state (parse-partial-sexp lim (point))))
    (cond
     ((nth 3 state) 'string)
     ((nth 4 state) 'comment)
     (t nil))))


(defun fsharp-outdent-p ()
  "Returns non-nil if the current line should dedent one level."
  (save-excursion
    (progn (back-to-indentation)
           (looking-at fsharp-outdent-re))))


(defun fsharp--indenting-comment-p ()
  "Returns non-nil if point is in an indenting comment line, otherwise nil.

Definition: Indenting comment line. A line containing only a
comment, but which is treated like a statement for indentation
calculation purposes. Such lines are only treated specially by
the mode; they are not treated specially by the Fsharp
interpreter.

The first non-blank line following an indenting comment line is
given the same amount of indentation as the indenting comment
line.

All other comment-only lines are ignored for indentation
purposes.

Are we looking at a comment-only line which is *not* an indenting
comment line? If so, we assume that it's been placed at the
desired indentation, so leave it alone. Indenting comment lines
are aligned as statements."
  ;; TODO[gastove|2019-10-22] this is a bug. The regular expression here matches
  ;; comments only if there is *no whites space* between the // and the first
  ;; characters in the comment.
  (and (looking-at "[ \t]*//[^ \t\n]")
       (fboundp 'forward-comment)
       (<= (current-indentation)
           (save-excursion
             (forward-comment (- (point-max)))
             (current-indentation)))))


(defun fsharp--hanging-operator-continuation-line-p ()
  "Return t if point is on at least the *second* line of the
buffer, and the previous line matches `fsharp--hanging-operator-re' --
which is to say, it ends in +, -, /, or *."
  (save-excursion
    (beginning-of-line)
    (and
     (not (bobp))
     ;; make sure; since eq test passed, there is a preceding line
     (forward-line -1)                  ; always true -- side effect
     ;; matches any line, so long as it ends with one of +, -, *, or /
     (looking-at fsharp--hanging-operator-re))))


;; TODO[gastove|2019-10-31] This function doesn't do everything it needs to.
;; Currently, it only reports a continuation line if there's a hanging
;; arithmetic operator *or* if we're inside a delimited block (something like {}
;; or []). It _needs_ to also respect symbols that open a new whitespace block
;; -- things like -> at the end of a line, or |> at the beginning of one.
;;
;; The trick is: the other major place where |> and -> lines are considered is
;; in `fsharp-compute-indentation', which... catches "undelimited" blocks as a
;; default case. They aren't _explicitly_ detected.
;;
;; In all, this makes me think we need a cleaner distinction between a
;; "continuation line" and a "relative line" -- that is, a line that continues
;; an ongoing expression (a sequence of items in a list, the completion of an
;; arithmetic expression) and a new block scope opened by a single symbol and
;; terminated with whitespace.
;;
;; We do already have `fsharp-statement-opens-block-p', which we could make much
;; more active use of. However: `fsharp-statement-opens-block-p' calls
;; `fsharp-goto-beyond-final-line', which... relies on
;; `fsharp-continuation-line-p'. So that will need untangling.
(defun fsharp-continuation-line-p ()
  "Return t if current line continues a line with a hanging
arithmetic operator *or* is inside a nesting construct (a list,
computation expression, etc)."
  (save-excursion
    (beginning-of-line)
    (or (fsharp--hanging-operator-continuation-line-p)
        (fsharp-nesting-level))))


(defun fsharp--previous-line-continuation-line-p ()
  "Returns true if previous line is a continuation line"
  (save-excursion
    (forward-line -1)
    (fsharp-continuation-line-p)))


(defun fsharp-statement-opens-block-p ()
  "Return t if the current statement opens a block. For instance:

type Shape =
    | Square
    | Rectangle

or:

let computation = [ this; that ]
    |> Array.someCalculation

Point should be at the start of a statement."
  (save-excursion
    (let ((start (point))
          (finish (progn (fsharp-goto-beyond-final-line) (1- (point))))
          (searching t)
          (answer nil)
          state)
      (goto-char start)
      ;; Keep searching until we're finished.
      (while searching
        (if (re-search-forward fsharp-block-opening-re finish t)
            (if (eq (point) finish)
                ;; sure looks like it opens a block -- but it might
                ;; be in a comment
                (progn
                  (setq searching nil)  ; search is done either way
                  (setq state (parse-partial-sexp start
                                                  (match-beginning 0)))
                  (setq answer (not (nth 4 state)))))
          ;; search failed: couldn't find a reason to believe we're opening a block.
          (setq searching nil)))
      answer)))


;; TODO[@gastove|2019-10-22]: the list of keywords this function claims to catch
;; does not at all match the keywords in the regexp it wraps.
(defun fsharp-statement-closes-block-p ()
  "Return t iff the current statement closes a block.
I.e., if the line starts with `return', `raise', `break', `continue',
and `pass'.  This doesn't catch embedded statements."
  (let ((here (point)))
    (fsharp-goto-initial-line)
    (back-to-indentation)
    (prog1
        (looking-at (concat fsharp-block-closing-keywords-re "\\>"))
      (goto-char here))))


;;---------------------------- Electric Keystrokes ----------------------------;;

(defun fsharp-electric-colon (arg)
  "Insert a colon.
In certain cases the line is dedented appropriately.  If a numeric
argument ARG is provided, that many colons are inserted
non-electrically.  Electric behavior is inhibited inside a string or
comment."
  (interactive "*P")
  (self-insert-command (prefix-numeric-value arg))
  ;; are we in a string or comment?
  (if (save-excursion
        (let ((pps (parse-partial-sexp (save-excursion
                                         (fsharp-beginning-of-def-or-class)
                                         (point))
                                       (point))))
          (not (or (nth 3 pps) (nth 4 pps)))))
      (save-excursion
        (let ((here (point))
              (outdent 0)
              (indent (fsharp-compute-indentation t)))
          (if (and (not arg)
                   (fsharp-outdent-p)
                   (= indent (save-excursion
                               (fsharp-next-statement -1)
                               (fsharp-compute-indentation t)))
                   )
              (setq outdent fsharp-indent-offset))
          ;; Don't indent, only dedent.  This assumes that any lines
          ;; that are already dedented relative to
          ;; fsharp-compute-indentation were put there on purpose.  It's
          ;; highly annoying to have `:' indent for you.  Use TAB, C-c
          ;; C-l or C-c C-r to adjust.  TBD: Is there a better way to
          ;; determine this???
          (if (< (current-indentation) indent) nil
            (goto-char here)
            (beginning-of-line)
            (delete-horizontal-space)
            (indent-to (- indent outdent)))))))


;; Electric deletion
(defun fsharp-electric-backspace (arg)
  "Delete preceding character or levels of indentation.
Deletion is performed by calling the function in `fsharp-backspace-function'
with a single argument (the number of characters to delete).

If point is at the leftmost column, delete the preceding newline.

Otherwise, if point is at the leftmost non-whitespace character of a
line that is neither a continuation line nor a non-indenting comment
line, or if point is at the end of a blank line, this command reduces
the indentation to match that of the line that opened the current
block of code.  The line that opened the block is displayed in the
echo area to help you keep track of where you are.  With
\\[universal-argument] dedents that many blocks (but not past column
zero).

Otherwise the preceding character is deleted, converting a tab to
spaces if needed so that only a single column position is deleted.
\\[universal-argument] specifies how many characters to delete;
default is 1.

When used programmatically, argument ARG specifies the number of
blocks to dedent, or the number of characters to delete, as indicated
above."
  (interactive "*p")
  (if (or (/= (current-indentation) (current-column))
          (bolp)
          (fsharp-continuation-line-p))

      (funcall fsharp-backspace-function arg)
    ;; else indent the same as the colon line that opened the block
    ;; force non-blank so fsharp-goto-block-up doesn't ignore it
    (insert-char ?* 1)
    (backward-char)
    (let ((base-indent 0)               ; indentation of base line
          (base-text "")                ; and text of base line
          (base-found-p nil))
      (save-excursion
        (while (< 0 arg)
          (condition-case nil           ; in case no enclosing block
              (progn
                (fsharp-goto-block-up 'no-mark)
                (setq base-indent (current-indentation)
                      base-text   (fsharp-suck-up-leading-text)
                      base-found-p t))
            (error nil))
          (setq arg (1- arg))))
      (delete-char 1)                   ; toss the dummy character
      (delete-horizontal-space)
      (indent-to base-indent)
      (if base-found-p
          (message "Closes block: %s" base-text)))))


(defun fsharp-electric-delete (arg)
  "Delete preceding or following character or levels of whitespace.

The behavior of this function depends on the variable
`delete-key-deletes-forward'.  If this variable is nil (or does not
exist, as in older Emacsen and non-XEmacs versions), then this
function behaves identically to \\[c-electric-backspace].

If `delete-key-deletes-forward' is non-nil and is supported in your
Emacs, then deletion occurs in the forward direction, by calling the
function in `fsharp-delete-function'.

\\[universal-argument] (programmatically, argument ARG) specifies the
number of characters to delete (default is 1)."
  (interactive "*p")
  (funcall fsharp-delete-function arg))


;; required for pending-del/delsel/delete-selection minor modes
(put 'fsharp-electric-colon 'delete-selection t) ;delsel
(put 'fsharp-electric-colon 'pending-delete   t) ;pending-del
(put 'fsharp-electric-backspace 'delete-selection 'supersede) ;delsel
(put 'fsharp-electric-backspace 'pending-delete   'supersede) ;pending-del
(put 'fsharp-electric-delete    'delete-selection 'supersede) ;delsel
(put 'fsharp-electric-delete    'pending-delete   'supersede) ;pending-del


;;-------------------------------- Indentation --------------------------------;;

(defun fsharp-indent-line (&optional arg)
  "Fix the indentation of the current line according to Fsharp rules.
With \\[universal-argument] (programmatically, the optional argument
ARG non-nil), ignore dedenting rules for block closing statements
(e.g. return, raise, break, continue, pass)

This function is normally bound to `indent-line-function' so
\\[indent-for-tab-command] will call it."
  (interactive "P")
  (let* ((ci (current-indentation))
         (move-to-indentation-p (<= (current-column) ci))
         (need (fsharp-compute-indentation (not arg)))
         (cc (current-column)))
    ;; dedent out a level if previous command was the same unless we're in
    ;; column 1
    (if (and (equal last-command this-command)
             (/= cc 0))
        (progn
          (beginning-of-line)
          (delete-horizontal-space)
          (indent-to (* (/ (- cc 1) fsharp-indent-offset) fsharp-indent-offset)))

      (progn
        ;; see if we need to dedent
        (if (fsharp-outdent-p)
            (setq need (- need fsharp-indent-offset)))

        (if (or fsharp-tab-always-indent
                move-to-indentation-p)
            (progn (if (/= ci need)
                       (save-excursion
                         (beginning-of-line)
                         (delete-horizontal-space)
                         (indent-to need)))
                   (if move-to-indentation-p (back-to-indentation)))
          (insert-tab))))))


;; NOTE[gastove|2019-10-25] An interesting point: this function is *only* ever
;; called if `open-bracket-pos' is non-nil; `open-bracket-pos' is generated by
;; `fsharp-nesting-level', which *only* returns non nil for non-string
;; characters. And yet: we don't just rely on `open-bracket-pos' as we compute
;; indentation, and I'm honestly not sure why.
(defun fsharp--compute-indentation-open-bracket (open-bracket-pos)
  "Computes indentation for a line within an open bracket expression."
  (save-excursion
    (let ((startpos (point))
          placeholder)
      ;; align with first item in list; else a normal
      ;; indent beyond the line with the open bracket
      (goto-char (1+ open-bracket-pos)) ; just beyond bracket
      ;; NOTE[gastove|2019-10-25] -- consider switching to a forward regexp search
      ;;     with a whitepsace character class.
      ;; is the first list item on the same line?
      (skip-chars-forward " \t")
      (if (and (null (memq (following-char) '(?\n ?# ?\\)))
               (not fsharp-conservative-indentation-after-bracket))
                                        ; yes, so line up with it
          (current-column)
        ;; here follows the else
        ;; first list item on another line, or doesn't exist yet
        ;; TODO[gastove|2019-10-25] this needs to skip past whitespace, newlines,
        ;; *and* comments. I'm not convinced it does.
        (forward-line 1)
        (while (and (< (point) startpos)
                    (looking-at "[ \t]*\\(//\\|[\n\\\\]\\)")) ; skip noise
          (forward-line 1))
        (if (and (< (point) startpos)
                 (/= startpos
                     (save-excursion
                       (goto-char (1+ open-bracket-pos))
                       (forward-comment (point-max))
                       (point))))
            ;; again mimic the first list item
            (current-indentation)
          ;; else they're about to enter the first item

          ;; NOTE[gastove|2019-10-25] Okay, this is all really hard to follow, but
          ;; I *think* what's going on here is:
          ;; - We go to the position of the opening bracket we're trying to compute indentation against.
          ;; - We set placeholder to point (meaning we set `placeholder' to `open-bracket-pos')
          ;; - We call a function that claims to go to the first line of a statement
          ;; - We call a function that I *believe* tries to take us to the opening delimiter of a matched pair
          ;; - We return the current indentation of *that*, plus indent offset
          ;; ... holy moly.
          (goto-char open-bracket-pos)
          (setq placeholder (point))
          (fsharp-goto-initial-line)
          (fsharp-goto-beginning-of-tqs
           (save-excursion (nth 3 (parse-partial-sexp
                                   placeholder (point)))))
          (+ (current-indentation) fsharp-indent-offset))))))


(defun fsharp--compute-indentation-continuation-line ()
  "Computes the indentation for a line which continues the line
above, but only when the previous line is not itself a continuation line."
  (save-excursion
    (forward-line -11)
    (let ((startpos (point))
          (open-bracket-pos (fsharp-nesting-level))
          endpos searching found state placeholder)

      ;; Started on 2nd line in block, so indent more. if base line is an
      ;; assignment with a start on a RHS, indent to 2 beyond the leftmost "=";
      ;; else skip first chunk of non-whitespace characters on base line, + 1 more
      ;; column
      (end-of-line)
      (setq endpos (point)
            searching t)
      (back-to-indentation)
      (setq startpos (point))
      ;; look at all "=" from left to right, stopping at first one not nested in a
      ;; list or string
      (while searching
        (skip-chars-forward "^=" endpos)
        (if (= (point) endpos)
            (setq searching nil)
          (forward-char 1)
          (setq state (parse-partial-sexp startpos (point)))
          (if (and (zerop (car state)) ; not in a bracket
                   (null (nth 3 state))) ; & not in a string
              (progn
                (setq searching nil) ; done searching in any case
                (setq found
                      (not (or
                            (eq (following-char) ?=)
                            (memq (char-after (- (point) 2))
                                  '(?< ?> ?!)))))))))
      (if (or (not found)       ; not an assignment
              (looking-at "[ \t]*\\\\")) ; <=><spaces><backslash>
          (progn
            (goto-char startpos)
            (skip-chars-forward "^ \t\n")))
      ;; if this is a continuation for a block opening
      ;; statement, add some extra offset.
      (+ (current-column) (if (fsharp-statement-opens-block-p)
                              fsharp-continuation-offset 0)
         1))))


(defun fsharp--compute-indentation-relative-to-previous (honor-block-close-p)
  "Indentation based on that of the statement that precedes us;
use the first line of that statement to establish the base, in
case the user forced a non-std indentation for the continuation
lines (if any)"
  ;; skip back over blank & non-indenting comment lines note:
  ;; will skip a blank or non-indenting comment line that
  ;; happens to be a continuation line too.  use fast Emacs 19
  ;; function if it's there.
  (save-excursion
    (let ((bod (fsharp-point 'bod))
          placeholder)
      (if (and (eq fsharp-honor-comment-indentation nil)
               (fboundp 'forward-comment))
          (forward-comment (- (point-max)))
        (let ((prefix-re "//[ \t]*")
              done)
          (while (not done)
            (re-search-backward "^[ \t]*\\([^ \t\n]\\|//\\)" nil 'move)
            (setq done (or (bobp)
                           (and (eq fsharp-honor-comment-indentation t)
                                (save-excursion
                                  (back-to-indentation)
                                  (not (looking-at prefix-re))
                                  ))
                           (and (not (eq fsharp-honor-comment-indentation t))
                                (save-excursion
                                  (back-to-indentation)
                                  (and (not (looking-at prefix-re))
                                       (or (looking-at "[^/]")
                                           (not (zerop (current-column))))))))))))
      ;; if we landed inside a string, go to the beginning of that
      ;; string. this handles triple quoted, multi-line spanning
      ;; strings.
      (fsharp-goto-beginning-of-tqs (nth 3 (parse-partial-sexp bod (point))))
      ;; now skip backward over continued lines
      (setq placeholder (point))
      (fsharp-goto-initial-line)
      ;; we may *now* have landed in a TQS, so find the beginning of
      ;; this string.
      (fsharp-goto-beginning-of-tqs
       (save-excursion (nth 3 (parse-partial-sexp
                               placeholder (point)))))
      (+ (current-indentation)
         (if (fsharp-statement-opens-block-p)
             fsharp-indent-offset
           (if (and honor-block-close-p (fsharp-statement-closes-block-p))
               (- fsharp-indent-offset)
             0))))))


(defun fsharp-newline-and-indent ()
  "Strives to act like the Emacs `newline-and-indent'.
This is just `strives to' because correct indentation can't be computed
from scratch for Fsharp code.  In general, deletes the whitespace before
point, inserts a newline, and takes an educated guess as to how you want
the new line indented."
  (interactive)
  (let ((ci (current-indentation)))
    (if (< ci (current-column))                 ; if point beyond indentation
        (newline-and-indent)
      ;; else try to act like newline-and-indent "normally" acts
      (beginning-of-line)
      (insert-char ?\n 1)
      (move-to-column ci))))


(defun fsharp-compute-indentation (honor-block-close-p)
  "Compute Fsharp indentation.
When HONOR-BLOCK-CLOSE-P is non-nil, statements such as `return',
`raise', `break', `continue', and `pass' force one level of
dedenting."
  (save-excursion
    (beginning-of-line)
    (let* ((bod (fsharp-point 'bod))
           (pps (parse-partial-sexp bod (point)))
           (boipps (parse-partial-sexp bod (fsharp-point 'boi)))
           (open-bracket-pos (fsharp-nesting-level)))

      (cond
       ((and open-bracket-pos (eq (and (looking-back "[[:space:]\n\r]+" nil t)
				       (match-beginning 0))
				  (1+ open-bracket-pos)))
	fsharp-indent-offset)
       ;; Continuation Lines
       ((fsharp-continuation-line-p)
        (if open-bracket-pos
            (fsharp--compute-indentation-open-bracket open-bracket-pos)
          (fsharp--compute-indentation-continuation-line)))

       ;; Previous line is a continuation line, use indentation of previous line
       ((fsharp--previous-line-continuation-line-p)
        (forward-line -1)
        (current-indentation))

       ((or
         ;; Beginning of Buffer; not on a continuation line
         (bobp)
         ;; "Indenting Comment"
         (fsharp--indenting-comment-p)) (current-indentation))

       ;; Final case includes things like pipe expressions (matches, left pipe)
       ;; and if/else blocks.
       ;;
       ;; else indentation based on that of the statement that
       ;; precedes us; use the first line of that statement to
       ;; establish the base, in case the user forced a non-std
       ;; indentation for the continuation lines (if any)
       (t (fsharp--compute-indentation-relative-to-previous honor-block-close-p))))))

(defun fsharp-guess-indent-offset (&optional global)
  "Guess a good value for, and change, `fsharp-indent-offset'.

By default, make a buffer-local copy of `fsharp-indent-offset' with the
new value, so that other Fsharp buffers are not affected.  With
\\[universal-argument] (programmatically, optional argument GLOBAL),
change the global value of `fsharp-indent-offset'.  This affects all
Fsharp buffers (that don't have their own buffer-local copy), both
those currently existing and those created later in the Emacs session.

Some people use a different value for `fsharp-indent-offset' than you use.
There's no excuse for such foolishness, but sometimes you have to deal
with their ugly code anyway.  This function examines the file and sets
`fsharp-indent-offset' to what it thinks it was when they created the
mess.

Specifically, it searches forward from the statement containing point,
looking for a line that opens a block of code.  `fsharp-indent-offset' is
set to the difference in indentation between that line and the Fsharp
statement following it.  If the search doesn't succeed going forward,
it's tried again going backward."
  (interactive "P")                     ; raw prefix arg
  (let (new-value
        (start (point))
        (restart (point))
        (found nil)
        colon-indent)
    (fsharp-goto-initial-line)
    (while (not (or found (eobp)))
      (when (and (re-search-forward fsharp-block-opening-re nil 'move)
                 (not (fsharp-in-literal-p restart)))
        (setq restart (point))
        (fsharp-goto-initial-line)
        (if (fsharp-statement-opens-block-p)
            (setq found t)
          (goto-char restart))))
    (unless found
      (goto-char start)
      (fsharp-goto-initial-line)
      (while (not (or found (bobp)))
        (setq found (and
                     (re-search-backward fsharp-block-opening-re nil 'move)
                     (or (fsharp-goto-initial-line) t) ; always true -- side effect
                     (fsharp-statement-opens-block-p)))))
    (setq colon-indent (current-indentation)
          found (and found (zerop (fsharp-next-statement 1)))
          new-value (- (current-indentation) colon-indent))
    (goto-char start)
    (if (not found)
        (message "Unable to determine default value for fsharp-indent-offset")
      (funcall (if global 'kill-local-variable 'make-local-variable)
               'fsharp-indent-offset)
      (setq fsharp-indent-offset new-value)
      (or noninteractive
          (message "%s value of fsharp-indent-offset set to %d"
                   (if global "Global" "Local")
                   fsharp-indent-offset)))))

(defun fsharp-comment-indent-function ()
  "Fsharp version of `comment-indent-function'."
  ;; This is required when filladapt is turned off.  Without it, when
  ;; filladapt is not used, comments which start in column zero
  ;; cascade one character to the right
  (save-excursion
    (beginning-of-line)
    (let ((eol (fsharp-point 'eol)))
      (and comment-start-skip
           (re-search-forward comment-start-skip eol t)
           (setq eol (match-beginning 0)))
      (goto-char eol)
      (skip-chars-backward " \t")
      (max comment-column (+ (current-column) (if (bolp) 0 1))))))

(defun fsharp-narrow-to-defun (&optional class)
  "Make text outside current defun invisible.
The defun visible is the one that contains point or follows point.
Optional CLASS is passed directly to `fsharp-beginning-of-def-or-class'."
  (interactive "P")
  (save-excursion
    (widen)
    (fsharp-end-of-def-or-class class)
    (let ((end (point)))
      (fsharp-beginning-of-def-or-class class)
      (narrow-to-region (point) end))))


(defun fsharp-shift-region (start end count)
  "Indent lines from START to END by COUNT spaces."
  (save-excursion
    (goto-char end)
    (beginning-of-line)
    (setq end (point))
    (goto-char start)
    (beginning-of-line)
    (setq start (point))
    (indent-rigidly start end count)))

(defun fsharp-shift-region-left (start end &optional count)
  "Shift region of Fsharp code to the left.
The lines from the line containing the start of the current region up
to (but not including) the line containing the end of the region are
shifted to the left, by `fsharp-indent-offset' columns.

If a prefix argument is given, the region is instead shifted by that
many columns.  With no active region, dedent only the current line.
You cannot dedent the region if any line is already at column zero."
  (interactive
   (let ((p (point))
         (m (mark))
         (arg current-prefix-arg))
     (if m
         (list (min p m) (max p m) arg)
       (list p (save-excursion (forward-line 1) (point)) arg))))
  ;; if any line is at column zero, don't shift the region
  (save-excursion
    (goto-char start)
    (while (< (point) end)
      (back-to-indentation)
      (if (and (zerop (current-column))
               (not (looking-at "\\s *$")))
          (error "Region is at left edge"))
      (forward-line 1)))
  (fsharp-shift-region start end (- (prefix-numeric-value
                                     (or count fsharp-indent-offset)))))


(defun fsharp-shift-region-right (start end &optional count)
  "Shift region of Fsharp code to the right.
The lines from the line containing the start of the current region up
to (but not including) the line containing the end of the region are
shifted to the right, by `fsharp-indent-offset' columns.

If a prefix argument is given, the region is instead shifted by that
many columns.  With no active region, indent only the current line."
  (interactive
   (let ((p (point))
         (m (mark))
         (arg current-prefix-arg))
     (if m
         (list (min p m) (max p m) arg)
       (list p (save-excursion (forward-line 1) (point)) arg))))
  (fsharp-shift-region start end (prefix-numeric-value
                                  (or count fsharp-indent-offset))))


(defun fsharp-indent-region (start end &optional indent-offset)
  "Reindent a region of Fsharp code.

The lines from the line containing the start of the current region up
to (but not including) the line containing the end of the region are
reindented.  If the first line of the region has a non-whitespace
character in the first column, the first line is left alone and the
rest of the region is reindented with respect to it.  Else the entire
region is reindented with respect to the (closest code or indenting
comment) statement immediately preceding the region.

This is useful when code blocks are moved or yanked, when enclosing
control structures are introduced or removed, or to reformat code
using a new value for the indentation offset.

If a numeric prefix argument is given, it will be used as the value of
the indentation offset.  Else the value of `fsharp-indent-offset' will be
used.

Warning: The region must be consistently indented before this function
is called!  This function does not compute proper indentation from
scratch (that's impossible in Fsharp), it merely adjusts the existing
indentation to be correct in context.

Warning: This function really has no idea what to do with
non-indenting comment lines, and shifts them as if they were indenting
comment lines.  Fixing this appears to require telepathy.

Special cases: whitespace is deleted from blank lines; continuation
lines are shifted by the same amount their initial line was shifted,
in order to preserve their relative indentation with respect to their
initial line; and comment lines beginning in column 1 are ignored."
  (interactive "*r\nP")                         ; region; raw prefix arg
  (save-excursion
    (goto-char end)   (beginning-of-line) (setq end (point-marker))
    (goto-char start) (beginning-of-line)
    (let ((fsharp-indent-offset (prefix-numeric-value
                                 (or indent-offset fsharp-indent-offset)))
          (indents '(-1))               ; stack of active indent levels
          (target-column 0)             ; column to which to indent
          (base-shifted-by 0)           ; amount last base line was shifted
          (indent-base (if (looking-at "[ \t\n]")
                           (fsharp-compute-indentation t)
                         0))
          ci)
      (while (< (point) end)
        (setq ci (current-indentation))
        ;; figure out appropriate target column
        (cond
         ((or (looking-at "//")         ; comment in column 1
              (looking-at "[ \t]*$"))   ; entirely blank
          (setq target-column 0))
         ((fsharp-continuation-line-p)  ; shift relative to base line
          (setq target-column (+ ci base-shifted-by)))
         (t                             ; new base line
          (if (> ci (car indents))      ; going deeper; push it
              (setq indents (cons ci indents))
            ;; else we should have seen this indent before
            (setq indents (memq ci indents)) ; pop deeper indents
            (if (null indents)
                (error "Bad indentation in region, at line %d"
                       (save-restriction
                         (widen)
                         (1+ (count-lines 1 (point)))))))
          (setq target-column (+ indent-base
                                 (* fsharp-indent-offset
                                    (- (length indents) 2))))
          (setq base-shifted-by (- target-column ci))))
        ;; shift as needed
        (if (/= ci target-column)
            (progn
              (delete-horizontal-space)
              (indent-to target-column)))
        (forward-line 1))))
  (set-marker end nil))


;;------------------------------ Motion and Mark ------------------------------;;

(defun fsharp-previous-statement (count)
  "Go to the start of the COUNTth preceding Fsharp statement.
By default, goes to the previous statement.  If there is no such
statement, goes to the first statement.  Return count of statements
left to move.  `Statements' do not include blank, comment, or
continuation lines."
  (interactive "p")                     ; numeric prefix arg
  (if (< count 0) (fsharp-next-statement (- count))
    (fsharp-goto-initial-line)
    (let (start)
      (while (and
              (setq start (point))      ; always true -- side effect
              (> count 0)
              (zerop (forward-line -1))
              (fsharp-goto-statement-at-or-above))
        (setq count (1- count)))
      (if (> count 0) (goto-char start)))
    count))

(defun fsharp-next-statement (count)
  "Go to the start of next Fsharp statement.
If the statement at point is the i'th Fsharp statement, goes to the
start of statement i+COUNT.  If there is no such statement, goes to the
last statement.  Returns count of statements left to move.  `Statements'
do not include blank, comment, or continuation lines."
  (interactive "p")                     ; numeric prefix arg
  (if (< count 0) (fsharp-previous-statement (- count))
    (beginning-of-line)
    (let (start)
      (while (and
              (setq start (point))      ; always true -- side effect
              (> count 0)
              (fsharp-goto-statement-below))
        (setq count (1- count)))
      (if (> count 0) (goto-char start)))
    count))

(defun fsharp-goto-block-up (&optional nomark)
  "Move up to start of current block.
Go to the statement that starts the smallest enclosing block; roughly
speaking, this will be the closest preceding statement that ends with a
colon and is indented less than the statement you started on.  If
successful, also sets the mark to the starting point.

`\\[fsharp-mark-block]' can be used afterward to mark the whole code
block, if desired.

If called from a program, the mark will not be set if optional argument
NOMARK is not nil."
  (interactive)
  (let ((start (point))
        (found nil)
        initial-indent)
    (fsharp-goto-initial-line)
    ;; if on and (mutually recursive bindings), blank or non-indenting comment line, use the preceding stmt
    (when (or (looking-at "[ \t]*\\($\\|//[^ \t\n]\\)")
              (looking-at-p "[ \t]*and[ \t]+"))
      (fsharp-goto-statement-at-or-above)
      (setq found (fsharp-statement-opens-block-p)))
    ;; search back for colon line indented less
    (setq initial-indent (current-indentation))
    (if (zerop initial-indent)
        ;; force fast exit
        (goto-char (point-min)))
    (while (not (or found (bobp)))
      (setq found
            (and
             (re-search-backward fsharp-block-opening-re nil 'move)
             (or (fsharp-goto-initial-line) t) ; always true -- side effect
             (< (current-indentation) initial-indent)
             (fsharp-statement-opens-block-p))))
    (if found
        (progn
          (or nomark (push-mark start))
          (back-to-indentation))
      (goto-char start)
      (error "Enclosing block not found"))))

;; The FIXME comment here is antique, and unexplained. My suspicion is that this
;; function was lifted from a Python mode (F# doesn't have the `def' keyword).
;; -- RMD 2019-10-20
;;FIXME
(defun fsharp-beginning-of-def-or-class (&optional class count)
  "Move point to start of `def' or `class'.

Searches back for the closest preceding `def'.  If you supply a prefix
arg, looks for a `class' instead.  The docs below assume the `def'
case; just substitute `class' for `def' for the other case.
Programmatically, if CLASS is `either', then moves to either `class'
or `def'.

When second optional argument is given programmatically, move to the
COUNTth start of `def'.

If point is in a `def' statement already, and after the `d', simply
moves point to the start of the statement.

Otherwise (i.e. when point is not in a `def' statement, or at or
before the `d' of a `def' statement), searches for the closest
preceding `def' statement, and leaves point at its start.  If no such
statement can be found, leaves point at the start of the buffer.

Returns t iff a `def' statement is found by these rules.

Note that doing this command repeatedly will take you closer to the
start of the buffer each time.

To mark the current `def', see `\\[fsharp-mark-def-or-class]'."
  (interactive "P")                     ; raw prefix arg
  (setq count (or count 1))
  (let ((at-or-before-p (<= (current-column) (current-indentation)))
        (start-of-line (goto-char (fsharp-point 'bol)))
        (start-of-stmt (goto-char (fsharp-point 'bos)))
        (start-re (cond ((eq class 'either) "^[ \t]*\\(type\\|let\\)\\>")
                        (class "^[ \t]*type\\>")
                        (t "^[ \t]*let\\>"))))
    ;; searching backward
    (if (and (< 0 count)
             (or (/= start-of-stmt start-of-line)
                 (not at-or-before-p)))
        (end-of-line))
    ;; search forward
    (if (and (> 0 count)
             (zerop (current-column))
             (looking-at start-re))
        (end-of-line))
    (if (re-search-backward start-re nil 'move count)
        (goto-char (match-beginning 0)))))

;; Backwards compatibility
(defalias 'beginning-of-fsharp-def-or-class 'fsharp-beginning-of-def-or-class)

(defun fsharp-end-of-def-or-class (&optional class count)
  "Move point beyond end of `def' or `class' body.

By default, looks for an appropriate `def'.  If you supply a prefix
arg, looks for a `class' instead.  The docs below assume the `def'
case; just substitute `class' for `def' for the other case.
Programmatically, if CLASS is `either', then moves to either `class'
or `def'.

When second optional argument is given programmatically, move to the
COUNTth end of `def'.

If point is in a `def' statement already, this is the `def' we use.

Else, if the `def' found by `\\[fsharp-beginning-of-def-or-class]'
contains the statement you started on, that's the `def' we use.

Otherwise, we search forward for the closest following `def', and use that.

If a `def' can be found by these rules, point is moved to the start of
the line immediately following the `def' block, and the position of the
start of the `def' is returned.

Else point is moved to the end of the buffer, and nil is returned.

Note that doing this command repeatedly will take you closer to the
end of the buffer each time.

To mark the current `def', see `\\[fsharp-mark-def-or-class]'."
  (interactive "P")                     ; raw prefix arg
  (if (and count (/= count 1))
      (fsharp-beginning-of-def-or-class (- 1 count)))
  (let ((start (progn (fsharp-goto-initial-line) (point)))
        (which (cond ((eq class 'either) "\\(type\\|let\\)")
                     (class "type")
                     (t "let")))
        (state 'not-found))
    ;; move point to start of appropriate def/class
    (if (looking-at (concat "[ \t]*" which "\\>")) ; already on one
        (setq state 'at-beginning)
      ;; else see if fsharp-beginning-of-def-or-class hits container
      (if (and (fsharp-beginning-of-def-or-class class)
               (progn (fsharp-goto-beyond-block)
                      (> (point) start)))
          (setq state 'at-end)
        ;; else search forward
        (goto-char start)
        (if (re-search-forward (concat "^[ \t]*" which "\\>") nil 'move)
            (progn (setq state 'at-beginning)
                   (beginning-of-line)))))
    (cond
     ((eq state 'at-beginning) (fsharp-goto-beyond-block) t)
     ((eq state 'at-end) t)
     ((eq state 'not-found) nil)
     (t (error "Internal error in `fsharp-end-of-def-or-class'")))))


;; Helper functions


;; TODO: we only return the parse state if we are *not* inside a string. This
;; doesn't make a lot of sense; checking for being inside a triple-quoted string
;; is a thing we frequently need to do. Need to figure out a reason and/or
;; abstract over the top of this.
(defun fsharp-parse-state ()
  "Return the parse state at point (see `parse-partial-sexp' docs)."
  (save-excursion
    (let ((here (point))
          pps done)
      (while (not done)
        ;; back up to the first preceding line (if any; else start of
        ;; buffer) that begins with a popular Fsharp keyword, or a
        ;; non- whitespace and non-comment character.  These are good
        ;; places to start parsing to see whether where we started is
        ;; at a non-zero nesting level.  It may be slow for people who
        ;; write huge code blocks or huge lists ... tough beans.
        (re-search-backward fsharp-parse-state-re nil 'move)
        (beginning-of-line)
        ;; In XEmacs, we have a much better way to test for whether
        ;; we're in a triple-quoted string or not.  Emacs does not
        ;; have this built-in function, which is its loss because
        ;; without scanning from the beginning of the buffer, there's
        ;; no accurate way to determine this otherwise.
        ;;
        ;; NOTE[@gastove|2019-10-21]: it is not at *all* clear what this comment is on
        ;; about. Emacs has all the functions used in this function.
        (save-excursion (setq pps (parse-partial-sexp (point) here)))
        ;; make sure we don't land inside a triple-quoted string
        (setq done (or (not (nth 3 pps))
                       (bobp)))
        ;; Just go ahead and short circuit the test back to the
        ;; beginning of the buffer.  This will be slow, but not
        ;; nearly as slow as looping through many
        ;; re-search-backwards.
        (if (not done)
            (goto-char (point-min))))
      pps)))

(defun fsharp-nesting-level ()
  "Return the buffer position of the opening character of the
current enclosing pair. If nesting level is zero, return nil.

At time of writing, enclosing pair can be [], {} or (), but not
quotes (single or triple) or <>. Note that registering []
implicitly also registers [||], though the pipes are ignored."
  (let ((status (fsharp-parse-state)))
    (if (zerop (car status))
        nil                             ; not in a nest
      (car (cdr status)))))             ; char of open bracket


;; NOTE[gastove|2019-10-25] this function baffles me. A triple-quoted string is,
;; definitionally, always delimited by *triple quotes*. I suspect this function
;; of being something more akin to, "go to beginning of opening of pair", or
;; just "go to delimiter."
(defun fsharp-goto-beginning-of-tqs (delim)
  "Go to the beginning of the triple quoted string we find ourselves in.
DELIM is the TQS string delimiter character we're searching backwards
for."
  (let ((skip (and delim (make-string 1 delim)))
        (continue t))
    (when skip
      (save-excursion
        (while continue
          (search-backward skip nil t)
          (setq continue (and (not (bobp))
                              (= (char-before) ?\\))))
        (if (and (= (char-before) delim)
                 (= (char-before (1- (point))) delim))
            (setq skip (make-string 3 delim))))
      ;; we're looking at a triple-quoted string
      (search-backward skip nil t))))


(defun fsharp-goto-initial-line ()
  "Go to the initial line of the current statement.
Usually this is the line we're on, but if we're on the 2nd or
following lines of a continuation block, we need to go up to the first
line of the block."
  ;; Tricky: We want to avoid quadratic-time behavior for long
  ;; continued blocks, whether of the backslash or open-bracket
  ;; varieties, or a mix of the two.  The following manages to do that
  ;; in the usual cases.
  ;;
  ;; Also, if we're sitting inside a triple quoted string, this will
  ;; drop us at the line that begins the string.
  (let (open-bracket-pos)
    (while (fsharp-continuation-line-p)
      (beginning-of-line)
      (if (fsharp--hanging-operator-continuation-line-p)
          (while (fsharp--hanging-operator-continuation-line-p)
            (forward-line -1))
        ;; else zip out of nested brackets/braces/parens
        (while (setq open-bracket-pos (fsharp-nesting-level))
          (goto-char open-bracket-pos)))))
  (beginning-of-line))

;; TODO[gastove|2019-10-31] This is completely broken. I'm not totally sure why
;; or how, but it simply doesn't do the thing it says on the tin.
(defun fsharp-goto-beyond-final-line ()
  "Go to the point just beyond the final line of the current expression.
Usually this is the start of the next line, but if this is a
multi-line expression we need to skip over the continuation
lines."
  ;; TODO[gastove|2019-10-30] This works on triple-quoted strings that start on
  ;; their own line, but not if they are opened on the same line as a let.
  (if (looking-at (concat "[ \t]*\\(" fsharp-stringlit-re "\\)"))
      (goto-char (match-end 0)))
  ;;
  (forward-line 1)
  (let (state)
    ;; I think this first predicate is the problem -- "continuation lines", as
    ;; defined by that function, are only lines with hanging arithmetic
    ;; operators *or* lines inside certain pairs (things like data structures
    ;; and computation expressions). This fully doesn't account for
    ;; continuations using pipes.
    (while (and (fsharp-continuation-line-p)
                (not (eobp)))
      ;; skip over hanging operator lines
      (while (and (fsharp--hanging-operator-continuation-line-p)
                  (not (eobp)))
        (forward-line 1))
      ;; if in nest, zip to the end of the nest
      (setq state (fsharp-parse-state))
      (when (and (not (zerop (car state)))
                 (not (eobp)))
        (progn
          (parse-partial-sexp (point) (point-max) 0 nil state)
          (forward-line 1))))))


(defun fsharp-goto-beyond-block ()
  "Go to point just beyond the final line of block begun by the current line.
This is the same as where `fsharp-goto-beyond-final-line' goes unless
we're on colon line, in which case we go to the end of the block.
Assumes point is at the beginning of the line."
  (if (fsharp-statement-opens-block-p)
      (fsharp-mark-block nil 'just-move)
    (fsharp-goto-beyond-final-line)))


(defun fsharp-goto-statement-at-or-above ()
  "Go to the start of the first statement at or preceding point.
Return t if there is such a statement, otherwise nil.  `Statement'
does not include blank lines, comments, or continuation lines."
  (fsharp-goto-initial-line)
  (if (looking-at fsharp-blank-or-comment-re)
      ;; skip back over blank & comment lines
      ;; note:  will skip a blank or comment line that happens to be
      ;; a continuation line too
      (if (re-search-backward "^[ \t]*\\([^ \t\n]\\|//\\)" nil t)
          (progn (fsharp-goto-initial-line) t)
        nil)
    t))

(defun fsharp-goto-statement-below ()
  "Go to start of the first statement following the statement containing point.
Return t if there is such a statement, otherwise nil.  `Statement'
does not include blank lines, comments, or continuation lines."
  (beginning-of-line)
  (let ((start (point)))
    (fsharp-goto-beyond-final-line)
    (while (and
            (or (looking-at fsharp-blank-or-comment-re)
                (fsharp-in-literal-p))
            (not (eobp)))
      (forward-line 1))
    (if (eobp)
        (progn (goto-char start) nil)
      t)))

(defun fsharp-go-up-tree-to-keyword (key)
  "Go to begining of statement starting with KEY, at or preceding point.

KEY is a regular expression describing a Fsharp keyword.  Skip blank
lines and non-indenting comments.  If the statement found starts with
KEY, then stop, otherwise go back to first enclosing block starting
with KEY.  If successful, leave point at the start of the KEY line and
return t.  Otherwise, leave point at an undefined place and return nil."
  ;; skip blanks and non-indenting //
  (fsharp-goto-initial-line)
  (while (and
          (looking-at "[ \t]*\\($\\|//[^ \t\n]\\)")
          (zerop (forward-line -1)))    ; go back
    nil)
  (fsharp-goto-initial-line)
  (let* ((re (concat "[ \t]*" key "\\>"))
         (case-fold-search nil)                 ; let* so looking-at sees this
         (found (looking-at re))
         (dead nil))
    (while (not (or found dead))
      (condition-case nil               ; in case no enclosing block
          (fsharp-goto-block-up 'no-mark)
        (error (setq dead t)))
      (or dead (setq found (looking-at re))))
    (beginning-of-line)
    found))


(defun fsharp-suck-up-leading-text ()
  "Return string in buffer from start of indentation to end of line.
Prefix with \"...\" if leading whitespace was skipped."
  (save-excursion
    (back-to-indentation)
    (concat
     (if (bolp) "" "...")
     (buffer-substring (point) (progn (end-of-line) (point))))))


(defun fsharp-suck-up-first-keyword ()
  "Return first keyword on the line as a Lisp symbol.
`Keyword' is defined (essentially) as the regular expression
([a-z]+).  Returns nil if none was found."
  (let ((case-fold-search nil))
    (if (looking-at "[ \t]*\\([a-z]+\\)\\>")
        (intern (buffer-substring (match-beginning 1) (match-end 1)))
      nil)))

(defun fsharp-current-defun ()
  "Fsharp value for `add-log-current-defun-function'.
This tells add-log.el how to find the current function/method/variable."
  (save-excursion

    ;; Move back to start of the current statement.

    (fsharp-goto-initial-line)
    (back-to-indentation)
    (while (and (or (looking-at fsharp-blank-or-comment-re)
                    (fsharp-in-literal-p))
                (not (eq (point-at-bol) (point-min))))
      (backward-to-indentation 1))
    (fsharp-goto-initial-line)

    (let ((scopes "")
          (sep "")
          dead assignment)

      ;; Check for an assignment.  If this assignment exists inside a
      ;; def, it will be overwritten inside the while loop.  If it
      ;; exists at top lever or inside a class, it will be preserved.

      (when (looking-at "[ \t]*\\([a-zA-Z0-9_]+\\)[ \t]*=")
        (setq scopes (buffer-substring (match-beginning 1) (match-end 1)))
        (setq assignment t)
        (setq sep "."))

      ;; Prepend the name of each outer socpe (def or class).

      (while (not dead)
        (if (and (fsharp-go-up-tree-to-keyword "\\(class\\|def\\)")
                 (looking-at
                  "[ \t]*\\(class\\|def\\)[ \t]*\\([a-zA-Z0-9_]+\\)[ \t]*"))
            (let ((name (buffer-substring (match-beginning 2) (match-end 2))))
              (if (and assignment (looking-at "[ \t]*def"))
                  (setq scopes name)
                (setq scopes (concat name sep scopes))
                (setq sep "."))))
        (setq assignment nil)
        (condition-case nil             ; Terminate nicely at top level.
            (fsharp-goto-block-up 'no-mark)
          (error (setq dead t))))
      (if (string= scopes "")
          nil
        scopes))))


(defun fsharp-beginning-of-block ()
  "Move point to the beginning of the current top-level block"
  (interactive)
  (let ((prev (point)))
    (condition-case nil
        (while (progn (fsharp-goto-block-up 'no-mark)
                      (< (point) prev))
          (setq prev (point)))
      (error (while (fsharp-continuation-line-p)
               (forward-line -1)))))
  (beginning-of-line))


(defun fsharp-end-of-block ()
  "Move point to the end of the current top-level block"
  (interactive)
  (forward-line 1)
  (if (not (eobp))
      (progn
        (beginning-of-line)
        (condition-case nil
            (progn (re-search-forward "^[a-zA-Z#0-9([]")
                   (while (fsharp-continuation-line-p)
                     (forward-line 1))
                   (forward-line -1))
          (error
           (progn (goto-char (point-max)))))
        (end-of-line)
        (when (looking-at-p "\n[ \t]*and[ \t]+")
          (forward-line 1)
          (fsharp-end-of-block)))
    (goto-char (point-max))))


(defun fsharp-mark-phrase ()
  "Mark current phrase"
  (interactive)
  (fsharp-beginning-of-block)
  (push-mark (point))
  (fsharp-end-of-block)
  (exchange-point-and-mark))


(defun fsharp-mark-block (&optional extend just-move)
  "Mark following block of lines.  With prefix arg, mark structure.
Easier to use than explain.  It sets the region to an `interesting'
block of succeeding lines.  If point is on a blank line, it goes down to
the next non-blank line.  That will be the start of the region.  The end
of the region depends on the kind of line at the start:

 - If a comment, the region will include all succeeding comment lines up
   to (but not including) the next non-comment line (if any).

 - Else if a prefix arg is given, and the line begins one of these
   structures:

     if elif else try except finally for while def class

   the region will be set to the body of the structure, including
   following blocks that `belong' to it, but excluding trailing blank
   and comment lines.  E.g., if on a `try' statement, the `try' block
   and all (if any) of the following `except' and `finally' blocks
   that belong to the `try' structure will be in the region.  Ditto
   for if/elif/else, for/else and while/else structures, and (a bit
   degenerate, since they're always one-block structures) def and
   class blocks.

 - Else if no prefix argument is given, and the line begins a Fsharp
   block (see list above), and the block is not a `one-liner' (i.e.,
   the statement ends with a colon, not with code), the region will
   include all succeeding lines up to (but not including) the next
   code statement (if any) that's indented no more than the starting
   line, except that trailing blank and comment lines are excluded.
   E.g., if the starting line begins a multi-statement `def'
   structure, the region will be set to the full function definition,
   but without any trailing `noise' lines.

 - Else the region will include all succeeding lines up to (but not
   including) the next blank line, or code or indenting-comment line
   indented strictly less than the starting line.  Trailing indenting
   comment lines are included in this case, but not trailing blank
   lines.

A msg identifying the location of the mark is displayed in the echo
area; or do `\\[exchange-point-and-mark]' to flip down to the end.

If called from a program, optional argument EXTEND plays the role of
the prefix arg, and if optional argument JUST-MOVE is not nil, just
moves to the end of the block (& does not set mark or display a msg)."
  (interactive "P")                     ; raw prefix arg
  (fsharp-goto-initial-line)
  ;; skip over blank lines
  (while (and
          (looking-at "[ \t]*$")        ; while blank line
          (not (eobp)))                         ; & somewhere to go
    (forward-line 1))
  (if (eobp)
      (error "Hit end of buffer without finding a non-blank stmt"))
  (let ((initial-pos (point))
        (initial-indent (current-indentation))
        last-pos                        ; position of last stmt in region
        (followers
         '((if elif else) (elif elif else) (else)
           (try except finally) (except except) (finally)
           (for else) (while else)
           (def) (class) ) )
        first-symbol next-symbol)

    (cond
     ;; if comment line, suck up the following comment lines
     ((looking-at "[ \t]*//")
      (re-search-forward "^[ \t]*\\([^ \t]\\|//\\)" nil 'move) ; look for non-comment
      (re-search-backward "^[ \t]*//")  ; and back to last comment in block
      (setq last-pos (point)))

     ;; else if line is a block line and EXTEND given, suck up
     ;; the whole structure
     ((and extend
           (setq first-symbol (fsharp-suck-up-first-keyword) )
           (assq first-symbol followers))
      (while (and
              (or (fsharp-goto-beyond-block) t) ; side effect
              (forward-line -1)                 ; side effect
              (setq last-pos (point))   ; side effect
              (fsharp-goto-statement-below)
              (= (current-indentation) initial-indent)
              (setq next-symbol (fsharp-suck-up-first-keyword))
              (memq next-symbol (cdr (assq first-symbol followers))))
        (setq first-symbol next-symbol)))

     ;; else if line *opens* a block, search for next stmt indented <=
     ((fsharp-statement-opens-block-p)
      (while (and
              (setq last-pos (point))   ; always true -- side effect
              (fsharp-goto-statement-below)
              (> (current-indentation) initial-indent))))

     ;; else plain code line; stop at next blank line, or stmt or
     ;; indenting comment line indented <
     (t
      (while (and
              (setq last-pos (point))   ; always true -- side effect
              (or (fsharp-goto-beyond-final-line) t)
              (not (looking-at "[ \t]*$")) ; stop at blank line
              (or
               (>= (current-indentation) initial-indent)
               (looking-at "[ \t]*//[^ \t\n]"))) ; ignore non-indenting //
        nil)))

    ;; skip to end of last stmt
    (goto-char last-pos)
    (fsharp-goto-beyond-final-line)

    ;; set mark & display
    (if just-move
        ()                              ; just return
      (push-mark (point) 'no-msg)
      (forward-line -1)
      (message "Mark set after: %s" (fsharp-suck-up-leading-text))
      (goto-char initial-pos))))

(defun fsharp-mark-def-or-class (&optional class)
  "Set region to body of def (or class, with prefix arg) enclosing point.
Pushes the current mark, then point, on the mark ring (all language
modes do this, but although it's handy it's never documented ...).

In most Emacs language modes, this function bears at least a
hallucinogenic resemblance to `\\[fsharp-end-of-def-or-class]' and
`\\[fsharp-beginning-of-def-or-class]'.

And in earlier versions of Fsharp mode, all 3 were tightly connected.
Turned out that was more confusing than useful: the `goto start' and
`goto end' commands are usually used to search through a file, and
people expect them to act a lot like `search backward' and `search
forward' string-search commands.  But because Fsharp `def' and `class'
can nest to arbitrary levels, finding the smallest def containing
point cannot be done via a simple backward search: the def containing
point may not be the closest preceding def, or even the closest
preceding def that's indented less.  The fancy algorithm required is
appropriate for the usual uses of this `mark' command, but not for the
`goto' variations.

So the def marked by this command may not be the one either of the
`goto' commands find: If point is on a blank or non-indenting comment
line, moves back to start of the closest preceding code statement or
indenting comment line.  If this is a `def' statement, that's the def
we use.  Else searches for the smallest enclosing `def' block and uses
that.  Else signals an error.

When an enclosing def is found: The mark is left immediately beyond
the last line of the def block.  Point is left at the start of the
def, except that: if the def is preceded by a number of comment lines
followed by (at most) one optional blank line, point is left at the
start of the comments; else if the def is preceded by a blank line,
point is left at its start.

The intent is to mark the containing def/class and its associated
documentation, to make moving and duplicating functions and classes
pleasant."
  (interactive "P")                     ; raw prefix arg
  (let ((start (point))
        (which (cond ((eq class 'either) "\\(type\\|let\\)")
                     (class "type")
                     (t "let"))))
    (push-mark start)
    (if (not (fsharp-go-up-tree-to-keyword which))
        (progn (goto-char start)
               (error "Enclosing %s not found"
                      (if (eq class 'either)
                          "def or class"
                        which)))
      ;; else enclosing def/class found
      (setq start (point))
      (fsharp-goto-beyond-block)
      (push-mark (point))
      (goto-char start)
      (if (zerop (forward-line -1))     ; if there is a preceding line
          (progn
            (if (looking-at "[ \t]*$")  ; it's blank
                (setq start (point))    ; so reset start point
              (goto-char start))        ; else try again
            (if (zerop (forward-line -1))
                (if (looking-at "[ \t]*//") ; a comment
                    ;; look back for non-comment line
                    ;; tricky: note that the regexp matches a blank
                    ;; line, cuz \n is in the 2nd character class
                    (and
                     (re-search-backward "^[ \t]*\\([^ \t]\\|//\\)" nil 'move)
                     (forward-line 1))
                  ;; no comment, so go back
                  (goto-char start)))))))
  (exchange-point-and-mark))


;;------------------------------- SMIE Configs -------------------------------;;

(defconst fsharp-smie-grammar
  ;; SMIE grammar follow the refernce of SML-mode.
  (smie-prec2->grammar
   (smie-merge-prec2s
    (smie-bnf->prec2
     '((id)
       (expr ("while" expr "do" expr)
             ("if" expr "then" expr "else" expr)
             ("for" expr "in" expr "do" expr)
             ("for" expr "to" expr "do" expr)
             ("try" expr "with" branches)
             ("try" expr "finally" expr)
             ("match" expr "with" branches)
             ("type" expr "=" branches)
             ("begin" exprs "end")
             ("[" exprs "]")
             ("[|" exprs "|]")
             ("{" exprs "}")
             ("<@" exprs "@>")
             ("<@@" exprs "@@>")
             ("let" sexp "=" expr)
             ("fun" expr "->" expr))
       (sexp ("rec")
             (sexp ":" type)
             (sexp "||" sexp)
             (sexp "&&" sexp)
             ("(" exprs ")"))
       (exprs (exprs ";" exprs)
              (exprs "," exprs)
              (expr))
       (type (type "->" type)
             (type "*" type))
       (branches (branches "|" branches))
       (decls (sexp "=" expr))
       (toplevel (decls)
                 (expr)
                 (toplevel ";;" toplevel)))
     '((assoc "|"))
     '((assoc "->") (assoc "*"))
     '((assoc "let" "fun" "type" "open" "->"))
     '((assoc "let") (assoc "="))
     '((assoc "[" "]" "[|" "|]" "{" "}"))
     '((assoc "<@" "@>"))
     '((assoc "<@@" "@@>"))
     '((assoc "&&") (assoc "||") (noassoc ":"))
     '((assoc ";") (assoc ","))
     '((assoc ";;")))
    (smie-precs->prec2
     '((nonassoc (">" ">=" "<>" "<" "<=" "="))
       (assoc "::")
       (assoc "+" "-" "^")
       (assoc "/" "*" "%")))))
  )

(defun fsharp-smie-rules (kind token)
  (pcase (cons kind token)
    (`(:elem . basic) fsharp-indent-offset)
    (`(:after . "do") fsharp-indent-offset)
    (`(:after . "then") fsharp-indent-offset)
    (`(:after . "else") fsharp-indent-offset)
    (`(:after . "try") fsharp-indent-offset)
    (`(:after . "with") fsharp-indent-offset)
    (`(:after . "finally") fsharp-indent-offset)
    (`(:after . "in") 0)
    (`(:after . ,(or `"[" `"]" `"[|" `"|]")) fsharp-indent-offset)
    (`(,_ . ,(or `";" `",")) (if (smie-rule-parent-p "begin")
                                 0
                               (smie-rule-separator kind)))
    (`(:after . "=") fsharp-indent-offset)
    (`(:after . ";;") (smie-rule-separator kind))
    (`(:before . ";;") (if (smie-rule-bolp)
                           0))
    ))


(defun fsharp-mode-indent-smie-setup ()
  (smie-setup fsharp-smie-grammar #'fsharp-smie-rules))


(provide 'fsharp-mode-structure)
;;; fsharp-mode-structure.el ends here


================================================
FILE: fsharp-mode-util.el
================================================
;;; fsharp-mode-util.el --- utility functions -*- lexical-binding: t -*-

;; Copyright (C) 2015 Robin Neatherway

;; Author: 2015 Robin Neatherway <robin.neatherway@gmail.com>
;; Maintainer: Robin Neatherway <robin.neatherway@gmail.com>
;; Keywords: languages

;; This file is not part of GNU Emacs.

;; This file is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 3, or (at your option)
;; any later version.

;; This file is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING.  If not, write to
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.

(require 'cl-lib)

(defvar fsharp-ac-using-mono
  (cl-case system-type
    ((windows-nt cygwin msdos) nil)
    (otherwise t))
  "Whether the .NET runtime in use is mono.
Defaults to nil for Microsoft platforms (including Cygwin), t
for all *nix.")

(defun fsharp-mode--program-files-x86 ()
  (file-name-as-directory
   (or (getenv "ProgramFiles(x86)")
       (getenv "ProgramFiles")
       "C:\\Program Files (x86)")))

(defun fsharp-mode--vs2017-msbuild-find (exe)
  "Return EXE absolute path for Visual Studio 2017, if existent, else nil."
  (let ((candidates (mapcar (lambda (edition)
                              (concat (fsharp-mode--program-files-x86)
                                      edition
                                      "msbuild/15.0/bin/"
                                      exe))
                            '("Enterprise/" "Professional/"
                              "Community/" "BuildTools/"))))
    (cl-find-if (lambda (exe) (file-executable-p exe)) candidates)))

(defun fsharp-mode--msbuild-find (exe)
  (if fsharp-ac-using-mono
      (executable-find exe)
    (let* ((searchdirs (mapcar (lambda (ver)
                                 (concat (fsharp-mode--program-files-x86)
                                         "MSBuild/" ver "/Bin"))
                               '("14.0" "13.0" "12.0")))
           (exec-path (append searchdirs exec-path)))
      (or (fsharp-mode--vs2017-msbuild-find exe) (executable-find exe)))))

(defun fsharp-mode--executable-find (exe)
  (if fsharp-ac-using-mono
      (executable-find exe)
    (let* ((searchdirs (mapcar (lambda (ver)
                                 (concat (fsharp-mode--program-files-x86)
                                         "Microsoft SDKs/F#/"
                                         ver "/Framework/v4.0"))
                               '("10.1" "4.0" "3.1" "3.0")))
           (exec-path (append searchdirs exec-path)))
      (executable-find exe))))

(provide 'fsharp-mode-util)

;;; fsharp-mode-util.el ends here


================================================
FILE: fsharp-mode.el
================================================
;;; fsharp-mode.el --- Support for the F# programming language

;; Copyright (C) 1997 INRIA

;; Author: 1993-1997 Xavier Leroy, Jacques Garrigue and Ian T Zimmerman
;;         2010-2011 Laurent Le Brun <laurent@le-brun.eu>
;;         2012-2014 Robin Neatherway <robin.neatherway@gmail.com>
;;         2017-2023 Jürgen Hötzel
;; Maintainer: Jürgen Hötzel
;; Package-Requires: ((emacs "25"))
;; Keywords: languages
;; Version: 1.11-snapshot

;; This file is not part of GNU Emacs.

;; This file is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 3, or (at your option)
;; any later version.

;; This file is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING.  If not, write to
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.

;;; Code:

(require 'fsharp-mode-structure)
(require 'inf-fsharp-mode)
(require 'fsharp-mode-util)
(require 'compile)
(require 'project)
(require 'subr-x)
(require 'seq)

(defgroup fsharp nil
  "Support for the Fsharp programming language, <http://www.fsharp.net/>"
  :group 'languages
  :prefix "fsharp-")

;;; Compilation

(defvar fsharp-compile-command
  (seq-some #'fsharp-mode--executable-find '("fsharpc" "fsc"))
  "The program used to compile F# source files.")

(defvar fsharp-build-command
  (seq-some #'fsharp-mode--msbuild-find '("msbuild" "xbuild"))
  "The command used to build F# projects and solutions.")

;;; ----------------------------------------------------------------------------

(defvar fsharp-shell-active nil
  "Non nil when a subshell is running.")

(defvar running-xemacs  (string-match "XEmacs" emacs-version)
  "Non-nil if we are running in the XEmacs environment.")

(defvar fsharp-mode-map nil
  "Keymap used in fsharp mode.")

(unless fsharp-mode-map
  (setq fsharp-mode-map (make-sparse-keymap))
  (if running-xemacs
      (define-key fsharp-mode-map 'backspace 'backward-delete-char-untabify)
    (define-key fsharp-mode-map "\177" 'backward-delete-char-untabify))

  ;; F# bindings
  (define-key fsharp-mode-map "\C-c\C-a" 'fsharp-find-alternate-file)
  (define-key fsharp-mode-map "\C-c\C-c" 'compile)
  (define-key fsharp-mode-map "\M-\C-x" 'fsharp-eval-phrase)
  (define-key fsharp-mode-map "\C-c\C-e" 'fsharp-eval-phrase)
  (define-key fsharp-mode-map "\C-x\C-e" 'fsharp-eval-phrase)
  (define-key fsharp-mode-map "\C-c\C-r" 'fsharp-eval-region)
  (define-key fsharp-mode-map "\C-c\C-f" 'fsharp-load-buffer-file)
  (define-key fsharp-mode-map "\C-c\C-s" 'fsharp-show-subshell)
  (define-key fsharp-mode-map "\M-\C-h" 'fsharp-mark-phrase)

  (define-key fsharp-mode-map (kbd "M-n") 'next-error)
  (define-key fsharp-mode-map (kbd "M-p") 'previous-error)

  (define-key fsharp-mode-map "\C-c<" 'fsharp-shift-region-left)
  (define-key fsharp-mode-map "\C-c>" 'fsharp-shift-region-right)

  (define-key fsharp-mode-map "\C-m"      'fsharp-newline-and-indent)
  (define-key fsharp-mode-map "\C-c:"     'fsharp-guess-indent-offset)

  (define-key fsharp-mode-map (kbd "C-c <up>") 'fsharp-goto-block-up)

  (unless running-xemacs
    (let ((map (make-sparse-keymap "fsharp"))
          (forms (make-sparse-keymap "Forms")))
      (define-key fsharp-mode-map [menu-bar] (make-sparse-keymap))
      (define-key fsharp-mode-map [menu-bar fsharp] (cons "F#" map))

      (define-key map [goto-block-up] '("Goto block up" . fsharp-goto-block-up))
      (define-key map [mark-phrase] '("Mark phrase" . fsharp-mark-phrase))
      (define-key map [shift-left] '("Shift region to right" . fsharp-shift-region-right))
      (define-key map [shift-right] '("Shift region to left" . fsharp-shift-region-left))
      (define-key map [separator-2] '("---"))

      ;; others
      (define-key map [compile] '("Compile..." . compile))
      (define-key map [switch-view] '("Switch view" . fsharp-find-alternate-file))
      (define-key map [separator-1] '("--"))
      (define-key map [show-subshell] '("Show subshell" . fsharp-show-subshell))
      (define-key map [eval-region] '("Eval region" . fsharp-eval-region))
      (define-key map [eval-phrase] '("Eval phrase" . fsharp-eval-phrase)))))

;;;###autoload
(progn
  (add-to-list 'auto-mode-alist '("\\.fs[iylx]?\\'" . fsharp-mode))
  (add-to-list 'auto-mode-alist '("\\.fsproj\\'" . nxml-mode)))

(defvar fsharp-mode-syntax-table nil
  "Syntax table in use in fsharp mode buffers.")
(unless fsharp-mode-syntax-table
  (setq fsharp-mode-syntax-table (make-syntax-table))
                                        ; backslash is an escape sequence
  (modify-syntax-entry ?\\ "\\" fsharp-mode-syntax-table)

                                        ; ( is first character of comment start
  (modify-syntax-entry ?\( "()1n" fsharp-mode-syntax-table)
                                        ; * is second character of comment start,
                                        ; and first character of comment end
  (modify-syntax-entry ?*  ". 23n" fsharp-mode-syntax-table)
                                        ; ) is last character of comment end
  (modify-syntax-entry ?\) ")(4n" fsharp-mode-syntax-table)

                                        ; // is the beginning of a comment "b"
  (modify-syntax-entry ?/ ". 12b" fsharp-mode-syntax-table)
                                        ; // \n is the end of a comment "b"
  (modify-syntax-entry ?\n "> b" fsharp-mode-syntax-table)

                                        ; quote and underscore are part of symbols
                                        ; so are # and ! as they can form part of types/preprocessor
                                        ; directives and also keywords
  (modify-syntax-entry ?' "_" fsharp-mode-syntax-table)
  (modify-syntax-entry ?_ "_" fsharp-mode-syntax-table)
  (modify-syntax-entry ?# "_" fsharp-mode-syntax-table)
  (modify-syntax-entry ?! "_" fsharp-mode-syntax-table)

                                        ; ISO-latin accented letters and EUC kanjis are part of words
  (let ((i 160))
    (while (< i 256)
      (modify-syntax-entry i "w" fsharp-mode-syntax-table)
      (setq i (1+ i)))))

;; Other internal variables

(defvar fsharp-last-noncomment-pos nil
  "Caches last buffer position determined not inside a fsharp comment.")
(make-variable-buffer-local 'fsharp-last-noncomment-pos)

;; last-noncomment-pos can be a simple position, because we nil it
;; anyway whenever buffer changes upstream. last-comment-start and -end
;; have to be markers, because we preserve them when the changes' end
;; doesn't overlap with the comment's start.

(defvar fsharp-last-comment-start nil
  "A marker caching last determined fsharp comment start.")
(make-variable-buffer-local 'fsharp-last-comment-start)

(defvar fsharp-last-comment-end nil
  "A marker caching last determined fsharp comment end.")
(make-variable-buffer-local 'fsharp-last-comment-end)

(defvar fsharp-mode-hook nil
  "Hook for fsharp-mode")

(defcustom fsharp-autosave-on-file-load nil
  "Determine if buffer should be automatically saved on
`fsharp-load-buffer-file'.
If set to t, the buffer will always be saved, silently."
  :type 'boolean
  :group 'fsharp-mode)

;;;###autoload
(define-derived-mode fsharp-mode prog-mode "fsharp"
  :syntax-table fsharp-mode-syntax-table
  "Major mode for editing fsharp code.

\\{fsharp-mode-map}"

  (require 'fsharp-mode-font)

  (fsharp-mode-indent-smie-setup)

  (use-local-map fsharp-mode-map)

  (mapc 'make-local-variable
        '(paragraph-start
          require-final-newline
          paragraph-separate
          paragraph-ignore-fill-prefix
          comment-start
          comment-end
          comment-column
          comment-start-skip
          comment-indent-function
          adaptive-fill-regexp
          parse-sexp-ignore-comments
          indent-region-function
          indent-line-function
          add-log-current-defun-function
          underline-minimum-offset
          compile-command
          syntax-propertize-function))

  (setq local-abbrev-table       fsharp-mode-abbrev-table
        paragraph-start          (concat "^$\\|" page-delimiter)
        paragraph-separate       paragraph-start
        require-final-newline    'visit-save
        indent-tabs-mode         nil
        comment-start            "//"
        comment-end              ""
        comment-column           40
        comment-start-skip       "///* *"
        adaptive-fill-regexp     "[ \t]*\\(//+[ \t]*\\)*"
        comment-indent-function  'fsharp-comment-indent-function
        indent-region-function   'fsharp-indent-region
        indent-line-function     'fsharp-indent-line
        underline-minimum-offset  4

        paragraph-ignore-fill-prefix   t
        add-log-current-defun-function 'fsharp-current-defun
        fsharp-last-noncomment-pos     nil
        fsharp-last-comment-start      (make-marker)
        fsharp-last-comment-end        (make-marker))

                                        ; Syntax highlighting
  (setq font-lock-defaults '(fsharp-font-lock-keywords))
  (setq syntax-propertize-function 'fsharp--syntax-propertize-function)
                                        ; Some reasonable defaults for company mode
  ;; In Emacs 24.4 onwards, tell electric-indent-mode that fsharp-mode
  ;; has no deterministic indentation.
  (when (boundp 'electric-indent-inhibit) (setq electric-indent-inhibit t))

  (when-let ((file (buffer-file-name)))
    (setq compile-command (fsharp-mode-choose-compile-command file))))

(defun fsharp-mode-choose-compile-command (file)
  "Format an appropriate compilation command, depending on several factors:
1. The presence of a makefile
2. The presence of a .sln or .fsproj
3. The file's type.
"
  (let* ((fname    (file-name-nondirectory file))
         (dname    (file-name-directory file))
         (ext      (file-name-extension file))
         (proj     (fsharp-mode/find-sln-or-fsproj file))
         (makefile (or (file-exists-p (concat dname "/Makefile"))
                       (file-exists-p (concat dname "/makefile")))))
    (cond
     (makefile          compile-command)
     ((and fsharp-build-command proj) (combine-and-quote-strings `(,fsharp-build-command "/nologo" ,proj)))
     ((and fsharp-compile-command (member ext '("fs" "fsx"))) (combine-and-quote-strings `(,fsharp-compile-command "--nologo" ,file)))
     ((equal ext "fsl") (combine-and-quote-strings (list "fslex" file)))
     ((equal ext "fsy") (combine-and-quote-strings (list "fsyacc" file)))
     (t                 compile-command))))

(defun fsharp-find-alternate-file ()
  (interactive)
  (let ((name (buffer-file-name)))
    (if (string-match "^\\(.*\\)\\.\\(fs\\|fsi\\)$" name)
        (find-file
         (concat
          (fsharp-match-string 1 name)
          (if (string= "fs" (fsharp-match-string 2 name)) ".fsi" ".fs"))))))

;;; Subshell support

(defun fsharp-eval-region (start end)
  "Send the current region to the inferior fsharp process."
  (interactive"r")
  (require 'inf-fsharp-mode)
  (inferior-fsharp-eval-region start end))

(defun fsharp-eval-phrase ()
  "Send current phrase to the interactive mode"
  (interactive)
  (save-excursion
    (let ((p1) (p2))
      (fsharp-beginning-of-block)
      (setq p1 (point))
      (fsharp-end-of-block)
      (setq p2 (point))
      (fsharp-eval-region p1 p2))))

(defun fsharp-load-buffer-file ()
  "Load the filename corresponding to the present buffer in F# with #load"
  (interactive)
  (require 'inf-fsharp-mode)
  (let* ((name buffer-file-name)
         (command (concat "#load \"" name "\"")))
    (when (and (buffer-modified-p)
	       (or fsharp-autosave-on-file-load
		   (y-or-n-p (concat "Do you want to save \"" name
				     "\" before loading it? "))))
      (save-buffer))
    (fsharp-run-process-if-needed)
    (fsharp-simple-send inferior-fsharp-buffer-name command)))

(defun fsharp-show-subshell ()
  (interactive)
  (require 'inf-fsharp-mode)
  (inferior-fsharp-show-subshell))

(defconst fsharp-error-regexp-fs
  "^\\([^(\n]+\\)(\\([0-9]+\\),\\([0-9]+\\)):"
  "Regular expression matching the error messages produced by fsc.")

(if (boundp 'compilation-error-regexp-alist)
    (or (memq 'fsharp
              compilation-error-regexp-alist)
        (progn
          (add-to-list 'compilation-error-regexp-alist 'fsharp)
          (add-to-list 'compilation-error-regexp-alist-alist
                       `(fsharp ,fsharp-error-regexp-fs 1 2 3)))))

;; Usual match-string doesn't work properly with font-lock-mode
;; on some emacs.

(defun fsharp-match-string (num &optional string)

  "Return string of text matched by last search, without properties.

NUM specifies which parenthesized expression in the last regexp.
Value is nil if NUMth pair didn't match, or there were less than NUM
pairs.  Zero means the entire text matched by the whole regexp or
whole string."

  (let* ((data (match-data))
         (begin (nth (* 2 num) data))
         (end (nth (1+ (* 2 num)) data)))
    (if string (substring string begin end)
      (buffer-substring-no-properties begin end))))

;;; Project

(defun fsharp-mode/find-sln-or-fsproj (dir-or-file)
  "Search for a solution or F# project file in any enclosing
folders relative to DIR-OR-FILE."
  (fsharp-mode-search-upwards (rx (0+ nonl) (or ".fsproj" ".sln") eol)
                              (file-name-directory dir-or-file)))

(defun fsharp-mode-search-upwards (regex dir)
  (when dir
    (or (car-safe (directory-files dir 'full regex))
        (fsharp-mode-search-upwards regex (fsharp-mode-parent-dir dir)))))

(defun fsharp-mode-parent-dir (dir)
  (let ((p (file-name-directory (directory-file-name dir))))
    (unless (equal p dir)
      p)))

;; Make project.el aware of fsharp projects
(defun fsharp-mode-project-root (dir)
  (when-let (project-file (fsharp-mode/find-sln-or-fsproj dir))
    (cons 'fsharp (file-name-directory project-file))))

(cl-defmethod project-roots ((project (head fsharp)))
  (list (cdr project)))

(add-hook 'project-find-functions #'fsharp-mode-project-root)

(provide 'fsharp-mode)

;;; fsharp-mode.el ends here


================================================
FILE: inf-fsharp-mode.el
================================================
;;; inf-fsharp-mode.el --- Support for F# interactive

;; Copyright (C) 1997 INRIA

;; Author: 1993-1997 Xavier Leroy, Jacques Garrigue
;;         2010-2011 Laurent Le Brun <laurent@le-brun.eu>
;; Maintainer: Robin Neatherway <robin.neatherway@gmail.com>
;; Keywords: languages

;; This file is not part of GNU Emacs.

;; This file is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 3, or (at your option)
;; any later version.

;; This file is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING.  If not, write to
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.

(require 'tramp)
(require 'comint)
(require 'fsharp-mode-util)

(require 'cl-lib)

;; User modifiable variables

;; Whether you want the output buffer to be diplayed when you send a phrase

(defvar fsharp-display-when-eval t
  "*If true, display the inferior fsharp buffer when evaluating expressions.")

(defvar inferior-fsharp-program
  (cond
   ((executable-find "dotnet") "dotnet fsi --readline-")
   (fsharp-ac-using-mono "fsharpi --readline-")
   (t (concat "\"" (fsharp-mode--executable-find "fsi.exe") "\" --fsi-server-input-codepage:65001")))
  "Inferior F# command.")

;; End of User modifiable variables


(defvar inferior-fsharp-mode-map
  (let ((map (copy-keymap comint-mode-map)))
    (define-key map [M-return] 'fsharp-comint-send)
    map))

;; Augment fsharp mode, so you can process fsharp code in the source files.

(define-derived-mode inferior-fsharp-mode comint-mode "Inferior fsharp"
  "Major mode for interacting with an inferior fsharp process.
Runs a fsharp toplevel as a subprocess of Emacs, with I/O through an
Emacs buffer. A history of input phrases is maintained. Phrases can
be sent from another buffer in fsharp mode.

\\{inferior-fsharp-mode-map}"
  (setq comint-prompt-regexp "^> ?")
  (setq comint-prompt-read-only t)

  (set (make-local-variable 'paragraph-start) (concat "^$\\|" page-delimiter))
  (set (make-local-variable 'paragraph-separate) paragraph-start)
  (set (make-local-variable 'paragraph-ignore-fill-prefix) t)
  (set (make-local-variable 'require-final-newline) t)
  (set (make-local-variable 'comment-start) "(*")
  (set (make-local-variable 'comment-end) "*)")
  (set (make-local-variable 'comment-column) 40)
  (set (make-local-variable 'comment-start-skip) "(\\*+ *")
  (set (make-local-variable 'parse-sexp-ignore-comments) nil)
  (set (make-local-variable 'comint-process-echoes) t)
  (run-hooks 'inferior-fsharp-mode-hooks)

  ;; use compilation mode to parse errors, but RET and C-cC-c should still be from comint-mode
  (compilation-minor-mode)
  (make-local-variable 'minor-mode-map-alist)
  (setq minor-mode-map-alist (assq-delete-all 'compilation-minor-mode (cl-copy-seq minor-mode-map-alist))))

(defconst inferior-fsharp-buffer-subname "inferior-fsharp")
(defconst inferior-fsharp-buffer-name
  (concat "*" inferior-fsharp-buffer-subname "*"))

(defun fsharp--localname (file)
  "Return localname of a Tramp filename.
If FILE is not a Tramp filename return FILENAME"
  (if (tramp-tramp-file-p file)
      (with-parsed-tramp-file-name file nil
	localname)
    file))

(defun fsharp-run-process-if-needed (&optional cmd)
  "Launch fsi if needed, using CMD if supplied."
  (unless (comint-check-proc inferior-fsharp-buffer-name)
    (setq inferior-fsharp-program
          (or cmd (read-from-minibuffer "fsharp toplevel to run: "
                                        inferior-fsharp-program)))
    (let ((cmdlist (inferior-fsharp-args-to-list inferior-fsharp-program))
          (process-connection-type 'pty))
      (with-current-buffer (apply (function make-comint)
                                  inferior-fsharp-buffer-subname
                                  (car cmdlist) nil
                                  (cdr cmdlist))
        (when (eq system-type 'windows-nt)
          (set-process-coding-system (get-buffer-process (current-buffer))
                                     'utf-8 'utf-8))
        (inferior-fsharp-mode))
      (display-buffer inferior-fsharp-buffer-name))))

;;;###autoload
(defun run-fsharp (&optional cmd)
  "Run an inferior fsharp process.
Input and output via buffer `*inferior-fsharp*'."
  (interactive
   (list (if (not (comint-check-proc inferior-fsharp-buffer-name))
             (read-from-minibuffer "fsharp toplevel to run: "
                                   inferior-fsharp-program))))
  (fsharp-run-process-if-needed cmd)
  (switch-to-buffer-other-window inferior-fsharp-buffer-name))

;; split the command line (e.g. "mono fsi" -> ("mono" "fsi"))
;; we double the \ before unquoting, so that the user doesn't have to
(defun inferior-fsharp-args-to-list (string)
  (split-string-and-unquote (replace-regexp-in-string "\\\\" "\\\\\\\\" string)))

(defun inferior-fsharp-show-subshell ()
  (interactive)
  (fsharp-run-process-if-needed)
  (display-buffer inferior-fsharp-buffer-name)

  (let ((buf (current-buffer))
        (fsharp-buf  (get-buffer inferior-fsharp-buffer-name))
        (count 0))
    (while
        (and (< count 10)
             (not (equal (buffer-name (current-buffer))
                         inferior-fsharp-buffer-name)))
      (next-multiframe-window)
      (setq count (+ count 1)))
    (if  (equal (buffer-name (current-buffer))
                inferior-fsharp-buffer-name)
        (goto-char (point-max)))
    (while
        (> count 0)
      (previous-multiframe-window)
      (setq count (- count 1)))))

(defun inferior-fsharp-eval-region (start end)
  "Send the current region to the inferior fsharp process."
  (interactive "r")
  (fsharp-run-process-if-needed)
  ;; send location to fsi
  (let* ((name (file-truename (buffer-file-name (current-buffer))))
         (dir (fsharp--localname (file-name-directory name)))
         (line (number-to-string (line-number-at-pos start)))
         (loc (concat "# " line " \"" name "\"\n"))
         (movedir (concat "#silentCd @\"" dir "\";;\n")))
    (comint-send-string inferior-fsharp-buffer-name movedir)
    (comint-send-string inferior-fsharp-buffer-name loc))
  (save-excursion
    (goto-char end)
    (comint-send-region inferior-fsharp-buffer-name start (point))
    ;; normally, ";;" are part of the region
    (if (and (>= (point) 2)
             (prog2 (backward-char 2) (looking-at ";;")))
        (comint-send-string inferior-fsharp-buffer-name "\n")
      (comint-send-string inferior-fsharp-buffer-name "\n;;\n"))
    ;; the user may not want to see the output buffer
    (if fsharp-display-when-eval
        (display-buffer inferior-fsharp-buffer-name t))))

(defvar fsharp-previous-output nil
  "tells the beginning of output in the shell-output buffer, so that the
output can be retreived later, asynchronously.")

;; To insert the last output from fsharp at point
(defun fsharp-insert-last-output ()
  "Insert the result of the evaluation of previous phrase"
  (interactive)
  (let ((pos (process-mark (get-buffer-process inferior-fsharp-buffer-name))))
    (insert-buffer-substring inferior-fsharp-buffer-name
                             fsharp-previous-output (- pos 2))))


(defun fsharp-simple-send (proc string)
  (comint-simple-send proc (concat string ";;")))

(defun fsharp-comint-send ()
  (interactive)
  (let ((comint-input-sender 'fsharp-simple-send))
    (comint-send-input)))

(provide 'inf-fsharp-mode)

;;; inf-sharp-mode.el ends here


================================================
FILE: test/CompileCommandData/Directory With Spaces/noproj/test.fs
================================================


================================================
FILE: test/CompileCommandData/Directory With Spaces/proj/test.fs
================================================


================================================
FILE: test/CompileCommandData/Directory With Spaces/proj/test.fsproj
================================================


================================================
FILE: test/CompileCommandData/noproj/test.fs
================================================


================================================
FILE: test/CompileCommandData/proj/Makefile
================================================


================================================
FILE: test/CompileCommandData/proj/test.fs
================================================


================================================
FILE: test/CompileCommandData/proj/test.fsproj
================================================


================================================
FILE: test/FindSlnData/bar.sln
================================================


================================================
FILE: test/FindSlnData/noproj/test.fs
================================================


================================================
FILE: test/FindSlnData/sln/foo.sln
================================================


================================================
FILE: test/FindSlnData/test.fsproj
================================================


================================================
FILE: test/StructureTest/Blocks.fs
================================================
let notABlock = 5

let basicBlock =
    [ 1; 2; 3 ]
    |> List.fold (fun x y -> x + y)

type Shape =
    | Square
    | Rectangle
    | Triangle

let aFunction x y =
    if x < y
    then
        x
    else
        y



================================================
FILE: test/StructureTest/BracketIndent.fs
================================================
let formatOne = [ "this"
                  "that"
                  "the-other"

                              ]

let formatTwo = [
    "this"
    "that"

    ]

let formatThree =
    [ "this"
      "that"
      "the-other"
      "hi"

      ]


================================================
FILE: test/StructureTest/ContinuationLines.fs
================================================
let x = 5
let y =
    [ 1; 2 ]
    |> List.fold (fun x y -> x + y)

let z = 5 +
        6


================================================
FILE: test/StructureTest/Literals.fs
================================================
// Generated using https://hipsum.co/

// I'm a longer comment! Now, with Hipster Lorem Ipsum:
//
// Lorem ipsum dolor amet man braid +1 palo santo, whatever retro taxidermy
// quinoa cred venmo church-key. Pok pok cray cornhole selvage irony keytar
// disrupt man braid, everyday carry intelligentsia pitchfork street art hell
// of. Schlitz air plant beard, fam authentic health goth hella fashion axe palo
// santo pok pok. Hell of post-ironic artisan put a bird on it shoreditch shabby
// chic. Bitters 3 wolf moon food truck adaptogen.
//
// Paleo fanny pack poutine, williamsburg health goth four dollar toast
// aesthetic. Tbh viral truffaut live-edge asymmetrical ramps chillwave ethical
// keytar fixie post-ironic vaporware air plant intelligentsia. Wayfarers
// flannel iceland, DIY meditation celiac green juice disrupt. Food truck paleo
// bicycle rights cold-pressed roof party normcore tumblr.

let thisIsHereToBreakUpTheComments = 5

(* This is the same thing, but in a different comment syntax. *)

(* Lorem ipsum dolor amet man braid +1 palo santo, whatever retro taxidermy
quinoa cred venmo church-key. Pok pok cray cornhole selvage irony keytar disrupt
man braid, everyday carry intelligentsia pitchfork street art hell of. Schlitz
air plant beard, fam authentic health goth hella fashion axe palo santo pok pok.
Hell of post-ironic artisan put a bird on it shoreditch shabby chic. Bitters 3
wolf moon food truck adaptogen.

Paleo fanny pack poutine, williamsburg health goth four dollar toast aesthetic.
Tbh viral truffaut live-edge asymmetrical ramps chillwave ethical keytar fixie
post-ironic vaporware air plant intelligentsia. Wayfarers flannel iceland, DIY
meditation celiac green juice disrupt. Food truck paleo bicycle rights
cold-pressed roof party normcore tumblr. *)

/// Yet again the same thing, but in a doc comment.
///
/// Lorem ipsum dolor amet man braid +1 palo santo, whatever retro taxidermy
/// quinoa cred venmo church-key. Pok pok cray cornhole selvage irony keytar
/// disrupt man braid, everyday carry intelligentsia pitchfork street art hell
/// of. Schlitz air plant beard, fam authentic health goth hella fashion axe
/// palo santo pok pok. Hell of post-ironic artisan put a bird on it shoreditch
/// shabby chic. Bitters 3 wolf moon food truck adaptogen.
///
/// Paleo fanny pack poutine, williamsburg health goth four dollar toast
/// aesthetic. Tbh viral truffaut live-edge asymmetrical ramps chillwave ethical
/// keytar fixie post-ironic vaporware air plant intelligentsia. Wayfarers
/// flannel iceland, DIY meditation celiac green juice disrupt. Food truck paleo
/// bicycle rights cold-pressed roof party normcore tumblr.


let simple = "this is a very normal string"

let stringInString = "This contains another \"string\", so to speak."

let longer =
    """
    This is a triple-quoted string
    """

let evenLonger = """
This string is very long and had "normal extra quotes" and also
a small number of \"escaped quotes\", and also a gratuitous it's.
"""


================================================
FILE: test/StructureTest/Nesting.fs
================================================
// This file contains hand-crafted structures for use by `fsharp-mode-structure-tests.el`.
// In particular, many/most of those tests need to work by:
//
//   1. Inserting text in a temp buffer
//   2. Moving point to a known position
//   3. Comparing computed values against expected answers
//
// Frequently, we're comparing things like, "what is the exact (point) position
// of a given square brace." This means that formatting changes to this buffer
// -- indeed, edits _of any kind_ -- will almost certainly break the tests! Edit
// thoughtfully and intentionally! Update things as needed!

// (point) of opening [: 640
let aList = [ 1; 2; 3]

// (point) of inner opening [: 706
let nestedList = [ [ "this"; "that" ] ]

// (point) of opening [: 777
let multiLineList = [
    "this"
    "that"
]

// (point) of outermost opening [: 947
// (point) of middle opening [:    953
// (point) of innermost opening [: 955
let multiLineNestedList = [
    [ [ "how"; "now"]
    ]
]

// (point) of opening {: 1060
// (point) of inner {:   1121
let anAsync = async {
    let value = funCall()

    let! differentValue = async { return! 5 }
}

// (point) of opening (: 1208
let thing =
    [ 1; 2]
    |> List.map (fun i ->
                 i ** i )


================================================
FILE: test/StructureTest/Relative.fs
================================================
type Test  =
    | Unit
    | Integration of string
    | EndToEnd


if thing <> true then
    printfn "thing is not true"
else if thing = true
then
    printfn "maybe?"
else
    printfn "it is so"


let aThing (test : Test) = function
    | Unit -> ()
    | Integration -> ()
    | EndToEnd -> ()


================================================
FILE: test/Test1/Error.fs
================================================
module Error

let x = nonexisting()



================================================
FILE: test/Test1/FileTwo.fs
================================================
module FileTwo

type Foo =
  | Bar
  | Qux

let addition x y = x + y

let add x y = x + y

type NewObjectType() =

  member x.Terrific (y : int) : int =
    y


================================================
FILE: test/Test1/NoProject.fs
================================================
module FileTwo

open System.Collection


================================================
FILE: test/Test1/Pervasive.fs
================================================
let printtest args =
    printfn "Hello %d" 10
    0


================================================
FILE: test/Test1/Program.fs
================================================
module X =
  let func x = x + 1

let testval = FileTwo.NewObjectType()

let val2 = X.func 2

let val3 = testval.Terrific val2

let val4 : FileTwo.NewObjectType = testval

type Dummy = Foo | Bar

let val5:Dummy = Foo

[<EntryPoint>]
let main args =
    printfn "Hello %d" val2
    0


================================================
FILE: test/Test1/Script.fsx
================================================


module XA =
  let funky x = x + 1

let val99 = XA.funky 21


================================================
FILE: test/Test1/Test1.fsproj
================================================
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net9.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <Compile Include="Error.fs" />
    <Compile Include="FileTwo.fs" />
    <Compile Include="Program.fs" />
    <Compile Include="Pervasive.fs" />
  </ItemGroup>

</Project>


================================================
FILE: test/Test2/Main.fs
================================================
module Test2.Main

let val2 = List.map ((+) 1) [1;2]

[<EntryPoint>]
let main args =
    printfn "Hello %A" val2
    0


================================================
FILE: test/Test2/Test2.fsproj
================================================
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
    <ProductVersion>8.0.30703</ProductVersion>
    <SchemaVersion>2.0</SchemaVersion>
    <ProjectGuid>{116cc2f9-f987-4b3d-915a-34cac04a73db}</ProjectGuid>
    <OutputType>Library</OutputType>
    <RootNamespace>Test2</RootNamespace>
    <AssemblyName>Test2</AssemblyName>
    <Name>Test2</Name>
    <UsePartialTypes>False</UsePartialTypes>
    <BuildOrder>
      <BuildOrder>
        <String>Program.fs</String>
      </BuildOrder>
    </BuildOrder>
    <TargetFSharpCoreVersion>4.3.0.0</TargetFSharpCoreVersion>
    <MinimumVisualStudioVersion Condition="'$(MinimumVisualStudioVersion)' == ''">11</MinimumVisualStudioVersion>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
    <DebugSymbols>True</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>False</Optimize>
    <Tailcalls>False</Tailcalls>
    <OutputPath>bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <WarningLevel>3</WarningLevel>
    <PlatformTarget>x86</PlatformTarget>
    <DocumentationFile>bin\Debug\Test2.XML</DocumentationFile>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
    <DebugType>pdbonly</DebugType>
    <Optimize>True</Optimize>
    <Tailcalls>True</Tailcalls>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <WarningLevel>3</WarningLevel>
    <PlatformTarget>x86</PlatformTarget>
    <DocumentationFile>bin\Release\Test2.XML</DocumentationFile>
    <DebugSymbols>False</DebugSymbols>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="FSharp.Core, Version=$(TargetFSharpCoreVersion), Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
      <Private>True</Private>
    </Reference>
    <Reference Include="mscorlib" />
    <Reference Include="System" />
    <Reference Include="System.Core" />
    <ProjectReference Include="..\Test1\Test1.fsproj">
      <Project>{116cc2f9-f987-4b3d-915a-34cac04a73da}</Project>
      <Name>Test1</Name>
    </ProjectReference>
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Main.fs" />
  </ItemGroup>
  <Choose>
    <When Condition="'$(VisualStudioVersion)' == '11.0'">
      <PropertyGroup>
        <FSharpTargetsPath>$(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets</FSharpTargetsPath>
      </PropertyGroup>
    </When>
    <Otherwise>
      <PropertyGroup>
        <FSharpTargetsPath>$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets</FSharpTargetsPath>
      </PropertyGroup>
    </Otherwise>
  </Choose>
  <Import Project="$(FSharpTargetsPath)" Condition="Exists('$(FSharpTargetsPath)')" />
  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
	     Other similar extension points exist, see Microsoft.Common.targets.
	<Target Name="BeforeBuild">
	</Target>
	<Target Name="AfterBuild">
	</Target>
	-->
</Project>


================================================
FILE: test/apps/FQuake3/NativeMappings.fs
================================================
(*
Copyright (C) 2013 William F. Smith

This program is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.

This program is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

Derivative of Quake III Arena source:
Copyright (C) 1999-2005 Id Software, Inc.
*)

// Disable native interop warnings
#nowarn "9"
#nowarn "51"

namespace Engine.Native

open System
open System.IO
open System.Runtime.InteropServices
open Microsoft.FSharp.NativeInterop
open FSharp.Game.Math
open Engine.Core
open Engine.Net
open Engine.FileSystem
open Engine.NativeInterop
open FQuake3.Math
open FQuake3.Md3

/// Used to prevent massive copying of large immutable data.
module private Cache =
    let mutable md3Map = Map.empty<nativeint, Md3>

module Boolean =
    let inline ofNativePtr (ptr: nativeptr<qboolean>) =
        let mutable native = NativePtr.read ptr

        match native with
        | qboolean.qtrue -> true
        | _ -> false

    let inline toNativeByPtr (ptr: nativeptr<qboolean>) (value: bool) =
        let mutable native = NativePtr.read ptr

        native <- if value then qboolean.qtrue else qboolean.qfalse

        NativePtr.write ptr native

    let inline toNative (value: bool) =
        if value then qboolean.qtrue else qboolean.qfalse

module Vec2 =
    let inline ofNativePtr (ptr: nativeptr<vec2_t>) =
        let mutable native = NativePtr.read ptr

        vec2 (native.value, native.value1)

    let inline toNativeByPtr (ptr: nativeptr<vec2_t>) (v: vec2) =
        let mutable native = NativePtr.read ptr

        native.value <- v.X
        native.value1 <- v.Y

        NativePtr.write ptr native  

module Vec3 =
    let inline ofNativePtr (ptr: nativeptr<vec3_t>) =
        let mutable native = NativePtr.read ptr

        vec3 (native.value, native.value1, native.value2)

    let inline toNativeByPtr (ptr: nativeptr<vec3_t>) (v: vec3) =
        let mutable native = NativePtr.read ptr

        native.value <- v.X
        native.value1 <- v.Y
        native.value2 <- v.Z

        NativePtr.write ptr native   
        
module Vec4 =
    let inline ofNativePtr (ptr: nativeptr<vec4_t>) =
        let mutable native = NativePtr.read ptr

        vec4 (native.value, native.value1, native.value2, native.value3)

    let inline toNativeByPtr (ptr: nativeptr<vec4_t>) (v: vec4) =
        let mutable native = NativePtr.read ptr

        native.value <- v.X
        native.value1 <- v.Y
        native.value2 <- v.Z
        native.value3 <- v.W

        NativePtr.write ptr native  

module Mat4 =
    let inline ofNativePtr (ptr: nativeptr<single>) =
        mat4 (
            (NativePtr.get ptr 0),
            (NativePtr.get ptr 1),
            (NativePtr.get ptr 2),
            (NativePtr.get ptr 3),
            (NativePtr.get ptr 4),
            (NativePtr.get ptr 5),
            (NativePtr.get ptr 6),
            (NativePtr.get ptr 7),
            (NativePtr.get ptr 8),
            (NativePtr.get ptr 9),
            (NativePtr.get ptr 10),
            (NativePtr.get ptr 11),
            (NativePtr.get ptr 12),
            (NativePtr.get ptr 13),
            (NativePtr.get ptr 14),
            (NativePtr.get ptr 15)
        )

    let inline toNativeByPtr (ptr: nativeptr<single>) (m: mat4) =
        NativePtr.set ptr 0 m.[0, 0]
        NativePtr.set ptr 1 m.[0, 1]
        NativePtr.set ptr 2 m.[0, 2]
        NativePtr.set ptr 3 m.[0, 3]
        NativePtr.set ptr 4 m.[1, 0]
        NativePtr.set ptr 5 m.[1, 1]
        NativePtr.set ptr 6 m.[1, 2]
        NativePtr.set ptr 7 m.[1, 3]
        NativePtr.set ptr 8 m.[2, 0]
        NativePtr.set ptr 9 m.[2, 1]
        NativePtr.set ptr 10 m.[2, 2]
        NativePtr.set ptr 11 m.[2, 3]
        NativePtr.set ptr 12 m.[3, 0]
        NativePtr.set ptr 13 m.[3, 1]
        NativePtr.set ptr 14 m.[3, 2]
        NativePtr.set ptr 15 m.[3, 3]

module Cvar =
    let inline ofNativePtr (ptr: nativeptr<cvar_t>) =
        let mutable native = NativePtr.read ptr

        {
            Name = NativePtr.toStringAnsi native.name;
            String = NativePtr.toStringAnsi native.string;
            ResetString = NativePtr.toStringAnsi native.resetString;
            LatchedString = NativePtr.toStringAnsi native.latchedString;
            Flags = native.flags;
            IsModified = Boolean.ofNativePtr &&native.modified;
            ModificationCount = native.modificationCount;
            Value = native.value;
            Integer = native.integer;
        }

module Bounds =
    let inline ofNativePtr (ptr: nativeptr<vec3_t>) =
        Bounds (
            Vec3.ofNativePtr <| NativePtr.add ptr 0,
            Vec3.ofNativePtr <| NativePtr.add ptr 1)

    let inline toNativeByPtr (ptr: nativeptr<vec3_t>) (bounds: Bounds) =
        let mutable nativeX = NativePtr.get ptr 0
        let mutable nativeY = NativePtr.get ptr 1

        Vec3.toNativeByPtr &&nativeX bounds.Min
        Vec3.toNativeByPtr &&nativeY bounds.Max

        NativePtr.set ptr 0 nativeX
        NativePtr.set ptr 1 nativeY

module Message =
    let inline ofNativePtr (ptr: nativeptr<msg_t>) =
        let mutable native = NativePtr.read ptr

        {
            IsAllowedOverflow = Boolean.ofNativePtr &&native.allowoverflow;
            IsOverflowed = Boolean.ofNativePtr &&native.overflowed;
            IsOutOfBand = Boolean.ofNativePtr &&native.oob;
            Data = Seq.ofNativePtrArray native.cursize native.data;
            MaxSize = native.maxsize;
            ReadCount = native.readcount;
            Bit = native.bit;
        }

module IPAddress =
    let inline ofNativePtr (ptr: nativeptr<byte>) =
        {
            Octet1 = NativePtr.get ptr 0;
            Octet2 = NativePtr.get ptr 1;
            Octet3 = NativePtr.get ptr 2;
            Octet4 = NativePtr.get ptr 3;
        }

module Address =
    let inline ofNativePtr (ptr: nativeptr<netadr_t>) =
        let mutable native = NativePtr.read ptr

        {
            Type = enum<AddressType> (int native.type');
            IP = IPAddress.ofNativePtr &&native.ip
            Port = native.port;
        }

module Md3Frame =
    let inline ofNativePtr (ptr: nativeptr<md3Frame_t>) =
        let mutable native = NativePtr.read ptr

        {
            Bounds = Bounds.ofNativePtr &&native.bounds;
            LocalOrigin = Vec3.ofNativePtr &&native.localOrigin;
            Radius = native.radius;
            Name = NativePtr.toStringAnsi &&native.name;
        }

module Md3 =
    let ofNativePtr (ptr: nativeptr<md3Header_t>) =
        let mutable native = NativePtr.read ptr

        let hash = NativePtr.toNativeInt ptr

        match Map.tryFind hash Cache.md3Map with
        | Some x -> x
        | None ->

        let bytes = Array.zeroCreate<byte> native.ofsEnd
        Marshal.Copy (NativePtr.toNativeInt ptr, bytes, 0, native.ofsEnd)
        let md3 = FQuake3.Utils.Md3.parse bytes
        Cache.md3Map <- Map.add hash md3 Cache.md3Map
        md3

module DirectoryInfo =
    let ofNativePtr (ptr: nativeptr<directory_t>) =
        let mutable native = NativePtr.read ptr

        let path = NativePtr.toStringAnsi &&native.path
        let name = NativePtr.toStringAnsi &&native.gamedir

        DirectoryInfo (Path.Combine (path, name))

module Pak =
    let ofNativePtr (ptr: nativeptr<pack_t>) =
        let mutable native = NativePtr.read ptr

        {
            FileInfo = FileInfo (NativePtr.toStringAnsi &&native.pakFilename);
            Checksum = native.checksum;
            PureChecksum = native.checksum;
            FileCount = native.numfiles;
        }

module ServerPakChecksum =
    let createFrom_fs_serverPaks (size: int) (ptr: nativeptr<int>) =
        match NativePtr.isValid ptr with
        | false -> []
        | _ -> NativePtr.toList size ptr

module SearchPath =
    let ofNativePtr (ptr: nativeptr<searchpath_t>) =
        let mutable native = NativePtr.read ptr

        {
            DirectoryInfo = Option.ofNativePtr DirectoryInfo.ofNativePtr native.directory
        }

    let convertFrom_fs_searchpaths (ptr: nativeptr<searchpath_t>) =
        let rec f (searchPaths: SearchPath list) (ptr: nativeptr<searchpath_t>) =
            match NativePtr.isValid ptr with
            | false -> searchPaths
            | _ ->
            let mutable native = NativePtr.read ptr
            f (ofNativePtr ptr :: searchPaths) (native.next)

        f [] ptr


================================================
FILE: test/apps/FQuake3/NativeMappings.fs.faceup
================================================
«x:(*
Copyright (C) 2013 William F. Smith

This program is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.

This program is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

Derivative of Quake III Arena source:
Copyright (C) 1999-2005 Id Software, Inc.
*)»

«m:// »«x:Disable native interop warnings
»«k:#nowarn» «s:"9"»
«k:#nowarn» «s:"51"»

«k:namespace» «v:Engine».«v:Native»

«k:open» «v:System»
«k:open» «v:System».«v:IO»
«k:open» «v:System».«v:Runtime».«v:InteropServices»
«k:open» «v:Microsoft».«v:FSharp».«v:NativeInterop»
«k:open» «v:FSharp».«v:Game».«v:Math»
«k:open» «v:Engine».«v:Core»
«k:open» «v:Engine».«v:Net»
«k:open» «v:Engine».«v:FileSystem»
«k:open» «v:Engine».«v:NativeInterop»
«k:open» «v:FQuake3».«v:Math»
«k:open» «v:FQuake3».«v:Md3»

«m:/// »«x:Used to prevent massive copying of large immutable data.
»«k:module» «k:private» «v:Cache» =
    «k:let» «k:mutable» «v:md3Map» = Map.empty<nativeint, Md3>

«k:module» «t:Boolean» =
    «k:let» «k:inline» «f:ofNativePtr» («v:ptr»: «t:nativeptr»«:fsharp-ui-generic-face:<qboolean>») =
        «k:let» «k:mutable» «v:native» = NativePtr.read ptr

        «k:match» native «k:with»
        «:fsharp-ui-operator-face:|» qboolean.qtrue -> «k:true»
        «:fsharp-ui-operator-face:|» _ -> «k:false»

    «k:let» «k:inline» «f:toNativeByPtr» («v:ptr»: «t:nativeptr»«:fsharp-ui-generic-face:<qboolean>») («v:value»: «t:bool») =
        «k:let» «k:mutable» «v:native» = NativePtr.read ptr

        native <- «k:if» value «k:then» qboolean.qtrue «k:else» qboolean.qfalse

        NativePtr.write ptr native

    «k:let» «k:inline» «f:toNative» («v:value»: «t:bool») =
        «k:if» value «k:then» qboolean.qtrue «k:else» qboolean.qfalse

«k:module» «t:Vec2» =
    «k:let» «k:inline» «f:ofNativePtr» («v:ptr»: «t:nativeptr»«:fsharp-ui-generic-face:<vec2_t>») =
        «k:let» «k:mutable» «v:native» = NativePtr.read ptr

        vec2 (native.value, native.value1)

    «k:let» «k:inline» «f:toNativeByPtr» («v:ptr»: «t:nativeptr»«:fsharp-ui-generic-face:<vec2_t>») («v:v»: «t:vec2») =
        «k:let» «k:mutable» «v:native» = NativePtr.read ptr

        native.value <- v.X
        native.value1 <- v.Y

        NativePtr.write ptr native  

«k:module» «t:Vec3» =
    «k:let» «k:inline» «f:ofNativePtr» («v:ptr»: «t:nativeptr»«:fsharp-ui-generic-face:<vec3_t>») =
        «k:let» «k:mutable» «v:native» = NativePtr.read ptr

        vec3 (native.value, native.value1, native.value2)

    «k:let» «k:inline» «f:toNativeByPtr» («v:ptr»: «t:nativeptr»«:fsharp-ui-generic-face:<vec3_t>») («v:v»: «t:vec3») =
        «k:let» «k:mutable» «v:native» = NativePtr.read ptr

        native.value <- v.X
        native.value1 <- v.Y
        native.value2 <- v.Z

        NativePtr.write ptr native   
        
«k:module» «t:Vec4» =
    «k:let» «k:inline» «f:ofNativePtr» («v:ptr»: «t:nativeptr»«:fsharp-ui-generic-face:<vec4_t>») =
        «k:let» «k:mutable» «v:native» = NativePtr.read ptr

        vec4 (native.value, native.value1, native.value2, native.value3)

    «k:let» «k:inline» «f:toNativeByPtr» («v:ptr»: «t:nativeptr»«:fsharp-ui-generic-face:<vec4_t>») («v:v»: «t:vec4») =
        «k:let» «k:mutable» «v:native» = NativePtr.read ptr

        native.value <- v.X
        native.value1 <- v.Y
        native.value2 <- v.Z
        native.value3 <- v.W

        NativePtr.write ptr native  

«k:module» «t:Mat4» =
    «k:let» «k:inline» «f:ofNativePtr» («v:ptr»: «t:nativeptr»«:fsharp-ui-generic-face:<single>») =
        mat4 (
            (NativePtr.get ptr 0),
            (NativePtr.get ptr 1),
            (NativePtr.get ptr 2),
            (NativePtr.get ptr 3),
            (NativePtr.get ptr 4),
            (NativePtr.get ptr 5),
            (NativePtr.get ptr 6),
            (NativePtr.get ptr 7),
            (NativePtr.get ptr 8),
            (NativePtr.get ptr 9),
            (NativePtr.get ptr 10),
            (NativePtr.get ptr 11),
            (NativePtr.get ptr 12),
            (NativePtr.get ptr 13),
            (NativePtr.get ptr 14),
            (NativePtr.get ptr 15)
        )

    «k:let» «k:inline» «f:toNativeByPtr» («v:ptr»: «t:nativeptr»«:fsharp-ui-generic-face:<single>») («v:m»: «t:mat4») =
        NativePtr.set ptr 0 m.[0, 0]
        NativePtr.set ptr 1 m.[0, 1]
        NativePtr.set ptr 2 m.[0, 2]
        NativePtr.set ptr 3 m.[0, 3]
        NativePtr.set ptr 4 m.[1, 0]
        NativePtr.set ptr 5 m.[1, 1]
        NativePtr.set ptr 6 m.[1, 2]
        NativePtr.set ptr 7 m.[1, 3]
        NativePtr.set ptr 8 m.[2, 0]
        NativePtr.set ptr 9 m.[2, 1]
        NativePtr.set ptr 10 m.[2, 2]
        NativePtr.set ptr 11 m.[2, 3]
        NativePtr.set ptr 12 m.[3, 0]
        NativePtr.set ptr 13 m.[3, 1]
        NativePtr.set ptr 14 m.[3, 2]
        NativePtr.set ptr 15 m.[3, 3]

«k:module» «t:Cvar» =
    «k:let» «k:inline» «f:ofNativePtr» («v:ptr»: «t:nativeptr»«:fsharp-ui-generic-face:<cvar_t>») =
        «k:let» «k:mutable» «v:native» = NativePtr.read ptr

        {
            Name = NativePtr.toStringAnsi native.name;
            String = NativePtr.toStringAnsi native.string;
            ResetString = NativePtr.toStringAnsi native.resetString;
            LatchedString = NativePtr.toStringAnsi native.latchedString;
            Flags = native.flags;
            IsModified = Boolean.ofNativePtr &&native.modified;
            ModificationCount = native.modificationCount;
            Value = native.value;
            Integer = native.integer;
        }

«k:module» «t:Bounds» =
    «k:let» «k:inline» «f:ofNativePtr» («v:ptr»: «t:nativeptr»«:fsharp-ui-generic-face:<vec3_t>») =
        Bounds (
            Vec3.ofNativePtr «:fsharp-ui-operator-face:<|» NativePtr.add ptr 0,
            Vec3.ofNativePtr «:fsharp-ui-operator-face:<|» NativePtr.add ptr 1)

    «k:let» «k:inline» «f:toNativeByPtr» («v:ptr»: «t:nativeptr»«:fsharp-ui-generic-face:<vec3_t>») («v:bounds»: «t:Bounds») =
        «k:let» «k:mutable» «v:nativeX» = NativePtr.get ptr 0
        «k:let» «k:mutable» «v:nativeY» = NativePtr.get ptr 1

        Vec3.toNativeByPtr &&nativeX bounds.Min
        Vec3.toNativeByPtr &&nativeY bounds.Max

        NativePtr.set ptr 0 nativeX
        NativePtr.set ptr 1 nativeY

«k:module» «t:Message» =
    «k:let» «k:inline» «f:ofNativePtr» («v:ptr»: «t:nativeptr»«:fsharp-ui-generic-face:<msg_t>») =
        «k:let» «k:mutable» «v:native» = NativePtr.read ptr

        {
            IsAllowedOverflow = Boolean.ofNativePtr &&native.allowoverflow;
            IsOverflowed = Boolean.ofNativePtr &&native.overflowed;
            IsOutOfBand = Boolean.ofNativePtr &&native.oob;
            Data = Seq.ofNativePtrArray native.cursize native.data;
            MaxSize = native.maxsize;
            ReadCount = native.readcount;
            Bit = native.bit;
        }

«k:module» «t:IPAddress» =
    «k:let» «k:inline» «f:ofNativePtr» («v:ptr»: «t:nativeptr»«:fsharp-ui-generic-face:<byte>») =
        {
            Octet1 = NativePtr.get ptr 0;
            Octet2 = NativePtr.get ptr 1;
            Octet3 = NativePtr.get ptr 2;
            Octet4 = NativePtr.get ptr 3;
        }

«k:module» «t:Address» =
    «k:let» «k:inline» «f:ofNativePtr» («v:ptr»: «t:nativeptr»«:fsharp-ui-generic-face:<netadr_t>») =
        «k:let» «k:mutable» «v:native» = NativePtr.read ptr

        {
            Type = enum<AddressType> (int native.type');
            IP = IPAddress.ofNativePtr &&native.ip
            Port = native.port;
        }

«k:module» «t:Md3Frame» =
    «k:let» «k:inline» «f:ofNativePtr» («v:ptr»: «t:nativeptr»«:fsharp-ui-generic-face:<md3Frame_t>») =
        «k:let» «k:mutable» «v:native» = NativePtr.read ptr

        {
            Bounds = Bounds.ofNativePtr &&native.bounds;
            LocalOrigin = Vec3.ofNativePtr &&native.localOrigin;
            Radius = native.radius;
            Name = NativePtr.toStringAnsi &&native.name;
        }

«k:module» «t:Md3» =
    «k:let» «f:ofNativePtr» («v:ptr»: «t:nativeptr»«:fsharp-ui-generic-face:<md3Header_t>») =
        «k:let» «k:mutable» «v:native» = NativePtr.read ptr

        «k:let» «v:hash» = NativePtr.toNativeInt ptr

        «k:match» Map.tryFind hash Cache.md3Map «k:with»
        «:fsharp-ui-operator-face:|» Some x -> x
        «:fsharp-ui-operator-face:|» None ->

        «k:let» «v:bytes» = Array.zeroCreate<byte> native.ofsEnd
        Marshal.Copy (NativePtr.toNativeInt ptr, bytes, 0, native.ofsEnd)
        «k:let» «v:md3» = FQuake3.Utils.Md3.parse bytes
        Cache.md3Map <- Map.add hash md3 Cache.md3Map
        md3

«k:module» «t:DirectoryInfo» =
    «k:let» «f:ofNativePtr» («v:ptr»: «t:nativeptr»«:fsharp-ui-generic-face:<directory_t>») =
        «k:let» «k:mutable» «v:native» = NativePtr.read ptr

        «k:let» «v:path» = NativePtr.toStringAnsi &&native.path
        «k:let» «v:name» = NativePtr.toStringAnsi &&native.gamedir

        DirectoryInfo (Path.Combine (path, name))

«k:module» «t:Pak» =
    «k:let» «f:ofNativePtr» («v:ptr»: «t:nativeptr»«:fsharp-ui-generic-face:<pack_t>») =
        «k:let» «k:mutable» «v:native» = NativePtr.read ptr

        {
            FileInfo = FileInfo (NativePtr.toStringAnsi &&native.pakFilename);
            Checksum = native.checksum;
            PureChecksum = native.checksum;
            FileCount = native.numfiles;
        }

«k:module» «t:ServerPakChecksum» =
    «k:let» «f:createFrom_fs_serverPaks» («v:size»: «t:int») («v:ptr»: «t:nativeptr»«:fsharp-ui-generic-face:<int>») =
        «k:match» NativePtr.isValid ptr «k:with»
        «:fsharp-ui-operator-face:|» «k:false» -> []
        «:fsharp-ui-operator-face:|» _ -> NativePtr.toList size ptr

«k:module» «t:SearchPath» =
    «k:let» «f:ofNativePtr» («v:ptr»: «t:nativeptr»«:fsharp-ui-generic-face:<searchpath_t>») =
        «k:let» «k:mutable» «v:native» = NativePtr.read ptr

        {
            DirectoryInfo = Option.ofNativePtr DirectoryInfo.ofNativePtr native.directory
        }

    «k:let» «f:convertFrom_fs_searchpaths» («v:ptr»: «t:nativeptr»«:fsharp-ui-generic-face:<searchpath_t>») =
        «k:let» «k:rec» «f:f» («v:searchPaths»: «t:SearchPath list») («v:ptr»: «t:nativeptr»«:fsharp-ui-generic-face:<searchpath_t>») =
            «k:match» NativePtr.isValid ptr «k:with»
            «:fsharp-ui-operator-face:|» «k:false» -> searchPaths
            «:fsharp-ui-operator-face:|» _ ->
            «k:let» «k:mutable» «v:native» = NativePtr.read ptr
            f (ofNativePtr ptr :: searchPaths) (native.next)

        f [] ptr


================================================
FILE: test/apps/FSharp.Compatibility/Format.fs
================================================
(*  OCaml Compatibility Library for F# (Format module)
    (FSharp.Compatibility.OCaml.Format)

    Copyright (c) 1996  Institut National de Recherche en 
                        Informatique et en Automatique
    Copyright (c) Jack Pappas 2012
        http://github.com/jack-pappas

    This code is distributed under the terms of the
    GNU Lesser General Public License (LGPL) v2.1.
    See the LICENSE file for details. *)

// References:
// http://caml.inria.fr/pub/docs/manual-ocaml/libref/Format.html

/// Pretty printing.
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module FSharp.Compatibility.OCaml.Format

(**************************************************************

  Data structures definitions.

 **************************************************************)
// TODO : Recreate 'size' as a measure type on int
type size = int
let inline size_of_int (n : int) : size = n
let inline int_of_size (s : size) : int = s
  
(* Tokens are one of the following : *)
type pp_token =
    (* normal text *)
    | Pp_text of string
    (* complete break *)
    | Pp_break of int * int
    (* go to next tabulation *)
    | Pp_tbreak of int * int
    (* set a tabulation *)
    | Pp_stab
    (* beginning of a block *)
    | Pp_begin of int * block_type
    (* end of a block *)
    | Pp_end
    (* beginning of a tabulation block *)
    | Pp_tbegin of tblock
    (* end of a tabulation block *)
    | Pp_tend
    (* to force a newline inside a block *)
    | Pp_newline
    (* to do something only if this very line has been broken *)
    | Pp_if_newline
    (* opening a tag name *)
    | Pp_open_tag of tag
    (* closing the most recently opened tag *)
    | Pp_close_tag

and tag = string

and block_type =
    (* Horizontal block no line breaking *)
    | Pp_hbox
    (* Vertical block each break leads to a new line *)
    | Pp_vbox
    (* Horizontal-vertical block: same as vbox, except if this block
                is small enough to fit on a single line *)
    | Pp_hvbox
    (* Horizontal or Vertical block: breaks lead to new line
                only when necessary to print the content of the block *)
    | Pp_hovbox
    (* Horizontal or Indent block: breaks lead to new line
                only when necessary to print the content of the block, or
                when it leads to a new indentation of the current line *)
    | Pp_box
    (* Internal usage: when a block fits on a single line *)
    | Pp_fits

and tblock =
    (* Tabulation box *)
    | Pp_tbox of (int list) ref

(* The Queue:
   contains all formatting elements.
   elements are tuples (size, token, length), where
   size is set when the size of the block is known
   len is the declared length of the token. *)
type pp_queue_elem = {
    mutable elem_size : size;
    token : pp_token;
    length : int;
}

(* Scan stack:
   each element is (left_total, queue element) where left_total
   is the value of pp_left_total when the element has been enqueued. *)
type pp_scan_elem =
    | Scan_elem of int * pp_queue_elem

(* Formatting stack:
   used to break the lines while printing tokens.
   The formatting stack contains the description of
   the currently active blocks. *)
type pp_format_elem =
    | Format_elem of block_type * int

(* General purpose queues, used in the formatter. *)
type 'a queue_elem =
  | Nil
  | Cons of 'a queue_cell

and 'a queue_cell = {
    mutable head : 'a;
    mutable tail : 'a queue_elem;
}

type 'a queue = {
    mutable insert : 'a queue_elem;
    mutable body : 'a queue_elem;
}

(* The formatter specific tag handling functions. *)
type formatter_tag_functions = {
    mark_open_tag : tag -> string;
    mark_close_tag : tag -> string;
    print_open_tag : tag -> unit;
    print_close_tag : tag -> unit;
  }

(* A formatter with all its machinery. *)
type formatter = {
    mutable pp_scan_stack : pp_scan_elem list;
    mutable pp_format_stack : pp_format_elem list;
    mutable pp_tbox_stack : tblock list;
    mutable pp_tag_stack : tag list;
    mutable pp_mark_stack : tag list;
    (* Global variables: default initialization is
     set_margin 78
     set_min_space_left 0. *)
    /// Value of right margin.
    mutable pp_margin : int;
    /// Minimal space left before margin, when opening a block.
    mutable pp_min_space_left : int;
    /// Maximum value of indentation: no blocks can be opened further.
    m
Download .txt
gitextract__62fiqaz/

├── .dir-locals.el
├── .github/
│   ├── pull_request_template.md
│   └── workflows/
│       └── test.yml
├── .gitignore
├── CHANGELOG.md
├── Eldev
├── ISSUE_TEMPLATE.md
├── LICENSE
├── README.org
├── eglot-fsharp.el
├── fsharp-mode-font.el
├── fsharp-mode-structure.el
├── fsharp-mode-util.el
├── fsharp-mode.el
├── inf-fsharp-mode.el
└── test/
    ├── CompileCommandData/
    │   ├── Directory With Spaces/
    │   │   ├── noproj/
    │   │   │   └── test.fs
    │   │   └── proj/
    │   │       ├── test.fs
    │   │       └── test.fsproj
    │   ├── noproj/
    │   │   └── test.fs
    │   └── proj/
    │       ├── Makefile
    │       ├── test.fs
    │       └── test.fsproj
    ├── FindSlnData/
    │   ├── bar.sln
    │   ├── noproj/
    │   │   └── test.fs
    │   ├── sln/
    │   │   └── foo.sln
    │   └── test.fsproj
    ├── StructureTest/
    │   ├── Blocks.fs
    │   ├── BracketIndent.fs
    │   ├── ContinuationLines.fs
    │   ├── Literals.fs
    │   ├── Nesting.fs
    │   └── Relative.fs
    ├── Test1/
    │   ├── Error.fs
    │   ├── FileTwo.fs
    │   ├── NoProject.fs
    │   ├── Pervasive.fs
    │   ├── Program.fs
    │   ├── Script.fsx
    │   └── Test1.fsproj
    ├── Test2/
    │   ├── Main.fs
    │   └── Test2.fsproj
    ├── apps/
    │   ├── FQuake3/
    │   │   ├── NativeMappings.fs
    │   │   └── NativeMappings.fs.faceup
    │   ├── FSharp.Compatibility/
    │   │   ├── Format.fs
    │   │   └── Format.fs.faceup
    │   └── RecordHighlighting/
    │       ├── Test.fsx
    │       └── Test.fsx.faceup
    ├── eglot-fsharp-integration-util.el
    ├── expression.fsx
    ├── fsharp-mode-font-tests.el
    ├── fsharp-mode-structure-tests.el
    ├── fsi-tests.el
    ├── integration-tests.el
    └── nuget.fsx
Condensed preview — 54 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (345K chars).
[
  {
    "path": ".dir-locals.el",
    "chars": 173,
    "preview": ";;; Directory Local Variables\n;;; For more information see (info \"(emacs) Directory Variables\")\n\n((emacs-lisp-mode . ((i"
  },
  {
    "path": ".github/pull_request_template.md",
    "chars": 268,
    "preview": "## Description\n\n<!-- Add a small description of the PR that you're submitting -->\n\n## How to test\n\n<!-- Please describe "
  },
  {
    "path": ".github/workflows/test.yml",
    "chars": 2064,
    "preview": "name: \"CI\"\non:\n  pull_request:\n  push:\n    branches:\n      - master\njobs:\n  gnu-build:\n    strategy:\n      fail-fast: fa"
  },
  {
    "path": ".gitignore",
    "chars": 141,
    "preview": "*.elc\nbin\ntmp\n*~\n\n# Useful for doing releases\nemacs-fsharp-mode-bin/\n\n# Dependency archive\nfsautocomplete-*.zip\n\n# Devel"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 9447,
    "preview": "## 1.10 (2019.12-01)\n\nFeatures:\n  - #210: Remove old FsAutoComplete support (use LSP)\n\t- provide eglot (Emacs LSP client"
  },
  {
    "path": "Eldev",
    "chars": 339,
    "preview": "; -*- mode: emacs-lisp; lexical-binding: t -*-\n\n(setq package-lint-main-file \"eglot-fsharp.el\")\n(setq eldev-project-main"
  },
  {
    "path": "ISSUE_TEMPLATE.md",
    "chars": 595,
    "preview": "\n### Description\n\nPlease provide a succinct description of your issue.\n\n### Repro steps\n\nPlease provide the steps requir"
  },
  {
    "path": "LICENSE",
    "chars": 11358,
    "preview": "\n                                 Apache License\n                           Version 2.0, January 2004\n                  "
  },
  {
    "path": "README.org",
    "chars": 7543,
    "preview": "[[http://melpa.org/#/fsharp-mode][file:http://melpa.org/packages/fsharp-mode-badge.svg]]\n[[https://stable.melpa.org/#/fs"
  },
  {
    "path": "eglot-fsharp.el",
    "chars": 11019,
    "preview": ";;; eglot-fsharp.el --- fsharp-mode eglot integration                     -*- lexical-binding: t; -*-\n\n;; Copyright (C) "
  },
  {
    "path": "fsharp-mode-font.el",
    "chars": 18677,
    "preview": ";;; fsharp-mode-font.el --- Syntax highlighting for F#\n\n;; Copyright (C) 1997 INRIA\n\n;; Author: 1993-1997 Xavier Leroy, "
  },
  {
    "path": "fsharp-mode-structure.el",
    "chars": 73476,
    "preview": ";;; fsharp-mode-indent.el --- Stucture Definition, Mark, and Motion for F#\n\n;; Copyright (C) 2010 Laurent Le Brun\n\n;; Au"
  },
  {
    "path": "fsharp-mode-util.el",
    "chars": 3034,
    "preview": ";;; fsharp-mode-util.el --- utility functions -*- lexical-binding: t -*-\n\n;; Copyright (C) 2015 Robin Neatherway\n\n;; Aut"
  },
  {
    "path": "fsharp-mode.el",
    "chars": 14405,
    "preview": ";;; fsharp-mode.el --- Support for the F# programming language\n\n;; Copyright (C) 1997 INRIA\n\n;; Author: 1993-1997 Xavier"
  },
  {
    "path": "inf-fsharp-mode.el",
    "chars": 7796,
    "preview": ";;; inf-fsharp-mode.el --- Support for F# interactive\n\n;; Copyright (C) 1997 INRIA\n\n;; Author: 1993-1997 Xavier Leroy, J"
  },
  {
    "path": "test/CompileCommandData/Directory With Spaces/noproj/test.fs",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/CompileCommandData/Directory With Spaces/proj/test.fs",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/CompileCommandData/Directory With Spaces/proj/test.fsproj",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/CompileCommandData/noproj/test.fs",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/CompileCommandData/proj/Makefile",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/CompileCommandData/proj/test.fs",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/CompileCommandData/proj/test.fsproj",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/FindSlnData/bar.sln",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/FindSlnData/noproj/test.fs",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/FindSlnData/sln/foo.sln",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/FindSlnData/test.fsproj",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/StructureTest/Blocks.fs",
    "chars": 219,
    "preview": "let notABlock = 5\n\nlet basicBlock =\n    [ 1; 2; 3 ]\n    |> List.fold (fun x y -> x + y)\n\ntype Shape =\n    | Square\n    |"
  },
  {
    "path": "test/StructureTest/BracketIndent.fs",
    "chars": 244,
    "preview": "let formatOne = [ \"this\"\n                  \"that\"\n                  \"the-other\"\n\n                              ]\n\nlet fo"
  },
  {
    "path": "test/StructureTest/ContinuationLines.fs",
    "chars": 90,
    "preview": "let x = 5\nlet y =\n    [ 1; 2 ]\n    |> List.fold (fun x y -> x + y)\n\nlet z = 5 +\n        6\n"
  },
  {
    "path": "test/StructureTest/Literals.fs",
    "chars": 3016,
    "preview": "// Generated using https://hipsum.co/\n\n// I'm a longer comment! Now, with Hipster Lorem Ipsum:\n//\n// Lorem ipsum dolor a"
  },
  {
    "path": "test/StructureTest/Nesting.fs",
    "chars": 1243,
    "preview": "// This file contains hand-crafted structures for use by `fsharp-mode-structure-tests.el`.\n// In particular, many/most o"
  },
  {
    "path": "test/StructureTest/Relative.fs",
    "chars": 298,
    "preview": "type Test  =\n    | Unit\n    | Integration of string\n    | EndToEnd\n\n\nif thing <> true then\n    printfn \"thing is not tru"
  },
  {
    "path": "test/Test1/Error.fs",
    "chars": 37,
    "preview": "module Error\n\nlet x = nonexisting()\n\n"
  },
  {
    "path": "test/Test1/FileTwo.fs",
    "chars": 159,
    "preview": "module FileTwo\n\ntype Foo =\n  | Bar\n  | Qux\n\nlet addition x y = x + y\n\nlet add x y = x + y\n\ntype NewObjectType() =\n\n  mem"
  },
  {
    "path": "test/Test1/NoProject.fs",
    "chars": 39,
    "preview": "module FileTwo\n\nopen System.Collection\n"
  },
  {
    "path": "test/Test1/Pervasive.fs",
    "chars": 53,
    "preview": "let printtest args =\n    printfn \"Hello %d\" 10\n    0\n"
  },
  {
    "path": "test/Test1/Program.fs",
    "chars": 282,
    "preview": "module X =\n  let func x = x + 1\n\nlet testval = FileTwo.NewObjectType()\n\nlet val2 = X.func 2\n\nlet val3 = testval.Terrific"
  },
  {
    "path": "test/Test1/Script.fsx",
    "chars": 61,
    "preview": "\n\nmodule XA =\n  let funky x = x + 1\n\nlet val99 = XA.funky 21\n"
  },
  {
    "path": "test/Test1/Test1.fsproj",
    "chars": 342,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <OutputType>Exe</OutputType>\n    <TargetFramework>net9.0</Targ"
  },
  {
    "path": "test/Test2/Main.fs",
    "chars": 119,
    "preview": "module Test2.Main\n\nlet val2 = List.map ((+) 1) [1;2]\n\n[<EntryPoint>]\nlet main args =\n    printfn \"Hello %A\" val2\n    0\n"
  },
  {
    "path": "test/Test2/Test2.fsproj",
    "chars": 3292,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"4.0\" DefaultTargets=\"Build\" xmlns=\"http://schemas.microsof"
  },
  {
    "path": "test/apps/FQuake3/NativeMappings.fs",
    "chars": 8889,
    "preview": "(*\nCopyright (C) 2013 William F. Smith\n\nThis program is free software; you can redistribute it\nand/or modify it under th"
  },
  {
    "path": "test/apps/FQuake3/NativeMappings.fs.faceup",
    "chars": 11003,
    "preview": "«x:(*\nCopyright (C) 2013 William F. Smith\n\nThis program is free software; you can redistribute it\nand/or modify it under"
  },
  {
    "path": "test/apps/FSharp.Compatibility/Format.fs",
    "chars": 47406,
    "preview": "(*  OCaml Compatibility Library for F# (Format module)\n    (FSharp.Compatibility.OCaml.Format)\n\n    Copyright (c) 1996  "
  },
  {
    "path": "test/apps/FSharp.Compatibility/Format.fs.faceup",
    "chars": 57627,
    "preview": "«x:(*  OCaml Compatibility Library for F# (Format module)\n    (FSharp.Compatibility.OCaml.Format)\n\n    Copyright (c) 199"
  },
  {
    "path": "test/apps/RecordHighlighting/Test.fsx",
    "chars": 531,
    "preview": "type RecordTest1 = { something: int\n                     another: string }\n\ntype RecordTest2 = { something :int; another"
  },
  {
    "path": "test/apps/RecordHighlighting/Test.fsx.faceup",
    "chars": 681,
    "preview": "«k:type» «t:RecordTest1» = { something: «t:int»\n                     another: «t:string» }\n\n«k:type» «t:RecordTest2» = {"
  },
  {
    "path": "test/eglot-fsharp-integration-util.el",
    "chars": 7527,
    "preview": ";;; eglot-fsharp-integration-util.el --- Helper for eglot integration tests  -*- lexical-binding: t; -*-\n\n;; Copyright ("
  },
  {
    "path": "test/expression.fsx",
    "chars": 8,
    "preview": "1 + 1;;\n"
  },
  {
    "path": "test/fsharp-mode-font-tests.el",
    "chars": 1937,
    "preview": ";;; fsharp-mode-font-tests.el ---                         -*- lexical-binding: t; -*-\n\n(require 'buttercup)\n(require 'fs"
  },
  {
    "path": "test/fsharp-mode-structure-tests.el",
    "chars": 13511,
    "preview": ";;; fsharp-mode-structure-tests.el ---                         -*- lexical-binding: t; -*-\n\n(require 'buttercup)\n\n(requi"
  },
  {
    "path": "test/fsi-tests.el",
    "chars": 2680,
    "preview": ";;; fsi-tests.el --- Tests for F# interactive        -*- lexical-binding: t; -*-\n\n;; Copyright (C) 2022-2023  Jürgen Höt"
  },
  {
    "path": "test/integration-tests.el",
    "chars": 4504,
    "preview": ";;; integration-tests.el ---                         -*- lexical-binding: t; -*-\n\n;; Copyright (C) 2019-2023  Jürgen Höt"
  },
  {
    "path": "test/nuget.fsx",
    "chars": 145,
    "preview": "#r \"nuget: Newtonsoft.Json\";;\nopen Newtonsoft.Json;;\n\nlet o = {| X = 2; Y = \"Hello\" |};;\n\nprintfn \"xxx:%s:xxx\" (JsonConv"
  }
]

About this extraction

This page contains the full source code of the fsharp/emacs-fsharp-mode GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 54 files (318.6 KB), approximately 87.1k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!