emacs-fsharp-mode
emacs-fsharp-mode copied to clipboard
Folding Support
I may not fully understand the workings of the folding
minor mode, and how it interacts with other modes, however this one may be an easy addition:
Documentation on folding
If someone could address / inform me of how this works with other modes (I'm just starting to work with spacemacs), I'd be happy to work on a PR for this issue.
I think that as a minor mode it should "just work" alongside other modes. What behaviour do you see if you try to enable it in fsharp-mode?
Folding error: mode is not in 'folding-mode-marks-alist'
Is there another package that should be used instead to support folds in fsharp-mode?
I'm chainging the issue to something more generalized, as it appears that folding-mode might require polluting the file with comment-based symbols.
For a start, we could use outline-minor-mode with
(setq outline-regexp "\\( *module \\)\\|\\( *type \\)\\|\\( *let \\)")
and some sensible key bindings.
Edit: And if we can get scope information from FsAC, using outline-flag-region
is the way to go, I suppose:
(defun outline-flag-region (from to flag)
"Hide or show lines from FROM to TO, according to FLAG.
If FLAG is nil then text is shown, while if FLAG is t the text is hidden."
;; ...
)
Interesting. It probably would be possible to get scope information, but your suggested regex would get us 99.9% of the way there so I don't think it's really worth it.
(setq outline-regexp "\\( *module \\)\\|\\( *type \\)\\|\\( *let \\)")
Forgive my ignorance: where would this above line be used in, for example, a .spacemacs
or .emacs
config file?
@jeroldhaas I put it into my .spacemacs inside an eval-after-load form like this:
(with-eval-after-load 'fsharp-mode
(defun my-setup-outline-mode-for-fsharp-mode ()
(setq-local outline-regexp "\\( *module \\)\\|\\( *type \\)\\|\\( *let \\)")
(outline-minor-mode))
(add-hook 'fsharp-mode-hook 'my-setup-outline-mode-for-fsharp-mode))
@rneatherway I would like to integrate this into fsharp-mode, maybe with a regex a bit more sophisticated than the one above.
I would like your input regarding these two points:
- A
defcustom
to turn it off, if you do not want/need it. - Keybindings for show/hide a single item and show/hide all items (which prefix would you prefer)?
Sounds good. Do you want to turn on outline-minor-mode
by default? I'm not sure if that will surprise people or not. Perhaps if we can demonstrate that it works well in practice, and the default view has everything visible then it is OK. Either way, I think a defcustom
is a good way to go.
I found this page https://www.gnu.org/software/emacs/manual/html_node/emacs/Foldout.html on the topic of hiding and showing. Do those keybindings work already? I'm not convinced they are the best.
Considering spacemacs, I'd prefer it follow the evil binding for folds. Perhaps a dual binding can be done, for compatibility's sake?
What are those bindings? Is there a suggested way to provide evil bindings when you create a package? I've never used evil.
I'm not clear on standards for evil bindings.
-
z a
- toggle current fold -
z o
- open fold -
z c
- close fold -
z O
- open folds recursively in fold branch -
z C
- close folds recursively in fold branch -
z M
- close all folds in buffer
See the vim documentation on folds for more, and likely more accurate, info.
@jeroldhaas Evil keys are already set up to do code folding in spacemacs.
Those keys should just work providing that folding is specified for either the major mode or one of the minor modes.
Lovely, thanks for the information, @nosami - that would imply that spacemacs already has its set of bindings for helm and the regular emacs commands for outline-minor-mode
. So, then, all that's needed is to configure the regex in fsharp-mode
and have the minor mode start up along with it?
I presume so.
I checked, evil already works with outline-(minor-)mode (among others).
I looked into implementing this and stumbled over another package, that has a nicer implementation IMHO. The mode is called origami (https://github.com/gregsexton/origami.el). Here is a rough overview of its structure:
The origami model looks something like this:
A parser constructs a fold tree. This tree is then compared to the previous tree and the differences are applied to the buffer. Much like how React works with a 'virtual dom'. Since we have this intermediate data structure, you can query it and manipulate it very easily without worrying about scanning through a buffer looking for overlays or whatever.
By default, origami uses indentation based folding, which should play quite nice with FSharp. And the parser mentioned in the above quote is easily configurable, such that we can plug in one, that asks FsAC for scoping information or something like that. (I am not really sure what FsACs capabilities are and what exactly is needed to get good folding support.)
@rneatherway Would it be ok to depend on this mode?
PS: evil has not (yet) integrated origami, AFAICT. But evil support is as easy as mapping the vim fold commands to their counter parts in origami, which is done in evil-fold-list
.
Came across this issue when setting folding for me. FYI this worked best so far for me:
(defun cfg-outline-indent-level ()
"determine outline level based on indentation"
(let (buffer-invisibility-spec)
(save-excursion
(beginning-of-line)
(skip-chars-forward " ")
(+ 1 (current-column)))))
(defun cfg-fsharp-enable-folding ()
"enables folding for fsharp"
(setq-local outline-regexp "^ *\\(\\w\\|/\\)")
(setq-local outline-level #'cfg-outline-indent-level)
(outline-minor-mode 1))
(defun cfg-fsharp ()
"Configure fsharp environment"
(with-eval-after-load 'fsharp-mode
(add-hook 'fsharp-mode-hook #'cfg-fsharp-enable-folding)))
(defun cfg-outline ()
"Optional - Configure evil-fold for outline-minor-mode"
(add-to-list 'evil-fold-list '((outline-minor-mode)
:open-all outline-show-all
:close-all (lambda () (call-interactively 'outline-hide-sublevels))
:toggle outline-toggle-children
:open outline-show-children
:open-rec nil
:close outline-hide-subtree)))
Hi, just wanted to ping the maintainers on this issue. Coming to fsharp-mode from having gotten used to coding rust and clojure on eMacs, I can say that not having code folding work right out of the box is a major drag. Once I have a function working, I typically want to hide its implementation on the screen. I'm finding myself doing a lot of scrolling up to remember how I named a particular function when I'm composing a new one out of a series of piped commands.
As a DoomEmacs user, I'd love it if fsharp-mode had the same evil bindings mentioned above.
Thanks!!