Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

Already on GitHub? Sign in to your account

How to configure headings? #7

Open
thblt opened this Issue Sep 10, 2017 · 8 comments

Comments

Projects
None yet
2 participants

thblt commented Sep 10, 2017

I'm sorry if the answer to this question is obvious, but I'm completely stuck trying to make Outshine use my headings definition instead of the ones it computes. From emacs -q in a Literate Haskell buffer, I'm trying this:

(defun init-literate-haskell ()
  (interactive)
  (literate-haskell-mode)
  (setq outline-regexp (rx (one-or-more "="))
	outline-heading-alist (thblt/mk-outline-heading-alist "" ?= ""))
  (outline-minor-mode -1) ;; "Aggressively" reset the mode
  (outline-minor-mode)
  (outshine-hook-function))

This works perfectly well if I comment out outshine-hook-function (except for font-locking which for some reason doesn't work with just Outline), but when calling the hook (or actually hooking it as documented, I just call it from the function for convenience), the heading delimiters seem to be computed from Outshine, and are completely wrong (eg, it makes -- * the level 1 delimiter, which is meaningless in Literate Haskell)

Thanks!

Owner

alphapapa commented Sep 11, 2017

Have you tried putting:

 (setq outline-regexp (rx (one-or-more "="))
	outline-heading-alist (thblt/mk-outline-heading-alist "" ?= ""))

At the end of the function?

thblt commented Sep 11, 2017

Sorry about the original issue, I guess I sent it after struggling with this for too long, and didn't even notice it included a call to thblt/mk-outline-heading-alist without the function's definition. I've replaced the function call with its return value.

Following your advice, I tried this (with the same setq twice just to be extra sure, but removing the first one doesn't seem to change anything):

(defun init-literate-haskell ()
  (interactive)
  (literate-haskell-mode)
  (setq outline-regexp (rx (one-or-more "="))
	outline-heading-alist '(("=" . 1)
				("==" . 2)
				("===" . 3)
				("====" . 4)
				("=====" . 5)
				("======" . 6)
				("=======" . 7)
				("========" . 8)))
  (outline-minor-mode -1) ;; "Aggressively" reset the mode
  (outline-minor-mode)
  (outshine-hook-function)
  (setq outline-regexp (rx (one-or-more "="))
	outline-heading-alist '(("=" . 1)
				("==" . 2)
				("===" . 3)
				("====" . 4)
				("=====" . 5)
				("======" . 6)
				("=======" . 7)
				("========" . 8))))
  • Incorrect headings (lines starting with --*) get fontified
  • Folding them with TAB simply insert a space at position 2 (eg --- * Hello becomes - -- * Hello)
  • Outline demote fails with errors similar to backtrace 2, below.
  • Correct headings (with =) don't get fontified.
  • They seem to be partially detected: trying to fold them with TAB works once or twice, then fails with backtrace 1, below.
  • outline-promote and outline-demote fail with backtrace 2, below.

Backtrace 1

Debugger entered--Lisp error: (wrong-type-argument number-or-marker-p nil)
  max(1 nil)
  (cond (current-prefix-arg (prefix-numeric-value current-prefix-arg)) ((save-excursion (beginning-of-line) (looking-at outline-regexp)) (max 1 (funcall outline-level))) (t 1))
  (let ((toplevel (cond (current-prefix-arg (prefix-numeric-value current-prefix-arg)) ((save-excursion (beginning-of-line) (looking-at outline-regexp)) (max 1 (funcall outline-level))) (t 1)))) (hide-sublevels toplevel))
  (cond ((eq last-command (quote outline-cycle-overview)) (if outshine-cycle-silently nil (message "CONTENTS...")) (save-excursion (goto-char (point-max)) (catch (quote exit) (while (and (progn (condition-case nil ... ...) t) (looking-at outline-regexp)) (show-branches) (if (bobp) (throw (quote exit) nil)))) (if outshine-cycle-silently nil (message "CONTENTS...done"))) (setq this-command (quote outline-cycle-toc) outshine-current-buffer-visibility-state (quote contents))) ((eq last-command (quote outline-cycle-toc)) (show-all) (if outshine-cycle-silently nil (message "SHOW ALL")) (setq this-command (quote outline-cycle-showall) outshine-current-buffer-visibility-state (quote all))) (t (let ((toplevel (cond (current-prefix-arg (prefix-numeric-value current-prefix-arg)) ((save-excursion ... ...) (max 1 ...)) (t 1)))) (hide-sublevels toplevel)) (if outshine-cycle-silently nil (message "OVERVIEW")) (setq this-command (quote outline-cycle-overview) outshine-current-buffer-visibility-state (quote overview))))
  (cond ((or (and (bobp) (not outshine-org-style-global-cycling-at-bob-p)) (and (bobp) (not (outline-on-heading-p)) outshine-org-style-global-cycling-at-bob-p)) (cond ((eq last-command (quote outline-cycle-overview)) (if outshine-cycle-silently nil (message "CONTENTS...")) (save-excursion (goto-char (point-max)) (catch (quote exit) (while (and ... ...) (show-branches) (if ... ...))) (if outshine-cycle-silently nil (message "CONTENTS...done"))) (setq this-command (quote outline-cycle-toc) outshine-current-buffer-visibility-state (quote contents))) ((eq last-command (quote outline-cycle-toc)) (show-all) (if outshine-cycle-silently nil (message "SHOW ALL")) (setq this-command (quote outline-cycle-showall) outshine-current-buffer-visibility-state (quote all))) (t (let ((toplevel (cond ... ... ...))) (hide-sublevels toplevel)) (if outshine-cycle-silently nil (message "OVERVIEW")) (setq this-command (quote outline-cycle-overview) outshine-current-buffer-visibility-state (quote overview))))) ((save-excursion (beginning-of-line 1) (looking-at outline-regexp)) (outline-back-to-heading) (let ((goal-column 0) beg eoh eol eos) (save-excursion (outline-back-to-heading) (setq beg (point)) (save-excursion (outline-next-line) (setq eol (point))) (outline-end-of-heading) (setq eoh (point)) (outline-end-of-subtree) (setq eos (point))) (cond ((= eos eoh) (if outshine-cycle-silently nil (message "EMPTY ENTRY"))) ((>= eol eos) (show-entry) (show-children) (if outshine-cycle-silently nil (message "CHILDREN")) (setq this-command (quote outline-cycle-children))) ((eq last-command (quote outline-cycle-children)) (show-subtree) (if outshine-cycle-silently nil (message "SUBTREE"))) (t (hide-subtree) (if outshine-cycle-silently nil (message "FOLDED")))))) ((outline-cycle-emulate-tab) (indent-relative)) (t (outline-back-to-heading)))
  (cond ((equal arg (quote (4))) (let ((outshine-org-style-global-cycling-at-bob-p nil) (current-prefix-arg nil)) (save-excursion (goto-char (point-min)) (outline-cycle nil)))) (t (cond ((or (and (bobp) (not outshine-org-style-global-cycling-at-bob-p)) (and (bobp) (not (outline-on-heading-p)) outshine-org-style-global-cycling-at-bob-p)) (cond ((eq last-command (quote outline-cycle-overview)) (if outshine-cycle-silently nil (message "CONTENTS...")) (save-excursion (goto-char ...) (catch ... ...) (if outshine-cycle-silently nil ...)) (setq this-command (quote outline-cycle-toc) outshine-current-buffer-visibility-state (quote contents))) ((eq last-command (quote outline-cycle-toc)) (show-all) (if outshine-cycle-silently nil (message "SHOW ALL")) (setq this-command (quote outline-cycle-showall) outshine-current-buffer-visibility-state (quote all))) (t (let (...) (hide-sublevels toplevel)) (if outshine-cycle-silently nil (message "OVERVIEW")) (setq this-command (quote outline-cycle-overview) outshine-current-buffer-visibility-state (quote overview))))) ((save-excursion (beginning-of-line 1) (looking-at outline-regexp)) (outline-back-to-heading) (let ((goal-column 0) beg eoh eol eos) (save-excursion (outline-back-to-heading) (setq beg (point)) (save-excursion (outline-next-line) (setq eol ...)) (outline-end-of-heading) (setq eoh (point)) (outline-end-of-subtree) (setq eos (point))) (cond ((= eos eoh) (if outshine-cycle-silently nil ...)) ((>= eol eos) (show-entry) (show-children) (if outshine-cycle-silently nil ...) (setq this-command ...)) ((eq last-command ...) (show-subtree) (if outshine-cycle-silently nil ...)) (t (hide-subtree) (if outshine-cycle-silently nil ...))))) ((outline-cycle-emulate-tab) (indent-relative)) (t (outline-back-to-heading)))))
  outline-cycle(nil)
  (if (or (and (bobp) (not (outline-on-heading-p)) outshine-org-style-global-cycling-at-bob-p) (outline-on-heading-p)) (outline-cycle arg) (let* ((outline-minor-mode nil) (original-func (if (equal (kbd "<tab>") (kbd "TAB")) (or (key-binding (kbd "TAB")) (key-binding (kbd "TAB"))) (key-binding (kbd "TAB"))))) (condition-case nil (call-interactively original-func) (error nil))))
  (lambda (&optional arg) (interactive "P") (if (or (and (bobp) (not (outline-on-heading-p)) outshine-org-style-global-cycling-at-bob-p) (outline-on-heading-p)) (outline-cycle arg) (let* ((outline-minor-mode nil) (original-func (if (equal (kbd "<tab>") (kbd "TAB")) (or (key-binding ...) (key-binding ...)) (key-binding (kbd "TAB"))))) (condition-case nil (call-interactively original-func) (error nil)))))(nil)
  funcall-interactively((lambda (&optional arg) (interactive "P") (if (or (and (bobp) (not (outline-on-heading-p)) outshine-org-style-global-cycling-at-bob-p) (outline-on-heading-p)) (outline-cycle arg) (let* ((outline-minor-mode nil) (original-func (if (equal (kbd "<tab>") (kbd "TAB")) (or (key-binding ...) (key-binding ...)) (key-binding (kbd "TAB"))))) (condition-case nil (call-interactively original-func) (error nil))))) nil)
  call-interactively((lambda (&optional arg) (interactive "P") (if (or (and (bobp) (not (outline-on-heading-p)) outshine-org-style-global-cycling-at-bob-p) (outline-on-heading-p)) (outline-cycle arg) (let* ((outline-minor-mode nil) (original-func (if (equal (kbd "<tab>") (kbd "TAB")) (or (key-binding ...) (key-binding ...)) (key-binding (kbd "TAB"))))) (condition-case nil (call-interactively original-func) (error nil))))) nil nil)
  command-execute((lambda (&optional arg) (interactive "P") (if (or (and (bobp) (not (outline-on-heading-p)) outshine-org-style-global-cycling-at-bob-p) (outline-on-heading-p)) (outline-cycle arg) (let* ((outline-minor-mode nil) (original-func (if (equal (kbd "<tab>") (kbd "TAB")) (or (key-binding ...) (key-binding ...)) (key-binding (kbd "TAB"))))) (condition-case nil (call-interactively original-func) (error nil))))))

Backtrace 2

Debugger entered--Lisp error: (wrong-type-argument number-or-marker-p nil)
  >=(nil 1000)
  outline-static-level-p(nil)
  (cond ((= delta 0) t) ((outline-static-level-p level) t) ((null headlist) nil) ((consp (car headlist)) (or (car (rassoc (+ delta level) headlist)) (and atom (> (+ delta level) 0) (make-string (+ delta level) atom)))) (t (let* ((l (length headlist)) (n1 (- l (length (member head headlist)))) (n2 (+ delta n1))) (cond ((= n1 l) nil) ((< n2 0) nil) ((>= n2 l) nil) ((let* ((tail ...) (nilpos ...)) (< nilpos delta)) nil) (t (nth n2 headlist))))))
  (let* ((head (outline-cleanup-match (match-string 0))) (level (save-excursion (beginning-of-line 1) (funcall outline-level))) (newhead (cond ((= delta 0) t) ((outline-static-level-p level) t) ((null headlist) nil) ((consp (car headlist)) (or (car (rassoc ... headlist)) (and atom (> ... 0) (make-string ... atom)))) (t (let* ((l ...) (n1 ...) (n2 ...)) (cond (... nil) (... nil) (... nil) (... nil) (t ...))))))) (if (not newhead) (error "Cannot shift level %d heading \"%s\" to level %d" level head (+ level delta))) (if (and (not test) (stringp newhead)) (save-excursion (beginning-of-line 1) (or (looking-at (concat "[ 	]*\\(" (regexp-quote head) "\\)")) (error "Please contact maintainer")) (replace-match (outline-cleanup-match newhead) t t nil 1))))
  outline-change-heading((("-- * " . 1) ("-- ** " . 2) ("-- *** " . 3) ("-- **** " . 4) ("-- ***** " . 5) ("-- ****** " . 6) ("-- ******* " . 7) ("-- ******** " . 8)) 1 nil test)
  (while (re-search-forward re end t) (outline-change-heading headlist delta atom (quote test)))
  (let (head newhead level newlevel static) (goto-char beg) (while (re-search-forward re end t) (outline-change-heading headlist delta atom (quote test))) (goto-char beg) (while (re-search-forward re end t) (outline-change-heading headlist delta atom)))
  (save-excursion (if transmode (setq beg (min (point) (mark)) end (max (point) (mark))) (outline-back-to-heading) (setq beg (point)) (outline-end-of-heading) (outline-end-of-subtree) (setq end (point))) (setq beg (move-marker (make-marker) beg) end (move-marker (make-marker) end)) (let (head newhead level newlevel static) (goto-char beg) (while (re-search-forward re end t) (outline-change-heading headlist delta atom (quote test))) (goto-char beg) (while (re-search-forward re end t) (outline-change-heading headlist delta atom))))
  (let* ((headlist (outline-headings-list)) (atom (outline-headings-atom headlist)) (re (concat "^" outline-regexp)) (transmode (and transient-mark-mode mark-active)) beg end) (save-excursion (if transmode (setq beg (min (point) (mark)) end (max (point) (mark))) (outline-back-to-heading) (setq beg (point)) (outline-end-of-heading) (outline-end-of-subtree) (setq end (point))) (setq beg (move-marker (make-marker) beg) end (move-marker (make-marker) end)) (let (head newhead level newlevel static) (goto-char beg) (while (re-search-forward re end t) (outline-change-heading headlist delta atom (quote test))) (goto-char beg) (while (re-search-forward re end t) (outline-change-heading headlist delta atom)))))
  outline-change-level(1)
  (let ((delta (or arg 1))) (outline-change-level delta))
  outline-demote(1)
  funcall-interactively(outline-demote 1)
  call-interactively(outline-demote record nil)
  command-execute(outline-demote record)
  execute-extended-command(nil "outline-demote" "outline-de")
  funcall-interactively(execute-extended-command nil "outline-demote" "outline-de")
  call-interactively(execute-extended-command nil nil)
  command-execute(execute-extended-command)
Owner

alphapapa commented Sep 11, 2017

I know little about Haskell and nothing about Literate Haskell. From what I could quickly glean, it uses the regular Emacs haskell-mode.

Is there specific support for Literate Haskell in haskell-mode? If so, it should redefine the appropriate variables related to comment syntax, and I think Outshine should pick up on that. If not, then I think Outshine will continue to use the non-Literate comment syntax.

So I can think of two approaches you can try:

  1. Add support for Literate Haskell to haskell-mode, redefining the comment-syntax/outline-related variables appropriately. Or you might try copying the mode and making a literate-haskell-mode if that's simpler.
  2. Make your own version of outshine-hook-function and set your variables in it, replacing the code that sets the outshine versions of them.

Basically, AFAIK, if the major-mode correctly supports comment syntax, Outshine will too. So if you can, add support in the major-mode, and then lots of other things in Emacs will benefit from it.

thblt commented Sep 11, 2017

Literate Haskell is just regular Haskell + native compiler support for literate programming. Basically, ghc compiling a regular .hs files assume everything not explicitely marked as a comment is code (as in almost all programming languages), but when compiling a .lhs file does the exact opposite, and expects code to be marked as such. There are two ways to mark code, either with a LaTeX-like syntax:

\section{Say Hello!}

This is a Hello World in Haskell.  This line is not valid Haskell, 
but it doesn't have to be: it's treated by the compiler as a comment.

First, we declare the type of \texttt{main} (this is optional):

\begin{code}
main :: IO ()
\end{code}

Then we provide an implementation

\begin{code}
main = putStrLn "Hello, world!"
\end{code}

Or with a Markdown-like syntax

= Say Hello!

This is a Hello World in Haskell.  This line is not valid Haskell, 
but it doesn't have to be: it's treated by the compiler as a comment.

First, we declare the type of `main` (this is optional):

> main :: IO ()

Then we provide an implementation

> main = putStrLn "Hello, world!"

Both these are strict equivalents of, in regular Haskell with outline headings:

-- * Say Hello!

-- This is a Hello World in Haskell.  This line is not valid Haskell, but it doesn't have to be: it's treated by the compiler as a comment.

-- First, we declare the type of `main` (this is optional):

main :: IO ()

-- Then we provide an implementation

main = putStrLn "Hello, world!"

haskell-mode provides both the haskell-mode and literate-haskell-mode major modes, but these are different. The issue, IIUC, is that literate mode are an edge case wrt comments: headings in these modes should not start with the comment symbol, but this symbol most be defined so that the mode actually understands comments in code blocks.

Make your own version of outshine-hook-function and set your variables in it, replacing the code that sets the outshine versions of them.

I'll try that, and will submit a PR if I'm any successful. My first idea would be to update the function to introduce a new variable associating major modes with a function to define the delimiters, so edge cases like literate programming modes (or haskell-mode itself, which doesn't work out of the box), could be successfully handled without, hopefully, too much overhead. What do you think?

Owner

alphapapa commented Sep 11, 2017

haskell-mode provides both the haskell-mode and literate-haskell-mode major modes, but these are different. The issue, IIUC, is that literate mode are an edge case wrt comments: headings in these modes should not start with the comment symbol, but this symbol most be defined so that the mode actually understands comments in code blocks.

Yeah, that explains it. Outshine expects headings to be comments, so if they aren't comments in literate mode...

I'll try that, and will submit a PR if I'm any successful. My first idea would be to update the function to introduce a new variable associating major modes with a function to define the delimiters, so edge cases like literate programming modes (or haskell-mode itself, which doesn't work out of the box), could be successfully handled without, hopefully, too much overhead. What do you think?

I'm not sure if you mean to map every major mode to a function or to allow the default to be overridden. Maintaining a mapping wouldn't be good, because it would have to be kept up-to-date, and it wouldn't make use of the built-in Emacs facilities like it does now. But providing a way to override the functions for modes that require it would be great. Of course, since it's a hook function, we can't pass args to it nor bind special variables around it (well, if we call it manually we can, but not if we add it to a hook).

So I guess a customizable mapping allowing the defaults to be overridden by major-mode would be good. Or maybe the mapping should just disable the resetting of the outline- vars, that way you can just set them for outline-minor-mode like you did, and Outshine wouldn't override that.

or haskell-mode itself, which doesn't work out of the box

So non-literate haskell-mode doesn't work with Outshine by default? If so, that seems like a bug. Maybe that should be fixed first. And if that doesn't fix literate-haskell-mode, it would be good to see if there are any changes that need to be made to literate-haskell-mode first. I'm guessing that the commented-by-default style just isn't compatible with outline-minor-mode and/or Outshine by default, because Emacs isn't designed to work that way.

Well, I thought of several things as I was writing, so I hope all that made sense. :) Thanks.

thblt commented Sep 12, 2017

I'm not sure if you mean to map every major mode to a function or to allow the default to be overridden.

I was thinking of an override map only, since the default works on most cases. Something like (funcall (assoc-default major-mode 'outshine-hook-overrides 'equal 'outshine-hook-default-impl)

So non-literate haskell-mode doesn't work with Outshine by default?

It's not a bug, just a documented edge case: Haskell needs the trailing space of comment-start, but Outshine drops it. To get it to work in Haskell mode, one just needs to (setq outshine-preserve-delimiter-whitespace t). This is unrelated to the literate Haskell issue.

thblt commented Sep 12, 2017

(I think the requirement for the trailing space to be present comes from the need to avoid reducing the range of valid function names: Haskell functions names don't have to include alphanumeric characters,, so someone may want a function called, eg, --|-- --- and strange as it seems, this would actually be very idiomatic)

thblt commented Sep 12, 2017

On a related note, I'm beginning to suspect the original issue lies in outline-minor-mode, and maybe not Outshine. I'll investigate this and report back; maybe I underestimated the font lock issue as a symptom of something nastier.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment