unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: Emanuel Berg <incal@dataswamp.org>
To: emacs-devel@gnu.org
Subject: [Elisp: 8 out of 10 problems] I think the last one! (point)
Date: Sun, 11 Aug 2024 02:14:43 +0200	[thread overview]
Message-ID: <875xs7zzik.fsf@dataswamp.org> (raw)

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




             reply	other threads:[~2024-08-11  0:14 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-08-11  0:14 Emanuel Berg [this message]
2024-08-11  4:46 ` [Elisp: 8 out of 10 problems] I think the last one! (point) 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

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=875xs7zzik.fsf@dataswamp.org \
    --to=incal@dataswamp.org \
    --cc=emacs-devel@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).