emacs-fsharp-mode icon indicating copy to clipboard operation
emacs-fsharp-mode copied to clipboard

Folding Support

Open jeroldhaas opened this issue 9 years ago • 18 comments

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.

jeroldhaas avatar Nov 29 '15 17:11 jeroldhaas

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?

rneatherway avatar Dec 01 '15 21:12 rneatherway

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?

jeroldhaas avatar Dec 02 '15 18:12 jeroldhaas

I'm chainging the issue to something more generalized, as it appears that folding-mode might require polluting the file with comment-based symbols.

jeroldhaas avatar Dec 02 '15 18:12 jeroldhaas

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."
  ;; ...
)

delexi avatar Mar 13 '16 10:03 delexi

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.

rneatherway avatar Mar 13 '16 21:03 rneatherway

(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 avatar Mar 14 '16 22:03 jeroldhaas

@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))

delexi avatar Mar 16 '16 19:03 delexi

@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:

  1. A defcustom to turn it off, if you do not want/need it.
  2. Keybindings for show/hide a single item and show/hide all items (which prefix would you prefer)?

delexi avatar Mar 16 '16 20:03 delexi

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.

rneatherway avatar Mar 17 '16 14:03 rneatherway

Considering spacemacs, I'd prefer it follow the evil binding for folds. Perhaps a dual binding can be done, for compatibility's sake?

jeroldhaas avatar Mar 17 '16 16:03 jeroldhaas

What are those bindings? Is there a suggested way to provide evil bindings when you create a package? I've never used evil.

rneatherway avatar Mar 17 '16 16:03 rneatherway

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 avatar Mar 17 '16 16:03 jeroldhaas

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

nosami avatar Mar 17 '16 17:03 nosami

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?

jeroldhaas avatar Mar 17 '16 17:03 jeroldhaas

I presume so.

nosami avatar Mar 17 '16 17:03 nosami

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.

Source

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.

delexi avatar Mar 18 '16 20:03 delexi

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

iNecas avatar Apr 14 '19 22:04 iNecas

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

chuckberrypi avatar Dec 12 '22 18:12 chuckberrypi