Skip to content
New issue

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

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Preserve indentation based on the beginning of the buffer #6

Open
Fuco1 opened this issue Oct 24, 2017 · 7 comments
Open

Preserve indentation based on the beginning of the buffer #6

Fuco1 opened this issue Oct 24, 2017 · 7 comments

Comments

@Fuco1
Copy link

Fuco1 commented Oct 24, 2017

Let's say I'm editing a "json" region inside a yaml file

/SearchMp:
  post:
    description: Search resource meters at given address
    body:
      application/json:
        type: !include ../lib/search/schema.json
        example: |
          {
              "postalcode": "1011AB",
              "number": 105,
              "status": {
                  "attributes": {
                      "customerType": {
                          "value": "individual"
                      }
                  }
              }
          }

I would like to select the example, edit it and then have it aligned like in the example above. Instead, the body is aligned like in the indirect buffer, which is to say to the very left margin.

org-mode can do this properly so I think it would be possible to steal some code from there, but in either case shouldn't be very difficult. I might try to hack a PR together, would this be accepted?

@Fuco1
Copy link
Author

Fuco1 commented Oct 24, 2017

Well, I've fixed it with this code

(defun my-after-indirect-edit-realign (beg end)
  (save-excursion
    (goto-char beg)
    (let ((cc (current-column))
          (end-marker (set-marker (make-marker) end)))
      (while (< (progn
                  (forward-line)
                  (point)) end-marker)
        (line-beginning-position)
        (insert (make-string cc 32)))
      (when indent-tabs-mode
        (tabify beg end-marker))
      (set-marker end-marker nil))))

(add-hook 'edit-indirect-after-commit-functions 'my-after-indirect-edit-realign)

Might get bundled along for people to enable or not.

@Fanael
Copy link
Owner

Fanael commented Oct 24, 2017

I might try to hack a PR together, would this be accepted?

Sure.

@vincentbernat
Copy link

I am using this for similar effect:

(require 's)
(require 'dash)

(defvar edit-indirect--left-margin 0)

(defun vbe:compute-left-margin (code)
  "Compute left margin of a string of code."
  (-min
   (-map #'(lambda (line) (length (car (s-match "^\\s-*" line))))
         (-remove 's-blank? (s-lines code)))))

(defun vbe:after-indirect-edit-remove-left-margin ()
  "Remove left-margin and save it into a local variable."
  (let ((lm (vbe:compute-left-margin (buffer-substring (point-min) (point-max)))))
    (indent-rigidly (point-min) (point-max) (* -1 lm))
    (setq-local edit-indirect--left-margin lm)))

(defun vbe:after-indirect-edit-restore-left-margin ()
  "Restore left-margin before commiting."
  (indent-rigidly (point-min) (point-max) edit-indirect--left-margin))

(add-hook 'edit-indirect-after-creation-hook #'vbe:after-indirect-edit-remove-left-margin)
(add-hook 'edit-indirect-before-commit-hook #'vbe:after-indirect-edit-restore-left-margin)

@ecraven
Copy link

ecraven commented Mar 1, 2022

@vincentbernat thanks, this works very well! May I suggest adding

(put 'edit-indirect--left-margin 'permanent-local t) ;; don't lose this setting when another major mode is started

to keep the value even if another major-mode is started.

@amake
Copy link

amake commented Oct 19, 2022

I was looking for something similar to this and was working on my own when I hit a wall:

It's easy to trim the indent in the *-after-creation-hook and restore it in *-before-commit-hook, but that doesn't play well with edit-indirect-save: the indent will be restored every time you save, but it is never trimmed after creation, leading to multiply stacked indents.

There doesn't seem to be a good way to re-trim after saving; it seems an edit-indirect-after-commit-hook is called for. Would you consider accepting a PR for that?

@amake
Copy link

amake commented Oct 19, 2022

I should note that my trimming needs are a more complex version of @Fuco1's described above: I have a JavaScript application where I am often writing SQL in a string:

function doQuery() {
  ...
  const query = `
    SELECT
      foo,
      bar,
    FROM table 
    WHERE condition1
    AND condition2
    ORDER BY something DESC
  `;
  ...
}

I use thing-at-point-bounds-of-string-at-point to grab the whole string (from ` to `) and indirectly edit that; then I trim leading and trailing blank lines, and only then do I remove the longest common indent prefix. I retain the actual strings to restore, rather than a length.

I think this algorithm should be generally useful, but maybe there are use cases it doesn't handle well.

Edit: Here is my implementation:
https://github.com/amake/.emacs.d/blob/ddb84ffa5cd6d4cda378e6e59a5e4cc273b6446d/lisp/edit-string.el

And configuration:
https://github.com/amake/.emacs.d/blob/ddb84ffa5cd6d4cda378e6e59a5e4cc273b6446d/init.el#L1333-L1346

@uqix
Copy link

uqix commented Dec 29, 2023

@vincentbernat thanks, this works very well! May I suggest adding

(put 'edit-indirect--left-margin 'permanent-local t) ;; don't lose this setting when another major mode is started

to keep the value even if another major-mode is started.

I was looking for something similar to this and was working on my own when I hit a wall:

It's easy to trim the indent in the *-after-creation-hook and restore it in *-before-commit-hook, but that doesn't play well with edit-indirect-save: the indent will be restored every time you save, but it is never trimmed after creation, leading to multiply stacked indents.

There doesn't seem to be a good way to re-trim after saving; it seems an edit-indirect-after-commit-hook is called for. Would you consider accepting a PR for that?

Here's my config based on @vincentbernat one with points above considered:

;; <-------------------------
;; https://github.com/Fanael/edit-indirect/issues/6#issuecomment-387945773

(require 's)
(require 'dash)

(defvar edit-indirect--left-margin 0)

(defun vbe/compute-left-margin (code)
  "Compute left margin of a string of code."
  (-min
   (-map #'(lambda (line) (length (car (s-match "^\\s-*" line))))
         (-remove 's-blank? (s-lines code)))))

(defun vbe/edit-indirect/remove-left-margin ()
  "Remove left-margin and save it into a local variable."
  (let ((lm (vbe/compute-left-margin (buffer-substring (point-min) (point-max)))))
    (indent-rigidly (point-min) (point-max) (* -1 lm))
    (setq-local edit-indirect--left-margin lm)
    ;; https://github.com/Fanael/edit-indirect/issues/6#issuecomment-1055542145
    ;; buffer-local variable whose value should not be reset when changing major modes
    (put 'edit-indirect--left-margin 'permanent-local t)))

(defun vbe/edit-indirect/restore-left-margin ()
  "Restore left-margin before commiting."
  (indent-rigidly (point-min) (point-max) edit-indirect--left-margin))

(add-hook 'edit-indirect-after-creation-hook #'vbe/edit-indirect/remove-left-margin)
(add-hook 'edit-indirect-before-commit-hook #'vbe/edit-indirect/restore-left-margin)

(require 'edit-indirect)
;; https://github.com/Fanael/edit-indirect/issues/6#issuecomment-1284144173
(define-key edit-indirect-mode-map [remap save-buffer] #'edit-indirect-commit)
;; >-------------------------

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants