1
0
mirror of https://git.savannah.gnu.org/git/emacs/org-mode.git synced 2024-11-24 07:20:29 +00:00
org-mode/lisp/ol-irc.el

273 lines
9.6 KiB
EmacsLisp
Raw Permalink Normal View History

;;; ol-irc.el --- Links to IRC Sessions -*- lexical-binding: t; -*-
;;
;; Copyright (C) 2008-2024 Free Software Foundation, Inc.
;;
;; Author: Philip Jackson <emacs@shellarchive.co.uk>
;; Keywords: erc, irc, link, org
;;
;; This file is part of GNU Emacs.
;;
;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
2008-12-16 16:10:13 +00:00
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; This file implements links to an IRC session from within Org mode.
;; Org mode loads this module by default - if this is not what you want,
;; configure the variable `org-modules'.
;;
2008-11-13 13:40:14 +00:00
;; Please customize the variable `org-modules' to select
;; extensions you would like to use, and to deselect those which you don't
;; want.
;;
;; Please note that at the moment only ERC is supported. Other clients
;; shouldn't be difficult to add though.
;;
;; Then set `org-irc-link-to-logs' to non-nil if you would like a
;; file:/ type link to be created to the current line in the logs or
;; to t if you would like to create an irc:/ style link.
;;
;; Links within an org buffer might look like this:
;;
;; [[irc:/irc.libera.chat/#emacs/bob][chat with bob in #emacs on Libera.Chat]]
;; [[irc:/irc.libera.chat/#emacs][#emacs on Libera.Chat]]
;; [[irc:/irc.libera.chat/]]
;;
;; If, when the resulting link is visited, there is no connection to a
;; requested server then one will be created.
;;; Code:
(require 'org-macs)
(org-assert-version)
Move link-related core functions out of "org.el" * contrib/lisp/org-wl.el (org-wl-store-link-message): * lisp/Makefile (clean-install): * lisp/ob-core.el (org-link-bracket-re): (org-babel-open-src-block-result): (org-babel-read-element): (org-babel-read-link): (org-babel-result-end): * lisp/ob-tangle.el (org-link-bracket-re): (org-babel-tangle-single-block): (org-link-analytic-bracket-re): (org-babel-detangle): (org-babel-tangle-jump-to-org): * lisp/ol.el: * lisp/org-agenda.el (org-agenda-get-some-entry-text): (org-diary): (org-agenda-format-item): (org-agenda-open-link): (org-agenda-switch-to): (org-agenda-to-appt): * lisp/org-bbdb.el (org-bbdb-store-link): * lisp/org-bibtex.el (org-bibtex-store-link): * lisp/org-capture.el (org-capture-fill-template): * lisp/org-clock.el (org-clocktable-write-default): (org-clock-get-table-data): * lisp/org-compat.el (org-doi-server-url): (org-email-link-description-format): (org-make-link-description-function): (org-from-is-user-regexp): (org-descriptive-links): (org-url-hexify-p): (org-context-in-file-links): (org-keep-stored-link-after-insertion): (org-display-internal-link-with-indirect-buffer): (org-confirm-shell-link-function): (org-confirm-shell-link-not-regexp): (org-confirm-elisp-link-function): (org-confirm-elisp-link-not-regexp): (org-file-complete-link): (org-email-link-description): (org-make-link-string): (org-store-link-props): (org-add-link-props): (org-make-link-regexps): (org-angle-link-re): (org-plain-link-re): (org-bracket-link-regexp): (org-bracket-link-analytic-regexp): (org-any-link-re): * lisp/org-docview.el (org-docview-store-link): (org-docview-complete-link): * lisp/org-element.el (org-element-link-parser): * lisp/org-eshell.el (org-eshell-store-link): * lisp/org-eww.el (org-eww-store-link): (org-eww-copy-for-org-mode): * lisp/org-footnote.el (org-footnote-next-reference-or-definition): * lisp/org-gnus.el (org-gnus-article-link): (org-gnus-store-link): * lisp/org-id.el (org-id-store-link): * lisp/org-info.el (org-info-store-link): * lisp/org-irc.el (org-irc-erc-store-link): * lisp/org-mhe.el (org-mhe-store-link): * lisp/org-pcomplete.el (pcomplete/org-mode/searchhead): * lisp/org-protocol.el (org-protocol-do-capture): * lisp/org-rmail.el (org-rmail-store-link): * lisp/org-w3m.el (org-w3m-store-link): (org-w3m-copy-for-org-mode):
2018-11-26 23:04:41 +00:00
(require 'ol)
2017-12-04 17:55:03 +00:00
(declare-function erc-buffer-filter "erc" (predicate &optional proc))
(declare-function erc-channel-p "erc" (channel))
(declare-function erc-cmd-JOIN "erc" (channel &optional key))
(declare-function erc-current-logfile "erc-log" (&optional buffer))
(declare-function erc-default-target "erc" ())
(declare-function erc-get-server-nickname-list "erc" ())
2017-12-04 17:55:03 +00:00
(declare-function erc-logging-enabled "erc-log" (&optional buffer))
(declare-function erc-prompt "erc" ())
(declare-function erc-save-buffer-in-logs "erc-log" (&optional buffer))
(declare-function erc-server-buffer "erc" ())
2008-02-28 08:34:56 +00:00
(defvar org-irc-client 'erc
"The IRC client to act on.")
2017-12-04 17:55:03 +00:00
(defvar org-irc-link-to-logs nil
"Non-nil will store a link to the logs, nil will store an irc: style link.")
(defvar erc-default-port) ; dynamically scoped from erc.el
(defvar erc-session-port) ; dynamically scoped form erc-backend.el
(defvar erc-session-server) ; dynamically scoped form erc-backend.el
;; Generic functions/config (extend these for other clients)
(org-link-set-parameters "irc"
:follow #'org-irc-visit
:store #'org-irc-store-link
:export #'org-irc-export)
(defun org-irc-visit (link _)
"Parse LINK and dispatch to the correct function based on the client found."
(let ((link (org-irc-parse-link link)))
(cond
((eq org-irc-client 'erc)
(org-irc-visit-erc link))
(t
2012-08-13 03:59:44 +00:00
(error "ERC only known client")))))
(defun org-irc-parse-link (link)
"Parse an IRC LINK and return the attributes found.
Parse a LINK that looks like server:port/chan/user (port, chan
and user being optional) and return any of the port, channel or user
attributes that are found."
(let* ((parts (split-string link "/" t))
2008-12-16 16:10:13 +00:00
(len (length parts)))
(when (or (< len 1) (> len 3))
(error "Failed to parse link needed 1-3 parts, got %d" len))
(setcar parts (split-string (car parts) ":" t))
parts))
;;;###autoload
org-id.el: Add search strings, inherit parent IDs * lisp/ol.el (org-store-link): Refactor org-id links to use standard `org-store-link-functions'. (org-link-search): Create new headings at appropriate level. (org-link-precise-link-target): New function extracting logic to identify a precise link target, e.g. a heading, named object, or text search. (org-link-try-link-store-functions): Extract logic to call external link store functions. Pass them a new `interactive?' argument. * lisp/ol-bbdb.el (org-bbdb-store-link): * lisp/ol-bibtex.el (org-bibtex-store-link): * lisp/ol-docview.el (org-docview-store-link): * lisp/ol-eshell.el (org-eshell-store-link): * lisp/ol-eww.el (org-eww-store-link): * lisp/ol-gnus.el (org-gnus-store-link): * lisp/ol-info.el (org-info-store-link): * lisp/ol-irc.el (org-irc-store-link): * lisp/ol-man.el (org-man-store-link): * lisp/ol-mhe.el (org-mhe-store-link): * lisp/ol-rmail.el (org-rmail-store-link): Accept optional arg. * lisp/org-id.el (org-id-link-consider-parent-id): New option to allow a parent heading with an id to be considered as a link target. (org-id-link-use-context): New option to add context to org-id links. (org-id-get): Add optional `inherit' argument which considers parents' IDs if the current entry does not have one. (org-id-store-link): Consider IDs of parent headings as link targets when current heading has no ID and `org-id-link-consider-parent-id' is set. Add a search string to the link when enabled. (org-id-store-link-maybe): Function set as :store option for custom id link property. Move logic from `org-store-link' here to determine when an org-id link should be stored using `org-id-store-link'. (org-id-open): Recognise search strings after "::" in org-id links. * lisp/org-lint.el: Add checker for "::" in ID properties. * testing/lisp/test-ol.el: Add tests for `org-link-precise-link-target' and `org-id-store-link' functions, testing new options. * doc/org-manual.org: Update documentation about links. * etc/ORG-NEWS: Document changes and new options. These feature allows for more precise links when using org-id to link to org headings, without requiring every single headline to have an id. Link: https://list.orgmode.org/118435e8-0b20-46fd-af6a-88de8e19fac6@app.fastmail.com/
2023-11-19 14:52:05 +00:00
(defun org-irc-store-link (&optional _interactive?)
"Dispatch to the appropriate function to store a link to an IRC session."
(cond
((eq major-mode 'erc-mode)
(org-irc-erc-store-link))))
(defun org-irc-ellipsify-description (string &optional after)
"Remove unnecessary white space from STRING and add ellipses if necessary.
Strip starting and ending white space from STRING and replace any
chars that the value AFTER with `...'"
(let* ((after (number-to-string (or after 30)))
2008-12-16 16:10:13 +00:00
(replace-map (list (cons "^[ \t]*" "")
(cons "[ \t]*$" "")
(cons (concat "^\\(.\\{" after
"\\}\\).*") "\\1..."))))
(dolist (x replace-map string)
(when (string-match (car x) string)
(setq string (replace-match (cdr x) nil nil string))))))
;; ERC specific functions
(defun org-irc-erc-get-line-from-log (erc-line)
"Find the best line to link to from the ERC logs given ERC-LINE as a start.
If the user is on the ERC-prompt then search backward for the
first non-blank line, otherwise return the current line. The
result is a cons of the filename and search string."
(erc-save-buffer-in-logs)
(require 'erc-log)
(with-current-buffer (find-file-noselect (erc-current-logfile))
(goto-char (point-max))
(list
(abbreviate-file-name buffer-file-name)
;; can we get a '::' part?
(if (string= erc-line (erc-prompt))
2008-12-16 16:10:13 +00:00
(progn
(goto-char (line-beginning-position))
2008-12-16 16:10:13 +00:00
(when (search-backward-regexp "^[^ ]" nil t)
(buffer-substring-no-properties (line-beginning-position)
(line-end-position))))
(when (search-backward erc-line nil t)
(buffer-substring-no-properties (line-beginning-position)
(line-end-position)))))))
(defun org-irc-erc-store-link ()
"Store a link to the IRC log file or the session itself.
Depending on the variable `org-irc-link-to-logs' store either a
link to the log file for the current session or an irc: link to
the session itself."
(require 'erc-log)
(if org-irc-link-to-logs
(let* ((erc-line (buffer-substring-no-properties
(line-beginning-position) (line-end-position)))
2008-12-16 16:10:13 +00:00
(parsed-line (org-irc-erc-get-line-from-log erc-line)))
(if (erc-logging-enabled nil)
(progn
Move link-related core functions out of "org.el" * contrib/lisp/org-wl.el (org-wl-store-link-message): * lisp/Makefile (clean-install): * lisp/ob-core.el (org-link-bracket-re): (org-babel-open-src-block-result): (org-babel-read-element): (org-babel-read-link): (org-babel-result-end): * lisp/ob-tangle.el (org-link-bracket-re): (org-babel-tangle-single-block): (org-link-analytic-bracket-re): (org-babel-detangle): (org-babel-tangle-jump-to-org): * lisp/ol.el: * lisp/org-agenda.el (org-agenda-get-some-entry-text): (org-diary): (org-agenda-format-item): (org-agenda-open-link): (org-agenda-switch-to): (org-agenda-to-appt): * lisp/org-bbdb.el (org-bbdb-store-link): * lisp/org-bibtex.el (org-bibtex-store-link): * lisp/org-capture.el (org-capture-fill-template): * lisp/org-clock.el (org-clocktable-write-default): (org-clock-get-table-data): * lisp/org-compat.el (org-doi-server-url): (org-email-link-description-format): (org-make-link-description-function): (org-from-is-user-regexp): (org-descriptive-links): (org-url-hexify-p): (org-context-in-file-links): (org-keep-stored-link-after-insertion): (org-display-internal-link-with-indirect-buffer): (org-confirm-shell-link-function): (org-confirm-shell-link-not-regexp): (org-confirm-elisp-link-function): (org-confirm-elisp-link-not-regexp): (org-file-complete-link): (org-email-link-description): (org-make-link-string): (org-store-link-props): (org-add-link-props): (org-make-link-regexps): (org-angle-link-re): (org-plain-link-re): (org-bracket-link-regexp): (org-bracket-link-analytic-regexp): (org-any-link-re): * lisp/org-docview.el (org-docview-store-link): (org-docview-complete-link): * lisp/org-element.el (org-element-link-parser): * lisp/org-eshell.el (org-eshell-store-link): * lisp/org-eww.el (org-eww-store-link): (org-eww-copy-for-org-mode): * lisp/org-footnote.el (org-footnote-next-reference-or-definition): * lisp/org-gnus.el (org-gnus-article-link): (org-gnus-store-link): * lisp/org-id.el (org-id-store-link): * lisp/org-info.el (org-info-store-link): * lisp/org-irc.el (org-irc-erc-store-link): * lisp/org-mhe.el (org-mhe-store-link): * lisp/org-pcomplete.el (pcomplete/org-mode/searchhead): * lisp/org-protocol.el (org-protocol-do-capture): * lisp/org-rmail.el (org-rmail-store-link): * lisp/org-w3m.el (org-w3m-store-link): (org-w3m-copy-for-org-mode):
2018-11-26 23:04:41 +00:00
(org-link-store-props
2008-12-16 16:10:13 +00:00
:type "file"
:description (concat "'" (org-irc-ellipsify-description
2008-12-16 16:10:13 +00:00
(cadr parsed-line) 20)
"' from an IRC conversation")
:link (concat "file:" (car parsed-line) "::"
(cadr parsed-line)))
t)
(error "This ERC session is not being logged")))
(let* ((link-text (org-irc-get-erc-link))
(link (org-irc-parse-link link-text)))
(if link-text
(progn
Move link-related core functions out of "org.el" * contrib/lisp/org-wl.el (org-wl-store-link-message): * lisp/Makefile (clean-install): * lisp/ob-core.el (org-link-bracket-re): (org-babel-open-src-block-result): (org-babel-read-element): (org-babel-read-link): (org-babel-result-end): * lisp/ob-tangle.el (org-link-bracket-re): (org-babel-tangle-single-block): (org-link-analytic-bracket-re): (org-babel-detangle): (org-babel-tangle-jump-to-org): * lisp/ol.el: * lisp/org-agenda.el (org-agenda-get-some-entry-text): (org-diary): (org-agenda-format-item): (org-agenda-open-link): (org-agenda-switch-to): (org-agenda-to-appt): * lisp/org-bbdb.el (org-bbdb-store-link): * lisp/org-bibtex.el (org-bibtex-store-link): * lisp/org-capture.el (org-capture-fill-template): * lisp/org-clock.el (org-clocktable-write-default): (org-clock-get-table-data): * lisp/org-compat.el (org-doi-server-url): (org-email-link-description-format): (org-make-link-description-function): (org-from-is-user-regexp): (org-descriptive-links): (org-url-hexify-p): (org-context-in-file-links): (org-keep-stored-link-after-insertion): (org-display-internal-link-with-indirect-buffer): (org-confirm-shell-link-function): (org-confirm-shell-link-not-regexp): (org-confirm-elisp-link-function): (org-confirm-elisp-link-not-regexp): (org-file-complete-link): (org-email-link-description): (org-make-link-string): (org-store-link-props): (org-add-link-props): (org-make-link-regexps): (org-angle-link-re): (org-plain-link-re): (org-bracket-link-regexp): (org-bracket-link-analytic-regexp): (org-any-link-re): * lisp/org-docview.el (org-docview-store-link): (org-docview-complete-link): * lisp/org-element.el (org-element-link-parser): * lisp/org-eshell.el (org-eshell-store-link): * lisp/org-eww.el (org-eww-store-link): (org-eww-copy-for-org-mode): * lisp/org-footnote.el (org-footnote-next-reference-or-definition): * lisp/org-gnus.el (org-gnus-article-link): (org-gnus-store-link): * lisp/org-id.el (org-id-store-link): * lisp/org-info.el (org-info-store-link): * lisp/org-irc.el (org-irc-erc-store-link): * lisp/org-mhe.el (org-mhe-store-link): * lisp/org-pcomplete.el (pcomplete/org-mode/searchhead): * lisp/org-protocol.el (org-protocol-do-capture): * lisp/org-rmail.el (org-rmail-store-link): * lisp/org-w3m.el (org-w3m-store-link): (org-w3m-copy-for-org-mode):
2018-11-26 23:04:41 +00:00
(org-link-store-props
:type "irc"
:link (concat "irc:/" link-text)
:description (concat "irc session `" link-text "'")
:server (car (car link))
:port (or (string-to-number (cadr (pop link))) erc-default-port)
:nick (pop link))
t)
(error "Failed to create ('irc:/' style) ERC link")))))
(defun org-irc-get-erc-link ()
"Return an org compatible irc:/ link from an ERC buffer."
2008-03-13 14:47:10 +00:00
(let* ((session-port (if (numberp erc-session-port)
2008-12-16 16:10:13 +00:00
(number-to-string erc-session-port)
erc-session-port))
(link (concat erc-session-server ":" session-port)))
(concat link "/"
2008-12-16 16:10:13 +00:00
(if (and (erc-default-target)
(erc-channel-p (erc-default-target))
(car (get-text-property (point) 'erc-data)))
;; we can get a nick
(let ((nick (car (get-text-property (point) 'erc-data))))
(concat (erc-default-target) "/" nick))
(erc-default-target)))))
2008-03-13 14:47:10 +00:00
(defun org-irc-get-current-erc-port ()
"Return the current port as a number.
Return the current port number or, if none is set, return the ERC
default."
2008-03-13 14:47:10 +00:00
(cond
((stringp erc-session-port)
(string-to-number erc-session-port))
((numberp erc-session-port)
erc-session-port)
(t
erc-default-port)))
2008-03-13 14:47:10 +00:00
(defun org-irc-visit-erc (link)
"Visit an ERC buffer based on criteria found in LINK."
(require 'erc)
(require 'erc-log)
(let* ((server (car (car link)))
(port (let ((p (cadr (pop link))))
(if p (string-to-number p) erc-default-port)))
2008-12-16 16:10:13 +00:00
(server-buffer)
(buffer-list
(erc-buffer-filter
(lambda nil
(let ((tmp-server-buf (erc-server-buffer)))
(and tmp-server-buf
(with-current-buffer tmp-server-buf
(and
(eq (org-irc-get-current-erc-port) port)
(string= erc-session-server server)
(setq server-buffer tmp-server-buf)))))))))
(if buffer-list
2008-12-16 16:10:13 +00:00
(let ((chan-name (pop link)))
;; if we got a channel name then switch to it or join it
(if chan-name
(let ((chan-buf (catch 'found
(dolist (x buffer-list)
(if (string= (buffer-name x) chan-name)
(throw 'found x))))))
(if chan-buf
(progn
(pop-to-buffer-same-window chan-buf)
2008-12-16 16:10:13 +00:00
;; if we got a nick, and they're in the chan,
;; then start a chat with them
(let ((nick (pop link)))
(when nick
(if (member nick (erc-get-server-nickname-list))
(progn
(goto-char (point-max))
(insert (concat nick ": ")))
(error "%s not found in %s" nick chan-name)))))
(progn
(pop-to-buffer-same-window server-buffer)
(erc-cmd-JOIN chan-name))))
(pop-to-buffer-same-window server-buffer)))
;; no server match, make new connection
(erc-select :server server :port port))))
(defun org-irc-export (link description format)
"Export an IRC link.
See `org-link-parameters' for details about LINK, DESCRIPTION and
FORMAT."
(let ((desc (or description link)))
(pcase format
(`html (format "<a href=\"irc:%s\">%s</a>" link desc))
(`md (format "[%s](irc:%s)" desc link))
(_ nil))))
(provide 'ol-irc)
;; Local variables:
;; generated-autoload-file: "org-loaddefs.el"
;; End:
;;; ol-irc.el ends here