Function: ibut:operate
ibut:operate is a byte-compiled function defined in hbut.el.
Signature
(ibut:operate &optional NEW-NAME EDIT-FLAG)
Documentation
Insert/modify an ibutton based on hbut:current in current buffer.
This is for internal Hyperbole use only. Use ibut:program and
ibut:create (an alias of defib), if programming applications
with Hyperbole.
IMPORTANT: Caller must either call hbut:at-p or manually set
the attributes of hbut:current prior to invoking this function,
i.e. there must be an ibutton stored in memory in `hbut:current
prior to invocation. If point is on an existing Hyperbole
button, edit-flag must be set to t; otherwise, this may create
a new ibutton inserted within the prior one, making the prior one
unusable.
Optional non-nil NEW-NAME is the new name to give the button. With optional EDIT-FLAG non-nil, modify an existing in-buffer ibutton rather than creating a new one.
If NEW-NAME is nil, use the active region text as the button name, if any; if no such region, then create/modify an unnamed implicit button.
Return instance string appended to name to form a per-buffer unique name; nil if name is already unique or no name. Signal an error when no such button is found in the current buffer.
Summary of operations based on inputs (name arg from 'hbut:current attrs):
|----+------+----------+--------+------+-----------------------------------------------|
| # | name | new-name | region | edit | operation |
|----+------+----------+--------+------+-----------------------------------------------|
| 1 | nil | nil | nil | nil | create: unnamed ibut from hbut:current attrs |
| 2 | nil | new-name | nil | nil | ERROR: edit-flag must be t to set new-name |
| 3 | name | nil | nil | nil | create: ibut with name |
| 4 | name | new-name | nil | nil | ERROR: create can't have name and new-name |
| 5 | name | new-name | region | nil | ERROR: create can't have name and new-name |
| 6 | name | nil | region | nil | create: ibut with name (ignore region) |
| 7 | nil | nil | region | nil | create: region named ibut |
| 8 | nil | new-name | region | nil | ERROR: edit-flag must be t to set new-name |
|----+------+----------+--------+------+-----------------------------------------------|
| 9 | nil | nil | nil | t | mod: remove any name from ibut |
| 10 | nil | new-name | nil | t | mod: add new-name as ibut's name attribute |
| 11 | name | nil | nil | t | mod: name of ibut from hbut:current attrs |
| 12 | name | new-name | nil | t | mod: rename ibut with name to new-name |
| 13 | name | new-name | region | t | ERROR: Can't use region to mod existing ibut |
| 14 | name | nil | region | t | ERROR: Can't use region to mod existing ibut |
| 15 | nil | nil | region | t | ERROR: Can't use region to mod existing ibut |
| 16 | nil | new-name | region | t | ERROR: Can't use region to mod existing ibut |
|----+------+----------+--------+------+-----------------------------------------------|
Source Code
;; Defined in ~/.emacs.d/elpa/hyperbole-20260414.325/hbut.el
(defun ibut:operate (&optional new-name edit-flag)
"Insert/modify an ibutton based on `hbut:current' in current buffer.
This is for internal Hyperbole use only. Use `ibut:program' and
`ibut:create' (an alias of `defib'), if programming applications
with Hyperbole.
IMPORTANT: Caller must either call `hbut:at-p' or manually set
the attributes of `hbut:current' prior to invoking this function,
i.e. there must be an ibutton stored in memory in `hbut:current
prior to invocation. If point is on an existing Hyperbole
button, `edit-flag' must be set to t; otherwise, this may create
a new ibutton inserted within the prior one, making the prior one
unusable.
Optional non-nil NEW-NAME is the new name to give the button. With
optional EDIT-FLAG non-nil, modify an existing in-buffer ibutton
rather than creating a new one.
If NEW-NAME is nil, use the active region text as the button
name, if any; if no such region, then create/modify an unnamed
implicit button.
Return instance string appended to name to form a per-buffer
unique name; nil if name is already unique or no name. Signal an
error when no such button is found in the current buffer.
Summary of operations based on inputs (name arg from \\='hbut:current attrs):
|----+------+----------+--------+------+-----------------------------------------------|
| # | name | new-name | region | edit | operation |
|----+------+----------+--------+------+-----------------------------------------------|
| 1 | nil | nil | nil | nil | create: unnamed ibut from hbut:current attrs |
| 2 | nil | new-name | nil | nil | ERROR: edit-flag must be t to set new-name |
| 3 | name | nil | nil | nil | create: ibut with name |
| 4 | name | new-name | nil | nil | ERROR: create can't have name and new-name |
| 5 | name | new-name | region | nil | ERROR: create can't have name and new-name |
| 6 | name | nil | region | nil | create: ibut with name (ignore region) |
| 7 | nil | nil | region | nil | create: region named ibut |
| 8 | nil | new-name | region | nil | ERROR: edit-flag must be t to set new-name |
|----+------+----------+--------+------+-----------------------------------------------|
| 9 | nil | nil | nil | t | mod: remove any name from ibut |
| 10 | nil | new-name | nil | t | mod: add new-name as ibut's name attribute |
| 11 | name | nil | nil | t | mod: name of ibut from hbut:current attrs |
| 12 | name | new-name | nil | t | mod: rename ibut with name to new-name |
| 13 | name | new-name | region | t | ERROR: Can't use region to mod existing ibut |
| 14 | name | nil | region | t | ERROR: Can't use region to mod existing ibut |
| 15 | nil | nil | region | t | ERROR: Can't use region to mod existing ibut |
| 16 | nil | new-name | region | t | ERROR: Can't use region to mod existing ibut |
|----+------+----------+--------+------+-----------------------------------------------|"
(barf-if-buffer-read-only)
(let* ((name (hattr:get 'hbut:current 'name))
(name-regexp (ibut:name-regexp (ibut:label-to-key name)))
(region-flag (hmouse-use-region-p))
(instance-flag))
(when (and new-name (or (not (stringp new-name)) (string-empty-p new-name)))
(hypb:error "(ibut:operate): 'new-name' value must be a non-empty string, not: '%s'"
new-name))
(when (and name new-name (not edit-flag))
(hypb:error "(ibut:operate): 'edit-flag' must be t to rename a button (hbut:current name and new-name both given)"))
(when (and new-name (not edit-flag))
(hypb:error "(ibut:operate): 'edit-flag' must be t to rename a button"))
(when (and region-flag edit-flag)
(hypb:error "(ibut:operate): 'edit-flag' must be nil when region is highlighted to use region as new button name"))
;; Error when on a read-only part of a buffer's text
(when (plist-member (text-properties-at (point)) 'read-only)
(hypb:error "(ibut:operate): Point must not be on a read-only Org element"))
;; Error when on an explicit button
(when (eq (hattr:get 'hbut:current 'categ) 'explicit)
(hypb:error "(ibut:operate): Point must not be on an explicit button: %s"
(ibut:label-to-key (hattr:get 'hbut:current 'lbl-key))))
;; Error when on an Emacs push-button
(when (plist-member (text-properties-at (point)) 'button)
(hypb:error "(ibut:operate): Point must not be on an Emacs push-button: %s"
(button-label (button-at (point)))))
;; Error when in read-only contexts of an Org file
(when (hsys-org-at-read-only-p)
(hypb:error "(ibut:operate): Point must not be in a read-only Org context"))
(unless new-name
(setq new-name name
name nil))
(when (stringp new-name)
(hattr:set 'hbut:current 'name new-name))
(save-excursion
(if (progn
(setq instance-flag (hbdata:ibut-instance-last (ibut:label-to-key
(if edit-flag new-name name))))
(when (null instance-flag)
(setq instance-flag t))
instance-flag)
(when (hmail:editor-p)
(hmail:msg-narrow))
(hypb:error "(ibut:operate): Failed to %s button %s%s%s in buffer %s"
(if edit-flag "modify" "create")
ibut:label-start name ibut:label-end
(buffer-name))))
(let (start end)
(cond (edit-flag
(cond (name
;; Rename all occurrences of button - those with same name
(let* ((but-key-and-pos (ibut:label-p nil nil nil 'pos))
(at-but (equal (car but-key-and-pos)
(ibut:label-to-key new-name))))
(when at-but
(ibut:delimit (nth 1 but-key-and-pos)
(nth 2 but-key-and-pos)
instance-flag))
(cond ((ibut:map
(lambda (_lbl start end)
(delete-region start end)
(when new-name
(ibut:delimit
(point)
(progn (insert new-name) (point))
instance-flag)))
name-regexp 'include-delims))
(at-but))))
(new-name
;; Add new-name to nameless button at point
(goto-char (or (hattr:get 'hbut:current 'lbl-start) (point)))
;; Skip back past any likely opening delim preceding button text.
(skip-chars-backward "\"<{[|")
(ibut:delimit (point)
(progn (insert new-name) (point))
instance-flag))
(t ;; Remove any existing name at point
(when (hattr:get 'hbut:current 'name-start)
(save-excursion
(delete-region (goto-char (hattr:get 'hbut:current 'name-start))
(hattr:get 'hbut:current 'name-end))
(when (looking-at ibut:label-separator-regexp)
(delete-region (match-beginning 0) (match-end 0))))
;; Skip past any likely opening delim preceding button text.
(skip-chars-forward "\"<{[|")
(setq start (point))))))
(t
;; Above flag is 't when we are creating the first instance
;; of the button name
;;
;; Add a new implicit button in the buffer, recording its
;; start and end positions; new-name is always nil here
(cond ((not (or name new-name region-flag))
;; No name to insert, just insert ibutton text below
)
((and region-flag
;; ignore region when name or new-name are set
(not (or name new-name))
;; new-name is always nil here
;; Ignore action-key-depress-prev-point
(setq start (region-beginning)
end (region-end)
name (buffer-substring-no-properties start end)))
nil)
((progn (when start (goto-char start))
(or (when name (looking-at (regexp-quote name)))
(when new-name (looking-at (regexp-quote new-name)))))
(setq start (point)
end (match-end 0)))
(name
(setq start (point))
(insert name)
(setq end (point)))
(new-name
(setq start (point))
(insert new-name)
(setq end (point)))
(t (hypb:error
"(ibut:operate): Operation failed. Check button attribute permissions: %s"
hattr:filename)))))
(unless edit-flag
(when (and start end)
(ibut:delimit start end instance-flag))
(when (hattr:get 'hbut:current 'actype)
(ibut:insert-text 'hbut:current)))
(goto-char (or start (max (- (point) 2) (point-min))))
(when start
;; Skip past any inserted comment char
(skip-syntax-forward "-<")
;; Skip past any name or label opening delim chars
(skip-chars-forward "\"<{[| \t\n\r")))
;; Set all in-memory hbut attributes for any button at point
(ibut:at-p)
(let ((lbl-key (hattr:get 'hbut:current 'lbl-key)))
(unless (and (stringp lbl-key) (not (string-empty-p lbl-key)))
(hypb:error "(ibut:operate): hbut:current lbl-key must be non-nil")))
(run-hooks (if edit-flag 'ibut-edit-hook 'ibut-create-hook))
;; Position point
(let ((new-key (ibut:label-to-key new-name)))
(cond ((and new-key (equal (ibut:label-p) new-key))
;; In case right before the start of the desired
;; button's delimiters.
(goto-char (min (+ (point) 2) (point-max)))
(when (search-backward ibut:label-start nil t)
(goto-char (match-end 0))))
(new-key
(let ((regexp (ibut:name-regexp new-key)))
(or (re-search-forward regexp nil t)
(re-search-backward regexp nil t)))
(goto-char (+ (match-beginning 0) (length ibut:label-start))))))
;; instance-flag might be 't which we don't want to return.
(when (stringp instance-flag) instance-flag)))