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
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.