unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* [Elisp: 8 out of 10 problems] I think the last one! (point)
@ 2024-08-11  0:14 Emanuel Berg
  2024-08-11  4:46 ` Emanuel Berg
                   ` (2 more replies)
  0 siblings, 3 replies; 13+ messages in thread
From: Emanuel Berg @ 2024-08-11  0:14 UTC (permalink / raw)
  To: emacs-devel

----------------------------------------------------------------------
Part 10:
  https://lists.gnu.org/archive/html/emacs-devel/2024-08/msg00154.html

Part  9:
  https://lists.gnu.org/archive/html/emacs-devel/2024-08/msg00380.html
----------------------------------------------------------------------

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
$ NOTE: Okay, I'll do the whole list then! No one should say, I am all %
~~~~~~. the time just complaining very vaguely, no specifics, no code, %
      % no suggestions, not taking any risks. Who came up with it? But %
      % it isn't true and this list shows it. But I'll write shorter,  %
      % christ, damn Russian novel, this. Or maybe this will be my me  %
      % last post as I've written 3 already, everyone can see that.    %
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

We have heard that in Emacs everything is done in the buffer.
This can be taken literally and figuratively. If we literally
instead say we can do a lot in the buffers, editing as well as
automatic, then everyone is cool with that, I think. But some
people have taken it too far and this is described in Part 10
above with ispell.el.

However, here is an interesting thing! If we say that is part
of who we are, we base are game on that, why don't we have
better commands for moving around in source.

Take a look again at ispell.el. That function is called
`ispell-process-line'. Man, what kind of #@&%$ lines to they
have! It is insane.

But also look closer at the code. Point is constantly moving.
Yet it is a haystack. (I removed comments.)

    (let ((word-start
            (copy-marker (+ ispell-start -1 (car (cdr poss)))))
          (word-len (length (car poss)))
          (line-end (copy-marker ispell-end))
          (line-start (copy-marker ispell-start))
          recheck-region replace)
      (goto-char word-start)
      (ispell-horiz-scroll)
      (goto-char (+ word-len word-start))
      (ispell-horiz-scroll)
      (goto-char word-start)
      (ispell-horiz-scroll)
      (or (ispell-looking-at (car poss)) ...))

So if moving around the buffer is so important to us, why
haven't we made it into an art of perfection decades ago?
TBH reading that code makes me dizzy! It is like a parody of
Elisp

    (let* ((ispell-pipe-word (car poss))
                 (actual-point (marker-position word-start))
                 (actual-line (line-number-at-pos actual-point))
                 (actual-column (save-excursion (goto-char actual-point)
                                                          (current-column))))
            (ispell-print-if-debug
                       "ispell-process-line: Ispell misalignment error:
      [Word from ispell pipe]: [%s], actual (point,line,column): (%s,%s,%s)\n"
                       ispell-pipe-word actual-point actual-line actual-column)
                      (error (concat "Ispell misalignment: word "
                                     "`%s' point %d; probably incompatible versions")
                             ispell-pipe-word actual-point))

What should we do? We have it to some degree, but more and
more consistent and short, always.

For every unit in Emacs that we know of, e.g. buffer, line,
region, paragraph, word, there should be a two sets
of functions.

  (word-beg) (word-len) (word-reg) (word-end)

So same with sentence then, as 'sent-' - "sentence-" is
too long.

  (sent-beg) (sent-len) (sent-reg) (sent-end)

This should mean _go there_

But there should be another set as well, that just reports the
position. This set can mirror the former but let's start from
the other end to avoid typos and make it easier to see.

  (pos-sent-beg) (pos-sent-len) (pos-sent-reg) (pos-sent-end)

There are good functions in Elisp, e.g. (pos-bol 0) and you
can do (pos-eol 2) to advance one line and check. Good, short,
neat and sweet. The only thing I would wish for with them, is
that they would indicate what column graphically, for just one
second, one called where it is visible..

However there is a lot of this. But that i neat, right?

It doesn't look bad but still, must be a better way than to
have every 3 out of 5 commands be about moving around point?

One example that we really have under-prioritized this, is as
simple as the most common of all, namely

  (goto-char (point-min))

But as you see that isn't optimized one bit. It is long to
type, and not atomic, it is nested even. Yuk!

Instead we should have, again, something like

(buf-beg) (buf-len) (buf-reg) (buf-end) all with implicity `goto-char'

  and

(pos-buf-beg) (pos-buf-reg) (pos-buf-end)

is my suggestion, anyway!

This is a pretty big problem, don't think it is trivial.
Who is gonna want to maintain code, that is endlessly moving
point around, in those super-long functions, very vaguely at
any point having anything to do with the interesting problems,
one thought one would solve?

Not me. Looks to boring and difficult to see the buffer
constantly in my head. When I program I don't want to think of
the buffer. But I've done all that myself as well, of course.
(I have 86 `point-min' in my source.)

So annoying thing number 8, even tho this is our game plan and
we have a lot, it doesn't seem like we have really optimized
this for speed, brevity and code clearness.

I know that some of you guys can read that fluently, cool in
a way but ... is that really interesting? Why?

No, reduce, reduce, reduce from code, with new syntax, old
syntax, smartness, new functions, less a few more lines of
Elisp, more idiomatic with libraries, and just do everything
to make the damn point stay put at least sometimes.

At the same time, more, more consistent, and _shorter_
functions to move it around.

This kind of stuff, yeah, compare to how long I type each line!

(progn (forward-paragraph -2) (when (eobp) (point)))

(How to avoid it altogether is another question, and
a good one. But here we care about the code, only.)

END OF TRANSMISSION

(goto-char (point-min))
      (while (not (eobp))
   (if (search-forward "<" nil t)
       (progn
         (forward-char -1)
         (setq result (xml-parse-tag-1 parse-dtd parse-ns))
         (cond
          ((null result)
      ;; Not looking at an xml start tag.
      (unless (eobp)
        (forward-char 1)))
          ((and xml (not xml-sub-parser))
      ;; Translation of rule [1] of XML specifications
      (error "XML: (Not Well-Formed) Only one root tag allowed"))
          ((and (listp (car result))
           parse-dtd)
      (setq dtd (car result))
      (if (cdr result)  ; possible leading comment
          (push (cdr result) xml)))
          (t
      (push result xml))))
     (goto-char (point-max))))

And now, more ispell.el:

Returns a cons cell where the `car' is sum SHIFT due to changes
in word replacements, and the `cdr' is the location of the final
word that was queried about."
  ;;(declare special ispell-start ispell-end)
  (let (poss accept-list max-word)
    (if (not (numberp shift))
   (setq shift 0))
    ;; send string to spell process and get input.
    (ispell-send-string string)
    (while (progn
        (ispell-accept-output)
        ;; Last item of output contains a blank line.
        (not (string= "" (car ispell-filter)))))
    ;; parse all inputs from the stream one word at a time.
    ;; Place in FIFO order and remove the blank item.
    (setq ispell-filter (nreverse (cdr ispell-filter)))
    (while (and (not ispell-quit) ispell-filter)
      ;; get next word, accounting for accepted words and start shifts
      (setq poss (ispell-parse-output (car ispell-filter)
                  accept-list shift))
      (if (and poss (listp poss))   ; spelling error occurred.
     ;; Whenever we have misspellings, we can change
     ;; the buffer.  Keep boundaries as markers.
     ;; Markers can move with highlighting!  This destroys
     ;; end of region markers line-end and ispell-region-end
     (let ((word-start
                 ;; There is a -1 offset here as the string is escaped
                 ;; with '^' to prevent us accidentally sending any
                 ;; ispell commands.
       (copy-marker (+ ispell-start -1 (car (cdr poss)))))
      (word-len (length (car poss)))
      (line-end (copy-marker ispell-end))
      (line-start (copy-marker ispell-start))
      recheck-region replace)
       (goto-char word-start)
       ;; Adjust the horizontal scroll & point
       (ispell-horiz-scroll)
       (goto-char (+ word-len word-start))
       (ispell-horiz-scroll)
       (goto-char word-start)
       (ispell-horiz-scroll)

       ;; Alignment cannot be tracked and this error will occur when
       ;; `query-replace' makes multiple corrections on the starting line.
       (or (ispell-looking-at (car poss))
      ;; This error occurs due to filter pipe problems
      (let* ((ispell-pipe-word (car poss))
             (actual-point (marker-position word-start))
             (actual-line (line-number-at-pos actual-point))
             (actual-column (save-excursion (goto-char actual-point)
                                                      (current-column))))
        (ispell-print-if-debug
                   "ispell-process-line: Ispell misalignment error:
  [Word from ispell pipe]: [%s], actual (point,line,column): (%s,%s,%s)\n"
                   ispell-pipe-word actual-point actual-line actual-column)
                  (error (concat "Ispell misalignment: word "
                                 "`%s' point %d; probably incompatible versions")
                         ispell-pipe-word actual-point)))
            (setq max-word (marker-position word-start))
            ;; ispell-cmd-loop can go recursive & change buffer
            (if ispell-keep-choices-win
                (setq replace (ispell-command-loop
                               (car (cdr (cdr poss)))
                               (car (cdr (cdr (cdr poss))))
                               (car poss) (marker-position word-start)
                               (+ word-len (marker-position word-start))))
              (save-window-excursion
                (setq replace (ispell-command-loop
                               (car (cdr (cdr poss)))
                               (car (cdr (cdr (cdr poss))))
                               (car poss) (marker-position word-start)
                               (+ word-len (marker-position word-start))))))

            (goto-char word-start)
            ;; Recheck when query replace edit changes misspelled word.
            ;; Error in tex mode when a potential math mode change exists.
            (if (and replace (listp replace) (= 2 (length replace)))
                (if (and (eq ispell-parser 'tex)
                         (string-match "[\\][]()[]\\|\\\\begin\\|\\$"
                                       (regexp-quote string)))
                    (error
                     "Don't start query replace on a line with math characters"
                     )
                  (set-marker line-end (point))
                  (setq ispell-filter nil
                        recheck-region t)))

            ;; Insert correction if needed.
            (cond
             ((or (null replace)
                  (equal 0 replace))   ; ACCEPT/INSERT
              (if (equal 0 replace)     ; BUFFER-LOCAL DICT ADD
                  (ispell-add-per-file-word-list (car poss)))
              ;; Do not recheck accepted word on this line.
              (setq accept-list (cons (car poss) accept-list)))
             (t            ; Replacement word selected or entered.
              (delete-region (point) (+ word-len (point)))
              (if (not (listp replace))
                  (progn
                    (insert replace)    ; Insert dictionary word.
                    (ispell-send-replacement (car poss) replace)
                    (setq accept-list (cons replace accept-list)))
                (let ((replace-word (car replace)))
                  ;; Recheck hand entered replacement word.
                  (insert replace-word)
                  (ispell-send-replacement (car poss) replace-word)
                  (if (car (cdr replace))
                      (save-window-excursion
                        (delete-other-windows) ; to correctly show help.
                        ;; Assume case-replace &
                        ;; case-fold-search correct?
                        (query-replace (car poss) (car replace) t)))
                  (goto-char word-start)
                  ;; Do not recheck if already accepted.
                  (if (member replace-word accept-list)
                      (setq accept-list (cons replace-word accept-list)
                            replace replace-word)
                    (let ((region-end (copy-marker ispell-region-end)))
                      (setq recheck-region ispell-filter
                            ispell-filter nil ; Save filter.
                            shift 0           ; Already accounted.
                            shift (ispell-region
                                   word-start
                                   (+ word-start (length replace-word))
                                   t shift))
                      (if (null shift) ; Quitting check.
                          (setq shift 0))
                      (set-marker ispell-region-end region-end)
                      (set-marker region-end nil)
                      (setq ispell-filter recheck-region
                            recheck-region nil
                            replace replace-word)))))
              (setq shift (+ shift (- (length replace) word-len)))))

            (if (not ispell-quit)
                (let (message-log-max)
                  (message
                   "Continuing spelling check using %s with %s dictionary..."
                   (file-name-non directory ispell-program-name)
                   (or ispell-current-dictionary "default"))))
            (sit-for 0)
            (setq ispell-start (marker-position line-start)
                  ispell-end (marker-position line-end))
            ;; Adjust markers when end of region lost from highlighting.
            (if (and (not recheck-region)
                     (< ispell-end (+ word-start word-len)))
                (setq ispell-end (+ word-start word-len)))
            (if (= word-start ispell-region-end)
                (set-marker ispell-region-end (+ word-start word-len)))
            ;; Going out of scope - unneeded.
            (set-marker line-start nil)
            (set-marker word-start nil)
            (set-marker line-end nil)))
      ;; Finished with misspelling!
      (setq ispell-filter (cdr ispell-filter)))
    (cons shift max-word)))

-- 
underground experts united
https://dataswamp.org/~incal




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

* Re: [Elisp: 8 out of 10 problems] I think the last one! (point)
  2024-08-11  0:14 [Elisp: 8 out of 10 problems] I think the last one! (point) Emanuel Berg
@ 2024-08-11  4:46 ` Emanuel Berg
  2024-08-11  5:43   ` Eli Zaretskii
  2024-08-11  5:22 ` Eli Zaretskii
  2024-08-11  7:44 ` Yuri Khan
  2 siblings, 1 reply; 13+ messages in thread
From: Emanuel Berg @ 2024-08-11  4:46 UTC (permalink / raw)
  To: emacs-devel

> But also look closer at the code. Point is
> constantly moving.

Let me just add one thing, there is nothing to say just
because you edit and draw, you must move point like crazy
explicitely back and forth.

Her, for example, draw a box. Not a single command to just
move point around.

BTW, for that super-simple function, it has 8 lines of
interfaces and 11 for the program. 42% interface!

But maybe if you setup things carefully, that I why I don't
have to move point around? Well, here, that is.

;;  +-------------------+
;;  |                   |
;;  |  Box, `draw-box'  |
;;  |                   |
;;  +-------------------+

(defun draw-box (xc &optional yc)
  (interactive
    (list (read-number "x cols: " 74)
          (read-number "y cols: " 25)))
  (unless yc
    (setq yc xc))
  (when (and (<= 3 xc)
             (<= 3 yc))
    (let* ((yb ?\|)
           (xb ?\-)
           (cb ?\+)
           (bb ?\s)
           (xci (- xc 2))
           (yci (- yc 2))
           (tl (format "%c%s%c" cb (make-string xci xb) cb))
           (xl (format "%c%s%c" yb (make-string xci bb) yb))
           (all `(,tl ,@(make-list yci xl) ,tl))
           (str (string-join all "\n")))
      (insert "\n" str))))

-- 
underground experts united
https://dataswamp.org/~incal




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

* Re: [Elisp: 8 out of 10 problems] I think the last one! (point)
  2024-08-11  0:14 [Elisp: 8 out of 10 problems] I think the last one! (point) Emanuel Berg
  2024-08-11  4:46 ` Emanuel Berg
@ 2024-08-11  5:22 ` Eli Zaretskii
  2024-08-11  7:44 ` Yuri Khan
  2 siblings, 0 replies; 13+ messages in thread
From: Eli Zaretskii @ 2024-08-11  5:22 UTC (permalink / raw)
  To: Emanuel Berg; +Cc: emacs-devel

> From: Emanuel Berg <incal@dataswamp.org>
> Date: Sun, 11 Aug 2024 02:14:43 +0200
> 
> Take a look again at ispell.el. That function is called
> `ispell-process-line'. Man, what kind of #@&%$ lines to they
> have! It is insane.
> 
> But also look closer at the code. Point is constantly moving.

Why is that a problem, for a command that analyzes text in a buffer?
I'd say it's only natural that we move across the buffer as we
spell-check it.  As one nice bonus, when we find a mis-spelled word,
we are already at that word, so it is already shown in the window
together with its context.  Every interactive speller works like that.

> So if moving around the buffer is so important to us, why
> haven't we made it into an art of perfection decades ago?

We did.

> For every unit in Emacs that we know of, e.g. buffer, line,
> region, paragraph, word, there should be a two sets
> of functions.
> 
>   (word-beg) (word-len) (word-reg) (word-end)
> 
> So same with sentence then, as 'sent-' - "sentence-" is
> too long.
> 
>   (sent-beg) (sent-len) (sent-reg) (sent-end)
> 
> This should mean _go there_
> 
> But there should be another set as well, that just reports the
> position. This set can mirror the former but let's start from
> the other end to avoid typos and make it easier to see.
> 
>   (pos-sent-beg) (pos-sent-len) (pos-sent-reg) (pos-sent-end)

We have that.  The difference between "going there" and "not going" is
in many cases negligible, so if we have forward-word (which can move N
words forward), then "Nth word from here" is just

   (save-excursion (forward-word N) (current-word))

For sentences, we actually have both forward-sentence and
sentence-end, but that's because there's a subtle difference between
moving by sentence and finding the sentence end.

More generally, Emacs has functions for stuff that is (a) non-trivial
and (b) done frequently enough to justify a dedicated API.  Having too
many functions is a disadvantage, because it is then harder to
remember and find what you need, and so we don't add functions out of
some theoretical principle of completeness or symmetry or consistency.

> One example that we really have under-prioritized this, is as
> simple as the most common of all, namely
> 
>   (goto-char (point-min))
> 
> But as you see that isn't optimized one bit. It is long to
> type, and not atomic, it is nested even. Yuk!
> 
> Instead we should have, again, something like
> 
> (buf-beg) (buf-len) (buf-reg) (buf-end) all with implicity `goto-char'
> 
>   and
> 
> (pos-buf-beg) (pos-buf-reg) (pos-buf-end)
> 
> is my suggestion, anyway!

You exaggerate the minor issues, and completely ignore the downside of
introducing more and more functions for no good reason.

> This is a pretty big problem, don't think it is trivial.
> Who is gonna want to maintain code, that is endlessly moving
> point around, in those super-long functions, very vaguely at
> any point having anything to do with the interesting problems,
> one thought one would solve?

We are doing this for decades, and that practice shows that it is not
a catastrophe you are trying to make it sound.  Far from it.

> So annoying thing number 8, even tho this is our game plan and
> we have a lot, it doesn't seem like we have really optimized
> this for speed, brevity and code clearness.

Of course, we do.  You haven't shown that your suggestions will make
Emacs faster in any non-trivial scenario.

> This kind of stuff, yeah, compare to how long I type each line!
> 
> (progn (forward-paragraph -2) (when (eobp) (point)))

On the contrary: it is quite concise and tells exactly what the code
is doing.  By contrast, replacing it by some special-case API would at
least sometimes require the reader to go refer to the documentation,
in order to understand what the implementation does.  And that is a
non-trivial disadvantage.

> And now, more ispell.el:
> 
> Returns a cons cell where the `car' is sum SHIFT due to changes
> in word replacements, and the `cdr' is the location of the final
> word that was queried about."

Did you read the documentation of what the speller returns when
submitted a line with possibly mis-spelled words?  It communicates the
results in a form of a DSL whose parsing is not trivial at all.  IOW,
let's keep in mind that anonymous wisdom: "Complex problems have
simple, easy-to-understand wrong answers."



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

* Re: [Elisp: 8 out of 10 problems] I think the last one! (point)
  2024-08-11  4:46 ` Emanuel Berg
@ 2024-08-11  5:43   ` Eli Zaretskii
  0 siblings, 0 replies; 13+ messages in thread
From: Eli Zaretskii @ 2024-08-11  5:43 UTC (permalink / raw)
  To: Emanuel Berg; +Cc: emacs-devel

> From: Emanuel Berg <incal@dataswamp.org>
> Date: Sun, 11 Aug 2024 06:46:14 +0200
> 
>            (tl (format "%c%s%c" cb (make-string xci xb) cb))
>            (xl (format "%c%s%c" yb (make-string xci bb) yb))
>            (all `(,tl ,@(make-list yci xl) ,tl))
>            (str (string-join all "\n")))
>       (insert "\n" str))))

This has a disadvantage of consing temporary strings and lists (which
will increase memory pressure and trigger GC) instead of inserting the
box characters directly into the buffer.



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

* Re: [Elisp: 8 out of 10 problems] I think the last one! (point)
  2024-08-11  0:14 [Elisp: 8 out of 10 problems] I think the last one! (point) Emanuel Berg
  2024-08-11  4:46 ` Emanuel Berg
  2024-08-11  5:22 ` Eli Zaretskii
@ 2024-08-11  7:44 ` Yuri Khan
  2024-08-11  9:06   ` Emanuel Berg
  2024-08-11 12:16   ` Emanuel Berg
  2 siblings, 2 replies; 13+ messages in thread
From: Yuri Khan @ 2024-08-11  7:44 UTC (permalink / raw)
  To: emacs-devel

On Sun, 11 Aug 2024 at 11:29, Emanuel Berg <incal@dataswamp.org> wrote:

> For every unit in Emacs that we know of, e.g. buffer, line,
> region, paragraph, word, there should be a two sets
> of functions.
>
>   (word-beg) (word-len) (word-reg) (word-end)
>   (sent-beg) (sent-len) (sent-reg) (sent-end)

You abbreviate words to the point their meaning is no longer
recognizable. ‘beg’ is a verb. ‘sent’ is a word in English that is the
past or past participle form of ‘send’. This may introduce confusion
in context where data is *sent* between processes or network hosts.

Okay, I can make out ‘begin’ and ‘length’. Without looking, I do not
understand at all what ‘reg’ is supposed to mean. region? regexp?
register?

Elisp and Emacs core libraries do not abbreviate words except where
traditional, and there’s a good reason not to. An abbreviated word is
harder to read and sometimes harder to type because you have to choose
the correct one out of a number of possible abbreviations. In many
cases, typing a long word is a matter of M-TAB or M-/.



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

* Re: [Elisp: 8 out of 10 problems] I think the last one! (point)
  2024-08-11  7:44 ` Yuri Khan
@ 2024-08-11  9:06   ` Emanuel Berg
  2024-08-11 12:16   ` Emanuel Berg
  1 sibling, 0 replies; 13+ messages in thread
From: Emanuel Berg @ 2024-08-11  9:06 UTC (permalink / raw)
  To: emacs-devel

Yuri Khan wrote:

> You abbreviate words to the point their meaning is no longer
> recognizable. 'beg' is a verb.

Beg is quite common as argument names. I it also good that beg
and end are as long short so they can be stapled.

> 'sent' is a word in English that is the past or past
> participle form of 'send'.

That OTOH maybe wasn't so good.

> Okay, I can make out 'begin' and 'length'.

No, in particular length is really ugly and error-prone
compared to beg, end and len.

> Without looking, I do not understand at all what 'reg' is
> supposed to mean. region? regexp? register?

There is not really a good word for that that I can come up
with, because to some people the region is a purely
interactive feature when set. This is in practice not so, but
maybe it should be? Anyway, obviously it isn't regexp as we
write that as regexp or re for short. Register, from what
century are you? The last one? :)

Maybe -spn is better (for span, returning a tuple with (min
max)).

> Elisp and Emacs core libraries do not abbreviate words
> except where traditional,

I know - that is some of the problems I took up in that post.

> and there's a good reason not to.

Maybe good reasons. Speed, time, less error-prone and lesser
strain on body and mind sitting and typing. Also less strain
on eyes, elbows, less time spend in the sun etc.

It should be clear, zsh i too cryptic for me if you use all
the features, but Emacs is crazy verbose, here, set the
region for interactive functions whenever it is read - yeah,
why one has to do anything with it then, one can wonder but
with Lisp, it is a lot of words.

It doesn't have to be super-short but a lot of more
conventions that are automated would be good.

So for example below, region -> reg, beginning -> beg, instead
of defun fun, "list" is actually not so pleasant to type so we
change that to just l. That is 84 chars from 107 and much
spacier and more relaxed.

(fun use-reg ()
  (if (use-reg-p)
      (l (region-beg) (reg-end))
    (l nil nil)))

(defun use-region ()
  (if (use-region-p)
      (list (region-beginning) (region-end))
    (list nil nil)))

> An abbreviated word is harder to read

Not always and you don't read code as you read a novel.

> and sometimes harder to type because you have to choose the
> correct one out of a number of possible abbreviations.

Sometimes yes, the individual case will have to decide, but in
general less chars to type, the faster it goes, for sure.

> In many cases, typing a long word is a matter of M-TAB or
> M-/.

But that isn't typing :(

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Note: This message is about Emacs and Elisp. As long as Emacs
~~~~~ and Elisp are not off-topic om this list, don't tell me
      to go somewhere else. I can't and I don't want to, and,
      _all_ my posts are about Emacs, ELPA, and Elisp. As you
      can clearly see above. Also, I'm responding to someone.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

-- 
underground experts united
https://dataswamp.org/~incal




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

* Re: [Elisp: 8 out of 10 problems] I think the last one! (point)
  2024-08-11  7:44 ` Yuri Khan
  2024-08-11  9:06   ` Emanuel Berg
@ 2024-08-11 12:16   ` Emanuel Berg
  2024-08-13 13:13     ` buffer segments, uniform and automatic (was: Re: [Elisp: 8 out of 10 problems] I think the last one! (point)) Emanuel Berg
  1 sibling, 1 reply; 13+ messages in thread
From: Emanuel Berg @ 2024-08-11 12:16 UTC (permalink / raw)
  To: emacs-devel

Yuri Khan wrote:

> Okay, I can make out 'begin' and 'length'. Without looking,
> I do not understand at all what 'reg' is supposed to mean.
> region? regexp? register?

Also, you should be aware that when you do optimizations such
as these, they are for experienced people. People who use it
every day. The purpose is not to make anything more accessible
to newcomers or anything like that. At some point, it will be
like removing the Presta valves from a bike. 2 valves is 0.67
grams. They do rotate on the wheels but it is well-known it
cannot affect the outcome of a race.

So, does that mean it is useless, meaningless? No, it means
you have done anything and is now ready to do, whatever it is
you should do.

Are we done optimizing Emacs? Have we added all the
interesting, complicated parts, but also trimmed every detail
to the point we have a well-oiled machine, ready to execute?

As for me the answer would be: we can always get more
interesting, complicated stuff, and as for the details: no,
they are not trimmed down at all, on the contrary they tell me
that thru Emacs history, people have not thought a lot about
that att all and it hasn't been prioritized.

I say this, of course, on a broad scale and is not
commentating anyone's individual contribution or style.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This post is written by incal, official Emacs developer since
2015 and contributor to core Emacs, to ELPA, and to Emacs
culture. This post is on-topic - it is about Emacs, ELPA,
Elisp, and the Emacs community.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

-- 
underground experts united
https://dataswamp.org/~incal




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

* buffer segments, uniform and automatic (was: Re: [Elisp: 8 out of 10 problems] I think the last one! (point))
  2024-08-11 12:16   ` Emanuel Berg
@ 2024-08-13 13:13     ` Emanuel Berg
  2024-08-13 14:10       ` Ihor Radchenko
  0 siblings, 1 reply; 13+ messages in thread
From: Emanuel Berg @ 2024-08-13 13:13 UTC (permalink / raw)
  To: emacs-devel

> Okay, I can make out 'begin' and 'length'. Without looking,
> I do not understand at all what 'reg' is supposed to mean.
> region? regexp? register?

I came up with an idea how to do this!

It is based on this policy:

1. Everything has a beginning and an end.

2. Everything belongs to something else.

3. The only standard worth its name isn't the one that comes
   from ISO, or the one in a manual, it isn't even the one the
   best programmers (almost) always stick to - it is the one
   created by a computer program.

So as for the segments in a text mode buffer, as for
point (2), it would look something like:

  [ buffer [ paragraphs [ sentences [words] ] ] ]

Definitions of each has to be setup manually and individually,
as for (1), so for example for word it could be

(list (progn (forward-word) (forward-word -1) (point))
      (progn (forward-word) (point)))

Then the program will define a set of functions for each
segment type, the same for each.

Note that so many common operations can be defined from the
definition, i.e. the (BEG END) pair:

-len is (- END BEG)
-reg is (BEG END)
-beg is BEG
-end is END
-cur is (buffer-substring-no-properties BEG END)
...

Everything else is then based on combinations of those:

-prev of -beg and -cur
-next of -end and -cur
...

And it goes on:

-all  of -cur and -next     (for some outer BEG END)
-count of `length' and -all (for some outer BEG and END)

I'm working on it now!

PS. Change of subject line as I think the previous one with
    [square brackets] hindered the thread from being
    archived online.

-- 
underground experts united
https://dataswamp.org/~incal




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

* Re: buffer segments, uniform and automatic (was: Re: [Elisp: 8 out of 10 problems] I think the last one! (point))
  2024-08-13 13:13     ` buffer segments, uniform and automatic (was: Re: [Elisp: 8 out of 10 problems] I think the last one! (point)) Emanuel Berg
@ 2024-08-13 14:10       ` Ihor Radchenko
  2024-08-13 17:10         ` Emanuel Berg
  0 siblings, 1 reply; 13+ messages in thread
From: Ihor Radchenko @ 2024-08-13 14:10 UTC (permalink / raw)
  To: Emanuel Berg; +Cc: emacs-devel

Emanuel Berg <incal@dataswamp.org> writes:

> So as for the segments in a text mode buffer, as for
> point (2), it would look something like:
> ...
>   [ buffer [ paragraphs [ sentences [words] ] ] ]
>
> Definitions of each has to be setup manually and individually,
> as for (1), so for example for word it could be
> ...
> Then the program will define a set of functions for each
> segment type, the same for each.
>
> Note that so many common operations can be defined from the
> definition, i.e. the (BEG END) pair:
>
> -len is (- END BEG)
> -reg is (BEG END)
> -beg is BEG
> -end is END
> -cur is (buffer-substring-no-properties BEG END)
> ...

Check out thingatpt.el: `beginning-of-thing', `end-of-thing',
`thing-at-point', etc.

"thing" can be anything, including page, paragraph, word, link, sexp, etc

-- 
Ihor Radchenko // yantar92,
Org mode contributor,
Learn more about Org mode at <https://orgmode.org/>.
Support Org development at <https://liberapay.com/org-mode>,
or support my work at <https://liberapay.com/yantar92>



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

* Re: buffer segments, uniform and automatic (was: Re: [Elisp: 8 out of 10 problems] I think the last one! (point))
  2024-08-13 14:10       ` Ihor Radchenko
@ 2024-08-13 17:10         ` Emanuel Berg
  2024-08-13 18:07           ` Emanuel Berg
  0 siblings, 1 reply; 13+ messages in thread
From: Emanuel Berg @ 2024-08-13 17:10 UTC (permalink / raw)
  To: emacs-devel

Ihor Radchenko wrote:

>> -len is (- END BEG)
>> -reg is (BEG END)
>> -beg is BEG
>> -end is END
>> -cur is (buffer-substring-no-properties BEG END) [...]
>
> Check out thingatpt.el: `beginning-of-thing',
> `end-of-thing', `thing-at-point', etc.
>
> "thing" can be anything, including page, paragraph, word,
> link, sexp, etc

You are right, didn't think of that. Let's see later if it
can be reused.

Right now, I'm stuck with this.

It works for these segments:

(pos--def "data" (pos--data))
(pos--def "para" (pos--para))
(pos--def "sntc" (pos--sntc))

But not for this:

(pos--def "word" (pos--word))

It says about `forward-char' in the docstring:

    This function has a ‘byte-compile’ property
    ‘byte-compile-zero-or-one-arg’. See the manual
    for details.

Maybe that is the reason?

;;; -*- lexical-binding: t -*-
;;
;; this file:
;;   https://dataswamp.org/~incal/emacs-init/pos.el

(defun pos--wash-str (str)
  (string-trim
    (replace-regexp-in-string "%" "%%"
      (replace-regexp-in-string "[\n[:blank:]]+" " " str))))

(defmacro pos--def (name spn)
  (let (     (reg (intern (format      "%s-reg" name)))
             (beg (intern (format      "%s-beg" name)))
             (end (intern (format      "%s-end" name)))
             (cur (intern (format      "%s-cur" name)))
             (len (intern (format      "%s-len" name)))
        (goto-beg (intern (format "goto-%s-beg" name)))
        (goto-end (intern (format "goto-%s-end" name))))
    `(progn
       (defun      ,reg ()      (pos--reg ,spn))
       (defun      ,beg ()      (pos--beg ,spn))
       (defun      ,end ()      (pos--end ,spn))
       (defun      ,cur ()      (pos--cur ,spn))
       (defun      ,len ()      (pos--len ,spn))
       (defun ,goto-beg () (pos--goto-beg ,spn))
       (defun ,goto-end () (pos--goto-end ,spn)) )
    ))

(defun pos--reg (spn)
  (pcase-let* (( `(,beg ,end) spn ))
    (list beg end)))

(defun pos--beg (spn)
  (car (pos--reg spn)))

(defun pos--end (spn)
  (cadr (pos--reg spn)))

(defun pos--cur (spn)
  (pcase-let* (( `(,beg ,end) (pos--reg spn) )
               ( cur (pos--wash-str (buffer-substring-no-properties beg end)) ))
    cur))

(defun pos--len (spn)
  (length (pos--cur spn)))

(defun pos--goto-beg (spn)
  (goto-char (pos--beg spn)))

(defun pos--goto-end (spn)
  (goto-char (pos--end spn)))

(defun pos--data ()
  (save-mark-and-excursion
    (let ((min (point-min))
          (max (point-max))
          (beg)
          (end))
      (goto-char min)
      (re-search-forward "." max t)
      (setq beg (match-beginning 0))
      (goto-char max)
      (re-search-backward "." beg t)
      (setq end (match-end 0))
      (when (and beg end (< beg end))
        (list beg end)))))

(defun pos--para ()
  (save-mark-and-excursion
    (let ((beg (progn (start-of-paragraph-text) (point)))
          (end (progn (end-of-paragraph-text)   (point))))
      (list beg end))))

(defun pos--sntc ()
  (save-mark-and-excursion
    (let ((end (forward-sentence))
          (beg (forward-sentence -1)))
      (list beg end))))

(defun pos--word ()
  (save-mark-and-excursion
    (let ((end (forward-word))
          (beg (forward-word -1)))
      (list beg end))))

(pos--def "data" (pos--data))
(pos--def "para" (pos--para))
(pos--def "sntc" (pos--sntc))
(pos--def "word" (pos--word))

(provide 'pos)

-- 
underground experts united
https://dataswamp.org/~incal




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

* Re: buffer segments, uniform and automatic (was: Re: [Elisp: 8 out of 10 problems] I think the last one! (point))
  2024-08-13 17:10         ` Emanuel Berg
@ 2024-08-13 18:07           ` Emanuel Berg
  2024-08-14 20:02             ` Emanuel Berg
  0 siblings, 1 reply; 13+ messages in thread
From: Emanuel Berg @ 2024-08-13 18:07 UTC (permalink / raw)
  To: emacs-devel

> It says about `forward-char' in the docstring:
>
>     This function has a 'byte-compile' property
>     'byte-compile-zero-or-one-arg'. See the manual
>     for details.
>
> Maybe that is the reason?

No, of course not. `forward-char' doesn't work like the
others, or they don't like `forward-char', point has to be
returned manually.

See? It will be sweet to have a uniform interface for this!

-- 
underground experts united
https://dataswamp.org/~incal




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

* Re: buffer segments, uniform and automatic (was: Re: [Elisp: 8 out of 10 problems] I think the last one! (point))
  2024-08-13 18:07           ` Emanuel Berg
@ 2024-08-14 20:02             ` Emanuel Berg
  2024-08-15  5:39               ` Emanuel Berg
  0 siblings, 1 reply; 13+ messages in thread
From: Emanuel Berg @ 2024-08-14 20:02 UTC (permalink / raw)
  To: emacs-devel

Consistent Elisp :)

See downmost for examples what you can do with it.

It supports buff-, para-, sent-, word-, and char- right now
but it is easy to add whatever you want.

Everything is defined as: (beg end next)

;;; -*- lexical-binding: t -*-
;;
;; this file:
;;   https://dataswamp.org/~incal/emacs-init/pos.el
;;
;; -----------------------------------------------------------------------

(require 'cl-lib)

(defun pos--wash-str (str)
  (string-trim
    (replace-regexp-in-string "%" "%%"
      (replace-regexp-in-string "[\n[:blank:]]+" " " str))))

;; -----------------------------------------------------------------------

(defun pos--all-in-f (goto-beg end cur next)
  (when (cl-every #'functionp (list goto-beg end cur next))
    (funcall goto-beg)
    (let ((end (funcall end))
          (res))
      (while (< (point) end)
        (push (funcall cur) res)
        (funcall next))
      (nreverse res))))

(defmacro pos--all-in (seg in)
  (let ( (goto-beg (intern (format "goto-%s-beg" in)))
         (end      (intern (format "%s-end"      in)))
         (cur      (intern (format "%s-cur"      seg)))
         (next     (intern (format "%s-next"     seg))) )
    `(pos--all-in-f (quote ,goto-beg)
                    (quote ,end)
                    (quote ,cur)
                    (quote ,next))))

;; -----------------------------------------------------------------------

(defmacro pos--def (name spn)
  (let ((reg (intern (format "%s-reg" name)))
        (beg (intern (format "%s-beg" name)))
        (end (intern (format "%s-end" name)))
        (cur (intern (format "%s-cur" name)))
        (len (intern (format "%s-len" name)))
        (bop (intern (format "%s-bop" name)))
        (eop (intern (format "%s-eop" name)))
        (goto-beg (intern (format "goto-%s-beg" name)))
        (goto-end (intern (format "goto-%s-end" name)))
        (next (intern (format "%s-next" name)))
        (prev (intern (format "%s-prev" name)))
        )
    `(progn
       (defun ,reg () (pos--reg ,spn))
       (defun ,beg () (pos--beg ,spn))
       (defun ,end () (pos--end ,spn))
       (defun ,cur () (pos--cur ,spn))
       (defun ,len () (pos--len ,spn))
       (defun ,bop () (pos--bop ,spn))
       (defun ,eop () (pos--eop ,spn))
       (defun ,goto-beg () (interactive) (pos--goto-beg ,spn))
       (defun ,goto-end () (interactive) (pos--goto-end ,spn))
       (when (= 3 (length ,spn))
         (defun ,next (&optional n) (interactive "p") (pos--next ,spn n))
         (defun ,prev (&optional n) (interactive "p") (pos--prev ,spn n)))
       )))

(defun pos--reg (spn &optional def)
  (pcase-let* ((`(,beg ,end ,next) spn))
    (if def
        (list beg end next)
      (list beg end))))

(defun pos--beg (spn)
  (car (pos--reg spn)))

(defun pos--end (spn)
  (cadr (pos--reg spn)))

(defun pos--cur (spn)
  (pcase-let* ((`(,beg ,end) (pos--reg spn))
               (cur (pos--wash-str (buffer-substring-no-properties beg end))))
    cur))

(defun pos--len (spn)
  (- (pos--end spn) (pos--beg spn)))

(defun pos--bop (spn)
  (= (point) (pos--beg spn)))

(defun pos--eop (spn)
  (= (point) (pos--end spn)))

(defun pos--goto-beg (spn)
  (goto-char (pos--beg spn)))

(defun pos--goto-end (spn)
  (goto-char (pos--end spn)))

(defun pos--next (spn &optional n)
  (or n (setq n 1))
  (pcase-let* ((`(,_beg ,_end ,next) (pos--reg spn t)))
    (when (functionp next)
      (funcall next n))))

(defun pos--prev (spn &optional n)
  (or n (setq n 1))
  (pcase-let* ((`(,_beg ,_end ,next) (pos--reg spn t)))
    (when (functionp next)
      (funcall next (* -1 n)))))

;; -----------------------------------------------------------------------

(defun pos--buff ()
  (list (point-min) (point-max)))

(defun pos--para ()
  (save-mark-and-excursion
    (let ((beg (progn (start-of-paragraph-text) (point)))
          (end (progn (end-of-paragraph-text)   (point))))
      (list beg end #'forward-paragraph))))

(defun pos--sent ()
  (save-mark-and-excursion
    (let ((end (forward-sentence))
          (beg (forward-sentence -1)))
      (list beg end #'forward-sentence))))

(defun pos--word ()
  (save-mark-and-excursion
    (let ((end (progn (forward-word)    (point)))
          (beg (progn (forward-word -1) (point))))
      (list beg end #'forward-word))))

(defun pos--char ()
  (save-mark-and-excursion
    (let ((beg (point))
          (end (progn (forward-char) (point))))
      (list beg end #'forward-char))))

;; -----------------------------------------------------------------------

(when nil
  (pos--def "buff" (pos--buff))
  (pos--def "para" (pos--para))
  (pos--def "sent" (pos--sent))
  (pos--def "word" (pos--word))
  (pos--def "char" (pos--char))
  )

(when nil
  ;; for buff-, para-, sent-, word-, and char-
  (word-reg)
  (word-beg)
  (word-end)
  (word-cur)
  (word-len)
  (word-bop)
  (word-eop)

  ;; M-x
  (goto-word-beg)
  (goto-word-end)

  ;; for para-, sent-, word-, char-
  ;; with C-u M-x and C-u n M-x
  (word-next)
  (word-next 5)
  (word-prev)
  (word-prev 5)
  (pos--all-in word sent) ; all combinations
)

(provide 'pos)

-- 
underground experts united
https://dataswamp.org/~incal




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

* Re: buffer segments, uniform and automatic (was: Re: [Elisp: 8 out of 10 problems] I think the last one! (point))
  2024-08-14 20:02             ` Emanuel Berg
@ 2024-08-15  5:39               ` Emanuel Berg
  0 siblings, 0 replies; 13+ messages in thread
From: Emanuel Berg @ 2024-08-15  5:39 UTC (permalink / raw)
  To: emacs-devel

> https://dataswamp.org/~incal/emacs-init/pos.el

Check out the URL for upgrades, right now it supports:

segments:
  buff-
  para-
  sent-
  word-
  char-

supports:
  -reg
  -beg -end
  -bop -eop
  -cur
  -len
  goto-*-beg goto-*-end
  -next -prev

also:
  (pos--all-in SEG_SMALL SEG_BIG)

So for example for the word segment you can do:

(word-reg)
(word-beg)
(word-end)
(word-cur)
(word-len)
(word-bop)
(word-eop)

(goto-word-beg) ; also M-x
(goto-word-end)
  
(word-next) ; also M-x, C-u M-x, and C-u n M-x
(word-next 5)
(word-prev)
(word-prev 5)

(pos--all-in word sent) ; all combinations

-- 
underground experts united
https://dataswamp.org/~incal




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

end of thread, other threads:[~2024-08-15  5:39 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-08-11  0:14 [Elisp: 8 out of 10 problems] I think the last one! (point) Emanuel Berg
2024-08-11  4:46 ` Emanuel Berg
2024-08-11  5:43   ` Eli Zaretskii
2024-08-11  5:22 ` Eli Zaretskii
2024-08-11  7:44 ` Yuri Khan
2024-08-11  9:06   ` Emanuel Berg
2024-08-11 12:16   ` Emanuel Berg
2024-08-13 13:13     ` buffer segments, uniform and automatic (was: Re: [Elisp: 8 out of 10 problems] I think the last one! (point)) Emanuel Berg
2024-08-13 14:10       ` Ihor Radchenko
2024-08-13 17:10         ` Emanuel Berg
2024-08-13 18:07           ` Emanuel Berg
2024-08-14 20:02             ` Emanuel Berg
2024-08-15  5:39               ` Emanuel Berg

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

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