unofficial mirror of help-gnu-emacs@gnu.org
 help / color / mirror / Atom feed
* Operate on each line in region
@ 2014-04-23 17:45 Jacob Gerlach
  2014-04-23 18:03 ` Doug Lewan
                   ` (3 more replies)
  0 siblings, 4 replies; 9+ messages in thread
From: Jacob Gerlach @ 2014-04-23 17:45 UTC (permalink / raw)
  To: help-gnu-emacs

I am writing a function that will indent each line in a region to justify equals signs:

 foo = 1
 longerfoo = bar

Would become 

       foo = 1
 longerfoo = bar

I believe I understand how to iterate through the lines in a region using either of two methods:

condition on the buffer positions for the start and end of the region:

(defun my-justify (start end)
(interactive 'r')
(while (< (point) end))
...body...
(forward-line 1)))

Narrow the buffer to region and use similar conditioning with (eobp).

I'm wondering if there is a more straightforward way to accomplish this.

Thanks for any suggestions.
Jake


^ permalink raw reply	[flat|nested] 9+ messages in thread

* RE: Operate on each line in region
  2014-04-23 17:45 Operate on each line in region Jacob Gerlach
@ 2014-04-23 18:03 ` Doug Lewan
  2014-04-23 18:05 ` Stefan Monnier
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 9+ messages in thread
From: Doug Lewan @ 2014-04-23 18:03 UTC (permalink / raw)
  To: Jacob Gerlach, help-gnu-emacs@gnu.org

Jacob,

There are a whole bunch of `align' commands that might be worth looking at.

,Douglas
Douglas Lewan
Shubert Ticketing
(201) 489-8600 ext 224

Of course, shells have one feature that no other language has: subshells.

> -----Original Message-----
> From: help-gnu-emacs-bounces+dougl=shubertticketing.com@gnu.org
> [mailto:help-gnu-emacs-bounces+dougl=shubertticketing.com@gnu.org] On
> Behalf Of Jacob Gerlach
> Sent: Wednesday, 2014 April 23 13:46
> To: help-gnu-emacs@gnu.org
> Subject: Operate on each line in region
> 
> I am writing a function that will indent each line in a region to
> justify equals signs:
> 
>  foo = 1
>  longerfoo = bar
> 
> Would become
> 
>        foo = 1
>  longerfoo = bar
> 
> I believe I understand how to iterate through the lines in a region
> using either of two methods:
> 
> condition on the buffer positions for the start and end of the region:
> 
> (defun my-justify (start end)
> (interactive 'r')
> (while (< (point) end))
> ...body...
> (forward-line 1)))
> 
> Narrow the buffer to region and use similar conditioning with (eobp).
> 
> I'm wondering if there is a more straightforward way to accomplish
> this.
> 
> Thanks for any suggestions.
> Jake



^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Operate on each line in region
  2014-04-23 17:45 Operate on each line in region Jacob Gerlach
  2014-04-23 18:03 ` Doug Lewan
@ 2014-04-23 18:05 ` Stefan Monnier
  2014-04-23 18:15 ` Pascal J. Bourguignon
       [not found] ` <mailman.20129.1398276348.10748.help-gnu-emacs@gnu.org>
  3 siblings, 0 replies; 9+ messages in thread
From: Stefan Monnier @ 2014-04-23 18:05 UTC (permalink / raw)
  To: help-gnu-emacs

> (defun my-justify (start end)
> (interactive 'r')
> (while (< (point) end))
> ...body...
> (forward-line 1)))

Just beware that as you go modify the buffer, the position that you
consider as "the end" will move as well.  You can solve this problem in
a few different ways:
- loop from end to start.
- make sure `end' is a marker rather than an integer so it will be
  automatically adjusted to keep pointing to the same logical position in
  the text.
- use narrowing (which uses similar machinery as a marker).

I personally recommend against using narrowing, unless it's *really*
much simpler.  The reason for it is that narrowing affects "everything",
so it's kind of a sledgehammer which can have unintended consequences.

> I'm wondering if there is a more straightforward way to accomplish this.

Not sure what "more straightforward" would look like.


        Stefan




^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Operate on each line in region
  2014-04-23 17:45 Operate on each line in region Jacob Gerlach
  2014-04-23 18:03 ` Doug Lewan
  2014-04-23 18:05 ` Stefan Monnier
@ 2014-04-23 18:15 ` Pascal J. Bourguignon
       [not found] ` <mailman.20129.1398276348.10748.help-gnu-emacs@gnu.org>
  3 siblings, 0 replies; 9+ messages in thread
From: Pascal J. Bourguignon @ 2014-04-23 18:15 UTC (permalink / raw)
  To: help-gnu-emacs

Jacob Gerlach <jacobgerlach@gmail.com> writes:

> I am writing a function that will indent each line in a region to justify equals signs:
>
>  foo = 1
>  longerfoo = bar
>
> Would become 
>
>        foo = 1
>  longerfoo = bar
>
> I believe I understand how to iterate through the lines in a region using either of two methods:
>
> condition on the buffer positions for the start and end of the region:
>
> (defun my-justify (start end)
> (interactive 'r')
> (while (< (point) end))
> ...body...
> (forward-line 1)))
>
> Narrow the buffer to region and use similar conditioning with (eobp).
>
> I'm wondering if there is a more straightforward way to accomplish this.

Good question.

If there's not, you should do it; it's trivial:

(defun call-with-region-narrowed-to-each-line-in-region (start end thunk)
  (save-excursion
   (save-restriction
    (narrow-to-region start end)
    (dolines (lstart lend)
             (save-restriction
              (narrow-to-region lstart lend)
              (funcall thunk))))))


(defmacro with-region-narrowed-to-each-line-in-region (start end &rest body)
  `(call-with-region-narrowed-to-each-line-in-region ,start ,end (lambda () ,@body)))


(with-temp-buffer
    (let ((data '("deux" "trois" "quatre")))
      (insert "1 one
2 two
3 three
4 four
5 five
")
      (goto-char (point-min))
      (forward-line 1)
      (block nil
        (let ((start (point)))
          (goto-char (point-max))
          (forward-line -1)
          (let ((end (point)))
            (with-region-narrowed-to-each-line-in-region start end
              (let ((old (split-string (buffer-substring (point-min) (point-max))  " ")))
                (message "%S" (list  (point-min) (point-max) old))
                (delete-region (point-min) (point-max))
                (insert (format ":%s:%s" (first old) (pop data))))))))
      (buffer-substring (point-min) (point-max))))

"1 one
:2:deux
:3:trois
:4:quatre
5 five
"


You can find dolines in: 

https://gitorious.org/com-informatimago/emacs/source/b58a0a336b46f3523700931117b409307b13d9b0:pjb-emacs.el#L2262

Notice however, that the implementation of dolines doesn't allow you to
insert new lines in the processed lines, since this will lead to an
infinite loop.  It could be modified to skip to the marker to the end of
the old line.
 
-- 
__Pascal Bourguignon__
http://www.informatimago.com/
"Le mercure monte ?  C'est le moment d'acheter !"


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Operate on each line in region
       [not found] ` <mailman.20129.1398276348.10748.help-gnu-emacs@gnu.org>
@ 2014-04-23 19:06   ` Barry Margolin
  2014-04-23 19:18     ` Jacob Gerlach
  0 siblings, 1 reply; 9+ messages in thread
From: Barry Margolin @ 2014-04-23 19:06 UTC (permalink / raw)
  To: help-gnu-emacs

In article <mailman.20129.1398276348.10748.help-gnu-emacs@gnu.org>,
 Stefan Monnier <monnier@iro.umontreal.ca> wrote:

> Not sure what "more straightforward" would look like.

One could imagine that since this is a fairly common need, there might 
be a macro like with-region or with-region-lines that already does it.

-- 
Barry Margolin, barmar@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Operate on each line in region
  2014-04-23 19:06   ` Barry Margolin
@ 2014-04-23 19:18     ` Jacob Gerlach
  2014-04-23 23:23       ` Jacob Gerlach
  0 siblings, 1 reply; 9+ messages in thread
From: Jacob Gerlach @ 2014-04-23 19:18 UTC (permalink / raw)
  To: help-gnu-emacs

> One could imagine that since this is a fairly common need, there might 
> 
> be a macro like with-region or with-region-lines that already does it.

This is exactly what I was imagining, I just didn't know it.

In any case, align-regexp seems to be the most straightforward of all, since it seems to be able to do exactly what I need. I haven't worked out the syntax yet to insert the white space at the beginning of the line instead of after the text ('foo' or 'longerfoo'), but I'm working on that part!


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Operate on each line in region
  2014-04-23 19:18     ` Jacob Gerlach
@ 2014-04-23 23:23       ` Jacob Gerlach
  2014-04-23 23:44         ` Barry Margolin
  0 siblings, 1 reply; 9+ messages in thread
From: Jacob Gerlach @ 2014-04-23 23:23 UTC (permalink / raw)
  To: help-gnu-emacs

I'm having more trouble figuring this out that I expected.

I tried building a function around align-regexp:

(defun my-justify-equals (start end)
  "Indents current region to justify equals signs"
  (interactive 'r')
  (align-regexp start end "\\([ \t]*\\)\\(.*\\)=" 1 nil nil))

But when I attempt to evaluate the defun I get 

    Invalid read syntax: ")"

In any case, I still can't get the right result using  align-regexp interactively.

I tried "\([ \t]*\)\(.*\)=" as my regexp, with 1 for 'group' and 'spacing.' This inserts a tab character at the beginning of the line...

After reviewing the documentation more closely, perhaps align regexp doesn't do what I need. The description for group states:

The "alignment character" is always the first character immediately following this parenthesis group.

However, I want a different behavior, where the alignment character is later in the string, so it seems that what I want to do isn't actually possible with align-regexp.

So I tried something more in line with my original idea, except working from the end to the beginning as Stefan suggested:

(defun my-justify-equals (start end)
  "Indents current region to justify equals signs"
  (interactive 'r')
  (save-excursion
    (goto-char end)
    (let (left-length)
      (save-excursion
	(while (> (point) start)
	  (if (re-search-forward "^[ \t]*\\(.*\\)=" (line-end-position) t)
	      (when (> (length (match-string-no-properties 1)) left-length)
		(setq left-length (length (match-string-no-properties 1)))))
	  (forward-line -1)))
      (goto-char end)
      (while (> (point) start)
	(if (re-search-forward "^[ \t]*\\(.*\\)=" (line-end-position) t)
	    (indent-line-to (+ tab-width (length (match-string-no-properties 1))))
	  (indent-line-to tab-width))
	(forward-line -1)))))

However, I can't test this. Attempting to evaluate the defun gives "Invalid read syntax ")". I followed the instructions at http://www.gnu.org/software/emacs/manual/html_node/elisp/Syntax-Errors.html, but was unable to identify an error.

What am I missing?

Thanks,
Jake


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Operate on each line in region
  2014-04-23 23:23       ` Jacob Gerlach
@ 2014-04-23 23:44         ` Barry Margolin
  2014-04-26  3:02           ` Jacob Gerlach
  0 siblings, 1 reply; 9+ messages in thread
From: Barry Margolin @ 2014-04-23 23:44 UTC (permalink / raw)
  To: help-gnu-emacs

In article <02d45a30-ac9e-46a1-8f7b-1e3ce651de00@googlegroups.com>,
 Jacob Gerlach <jacobgerlach@gmail.com> wrote:

> I'm having more trouble figuring this out that I expected.
> 
> I tried building a function around align-regexp:
> 
> (defun my-justify-equals (start end)
>   "Indents current region to justify equals signs"
>   (interactive 'r')
>   (align-regexp start end "\\([ \t]*\\)\\(.*\\)=" 1 nil nil))
> 
> But when I attempt to evaluate the defun I get 
> 
>     Invalid read syntax: ")"

You have the wrong type of quotes around the interactive string, it 
should be:

    (interactive "r")

-- 
Barry Margolin, barmar@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Operate on each line in region
  2014-04-23 23:44         ` Barry Margolin
@ 2014-04-26  3:02           ` Jacob Gerlach
  0 siblings, 0 replies; 9+ messages in thread
From: Jacob Gerlach @ 2014-04-26  3:02 UTC (permalink / raw)
  To: help-gnu-emacs

Took some more tweaking, but I finally got this working. Comments or recommendations welcome. I was never able to figure out if align-regexp could do what I needed.

Figured I'd post the final function:

(defun my-justify-equals (start end)
  "Indents current region to justify equals signs.
   Indents lines with no equals sign to default tab    
   width"
  (interactive "r")
  (save-excursion
    (goto-char end)
    (goto-char (line-beginning-position))
    (let ((left-length 0))
      (save-excursion
				(while (>= (point) start)
					(if (re-search-forward "[ \t]*\\(.*\\)=" (line-end-position) t)
							(when (> (length (match-string-no-properties 1)) left-length)
								(setq left-length (length (match-string-no-properties 1)))))
					(forward-line -1)))
      (goto-char end)
			(goto-char (line-beginning-position))
      (while (>= (point) start)
				(if (re-search-forward "^[ \t]*\\(.*\\)=" (line-end-position) t)
						(indent-line-to (+ tab-width (- left-length (length (match-string-no-properties 1)))))
					(indent-line-to tab-width))
				(forward-line -1)))))


^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2014-04-26  3:02 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-04-23 17:45 Operate on each line in region Jacob Gerlach
2014-04-23 18:03 ` Doug Lewan
2014-04-23 18:05 ` Stefan Monnier
2014-04-23 18:15 ` Pascal J. Bourguignon
     [not found] ` <mailman.20129.1398276348.10748.help-gnu-emacs@gnu.org>
2014-04-23 19:06   ` Barry Margolin
2014-04-23 19:18     ` Jacob Gerlach
2014-04-23 23:23       ` Jacob Gerlach
2014-04-23 23:44         ` Barry Margolin
2014-04-26  3:02           ` Jacob Gerlach

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).