4

I'm sure emacs used to do it my way, but things appear to have changed (probably years ago, I've just been living with the pain).

I want this (to conform to corp. standards):

MULTIFORM=$(
    curl -k -A http://foo.com |
    grep -m1 multiform |
    tr '=' '\n' |
    tail -1 |
    cut -d "'" -f 2
)

But emacs-25.1 insists that this is the right way to indent:

MULTIFORM=$(
    curl -k -A http://foo.com |
        grep -m1 multiform |
        tr '=' '\n' |
        tail -1 |
        cut -d "'" -f 2
         )

... quite apart from our standards, it's ugly.

I've tried messing around with sh-learn-line-indent and it offers rules like:

close-all ")"
list-intro ";"

... but playing with them gives no joy at all.

Any ideas?

wef
  • 452
  • 2
  • 9
  • Looks like being worth a bug-report. – Andreas Röhler Nov 02 '16 at 07:12
  • You can see if the settings from this question (not the answer): http://emacs.stackexchange.com/q/26329/184 help. – T. Verron Nov 02 '16 at 08:47
  • I did read that one but it appears to be another version of emacs??? Anyway, mine doesn't have variables sh-indentation-*. And I have tried yr suggestion of moving aside .emacs .elisp.d and .config/emacs. Nothing seems to make any difference. How does other people emacs' treat the above? – wef Nov 02 '16 at 10:50
  • ... and I'll enter a bug report if I get nowhere with this - good suggestion! – wef Nov 02 '16 at 10:53

2 Answers2

4

For the indentation after the | you should be able to get the right result by going to the incorrect line (i.e. the one that starts with grep) and then do M-x smie-config-set-indent RET before "|" RET nil RET.

But smie-config is not up to the task when it comes to tweaking the behavior of the close paren in the way you want. For that you'll need actual code. One possible approach could look like:

(defun my-sh-indent-rules ()
  (when (eq ?= (char-before))
    (skip-chars-backward "[:alnum:]_=")
    (current-column)))

(add-hook 'sh-mode-hook
          (lambda ()
            (add-hook 'smie-indent-functions
                      #'my-sh-indent-rules
                      nil 'local)))

BTW, if you want to handle the | alignment "by hand" rather than via smie-config, you could do it this way:

(defun my-sh-smie-rules (orig-fun kind token)
  (pcase (cons kind token)
   (`(:before . "|") nil)
   (_ (funcall orig-fun kind token))))
(advice-add 'sh-smie-sh-rules :around #'my-sh-smie-rules)

wef: This is the final bit of the puzzle that Stefan gave me in my muddle below - how to save the customisations for next time. As he said, I needed to teach smie about my preferences eg with M-x smie-config-set-indent then run M-x smie-config-save and look at the variable smie-config. Finally, I copied those rules (in my case, just one rule) into .emacs:

(defun my-sh-indent-rules ()
  (smie-config-local '((nil :before "|" (column . 8)))))
(add-hook 'smie-indent-functions #'my-sh-indent-rules)

There may be other ways to do this eg with the commands in sh-mode eg M-x sh-learn-line-indent - but I wasn't able to get it to work.

Stefan
  • 26,154
  • 3
  • 46
  • 84
  • Hmmm - M-x smie-config-set-indent gave me choices for `Adjust rule:` of: `after ";"` and `before ";"` but it objected to: `before "|"` with `"No match"` – wef Nov 03 '16 at 00:40
  • my-sh-indent-rules appears to work!! Thank you for that. I tried upvoting the answer but I don't have reputation for that. – wef Nov 03 '16 at 00:46
  • `M-x smie-config-show-indent` on the 'grep' line shows: Rules used: : before "|" -> (column . 8), :elem basic -> 4, :after "|" -> nil – wef Nov 03 '16 at 06:06
  • The output you give for `smie-config-show-indent` looks right, so you just need to `M-x smie-config-set-indent` at the same place and set `before "|"` to `nil`. – Stefan Nov 03 '16 at 12:41
  • Hoo boy! that worked like a bought one! Thanks so much! Now I need to try and work out why I missed that - I thought I'd tried all the combinations and permutations. Obviously not! – wef Nov 04 '16 at 04:21
  • I want to put it all in my .emacs but having some problems. So I set up as above and ran `M-x smie-save-config`. `smie-config` says that its value is ((sh-mode (nil :before "|" (column . 8)))) So I tried this: (defun my-sh-indent-rules () ((sh-mode (nil :before "|" (column . 8))))) (add-hook 'sh-mode-hook 'my-sh-indent-rules) Obviously, that doesn't work. – wef Nov 04 '16 at 09:58
  • `smie-config-save` does write to your `~/.emacs`, so there's nothing more to do to "put it all in my .emacs". If you want to do it in your mode hook, then add `(smie-config-local '((nil :before "|" (column . 8))))` in there. – Stefan Nov 04 '16 at 13:38
  • Sorry to be such an elisp ingénue, but I stuck this in .emacs and now it formats all lines with zero indent! (defun my-sh-indent-rules () (smie-config-local '((nil :before "|" (column . 8))))) (add-hook 'sh-mode-hook #'my-sh-indent-rules) – wef Nov 05 '16 at 06:16
  • ... and, on my emacs-25.1, `smie-config-save` does not save to .emacs (I tried it with emacs -Q) - it saves to a variable `smie-config`. It's on fedora-24 emacs-25.1-0.2.rc1.fc24.x86_64 – wef Nov 05 '16 at 06:21
  • Duh! I finally got everything lined up - I needed this: (defun my-sh-indent-rules () (smie-config-local '((nil :before "|" (column . 8))))) (add-hook 'smie-indent-functions #'my-sh-indent-rules) Sorry for the bacndwidth. – wef Nov 05 '16 at 09:58
  • I feel I should document this somewhere - perhaps I'll just tidy everything up and add it to your answer. Hope you don't mind. – wef Nov 05 '16 at 09:59
  • Indeed `smie-config-save` "saves" to `smie-config`, but `smie-config` is then saved by `customize` (via `customize-save-customized` or somesuch). I guess it should ask the user whether to save into `~/.emacs` and call `customize` itself accordingly. Please `M-x report-emacs-bug` asking for this. As for editing my answer, please be my guest, yes. – Stefan Nov 05 '16 at 17:15
  • BTW - the '(pcase' line above should read '(pcase (cons kind token)' – wef Dec 28 '18 at 00:34
  • my 'Duh!' cos I forgot to update this entry 2 years ago! Pressure of work. – wef Dec 29 '18 at 07:28
0

The following is a broken (or incomplete) answer ... I'm trying to fix my original problem using the sh-mode calls only instead of the lower-level smie calls that Stefan was kind enough to provide, just to see if it can be done. Reading those docs and running in a fresh emacs -Q session, I did this:

M-x sh-learn-line-indent RET before "|" RET nil

The style then worked for me and indented the lines ending in "|" - but not the final ")" - I'll still need Stefan's code for that. Then:

M-x sh-name-style RET bobs
M-x sh-save-styles-to-buffer RET *scratch*

and then used that code to create this:

(defun my-sh-indent-rules ()
  (setq sh-styles-alist
        '(("bobs"
           (sh-basic-offset . 4)
           (sh-first-lines-indent . 0)
           (sh-indent-after-case . +)
           (sh-indent-after-do . +)
           (sh-indent-after-done . 0)
           (sh-indent-after-else . +)
           (sh-indent-after-if . +)
           (sh-indent-after-loop-construct . +)
           (sh-indent-after-open . +)
           (sh-indent-comment . t)
           (sh-indent-for-case-alt . ++)
           (sh-indent-for-case-label . +)
           (sh-indent-for-continuation . +)
           (sh-indent-for-do . 0)
           (sh-indent-for-done . 0)
           (sh-indent-for-else . 0)
           (sh-indent-for-fi . 0)
           (sh-indent-for-then . 0))))
  (sh-load-style "bobs"))
(add-hook 'sh-mode-hook #'my-sh-indent-rules)

... note that it has no before "|" rule. Perhaps it doesn't know how to do that?

Anyway, running that in a fresh emacs -Q session it doesn't work even after a M-x sh-load-style RET bobs, so I'm reverting to the default behaviour.

I'm happy with Stefan's smie solution above, but I'd be interested to know how to make it work with sh-mode.

wef
  • 452
  • 2
  • 9
  • I think `sh-styles-*` stuff only works if you set `sh-use-smie` to `nil`. – npostavs Dec 28 '18 at 01:51
  • @npostavs - you're right, it works now, at least for indenting the pipeline. It may need more work to get the ')' to indent correctly. – wef Dec 29 '18 at 02:49