X-Git-Url: https://git.hcoop.net/bpt/emacs.git/blobdiff_plain/51ec3f09e45ca399c95108b6e486535012640743..c6678f2916c22cd3b16232dc941f318c894ef56e:/lisp/org/org-irc.el diff --git a/lisp/org/org-irc.el b/lisp/org/org-irc.el dissimilarity index 70% index d880eda9b4..5d83634733 100644 --- a/lisp/org/org-irc.el +++ b/lisp/org/org-irc.el @@ -1,228 +1,256 @@ -;;; org-irc.el --- Store links to IRC sessions - -;; Copyright (C) 2008 Free Software Foundation, Inc. - -;; Author: Philip Jackson -;; Keywords: erc, irc, link, org -;; Version: 1.3 - -;; 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, 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 -;; 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; see the file COPYING. If not, write to the -;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -;; Boston, MA 02110-1301, USA. - -;;; Commentary: - -;; Link to an IRC session. Only ERC has been implemented at the -;; moment. -;; -;; This file is loaded by default whenever org.el is loaded. Please -;; customize the variable `org-default-extensions' 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 diffficult 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.freenode.net/#emacs/bob][chat with bob in #emacs on freenode]] -;; [[irc:/irc.freenode.net/#emacs][#emacs on freenode]] -;; [[irc:/irc.freenode.net/]] -;; -;; If, when the resulting link is visited, there is no connection to a -;; requested server then one will be created. - -;;; Code: - -(require 'org) -(require 'erc) -(require 'erc-log) - -(defvar org-irc-client 'erc - "The IRC client to act on") -(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-server-announced-name) ; dynamically scoped form erc-backend.el - -;; Generic functions/config (extend these for other clients) - -(add-to-list 'org-store-link-functions - 'org-irc-store-link) - -(org-add-link-type "irc" 'org-irc-visit nil) - -(defun org-irc-visit (link) - "Dispatch to the correct visit function based on the client" - (let ((link (org-irc-parse-link link))) - (cond - ((eq org-irc-client 'erc) - (org-irc-visit-erc link)) - (t - (error "erc only known client"))))) - -(defun org-irc-parse-link (link) - "Get a of irc link attributes where `link' looks like -server:port/chan/user (port, chan and user being optional)." - (let* ((parts (split-string link "/" t)) - (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 -(defun org-irc-store-link () - "Dispatch to the appropreate function to store a link to -something IRC related" - (cond - ((eq major-mode 'erc-mode) - (org-irc-erc-store-link)))) - -(defun org-irc-elipsify-description (string &optional after) - "Strip starting and ending whitespace and replace any chars -that appear after the value in `after' with '...'" - (let* ((after (number-to-string (or after 30))) - (replace-map (list (cons "^[ \t]*" "") - (cons "[ \t]*$" "") - (cons (concat "^\\(.\\{" after - "\\}\\).*") "\\1...")))) - (mapc (lambda (x) - (when (string-match (car x) string) - (setq string (replace-match (cdr x) nil nil string)))) - replace-map) - string)) - -;; ERC specific functions - -(defun org-irc-erc-get-line-from-log (erc-line) - "Find the most suitable line to link to from the erc logs. 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) - (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)) - (progn - (goto-char (point-at-bol)) - (when (search-backward-regexp "^[^ ]" nil t) - (buffer-substring-no-properties (point-at-bol) - (point-at-eol)))) - (when (search-backward erc-line nil t) - (buffer-substring-no-properties (point-at-bol) - (point-at-eol))))))) - -(defun org-irc-erc-store-link () - "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." - (if org-irc-link-to-logs - (let* ((erc-line (buffer-substring-no-properties - (point-at-bol) (point-at-eol))) - (parsed-line (org-irc-erc-get-line-from-log erc-line))) - (if (erc-logging-enabled nil) - (progn - (org-store-link-props - :type "file" - :description (concat "'" (org-irc-elipsify-description - (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 - (org-store-link-props - :type "irc" - :link (org-make-link "irc:/" link-text) - :description (concat "irc session '" link-text "'") - :server (car (car link)) - :port (or (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" - (let ((link (concat erc-server-announced-name ":" - (number-to-string erc-session-port)))) - (concat link "/" - (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))))) - -(defun org-irc-visit-erc (link) - "Visit an ERC buffer based on criteria from the followed link" - (let* ((server (car (car link))) - (port (or (cadr (pop link)) erc-default-port)) - (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 - (string= erc-session-port port) - (string= erc-server-announced-name server) - (setq server-buffer tmp-server-buf))))))))) - (if buffer-list - (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 - (switch-to-buffer chan-buf) - ;; 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 - (switch-to-buffer server-buffer) - (erc-cmd-JOIN chan-name)))) - (switch-to-buffer server-buffer))) - ;; no server match, make new connection - (erc-select :server server :port port)))) - -(provide 'org-irc) - -;; arch-tag: 018d7dda-53b8-4a35-ba92-6670939e525a -;;; org-irc.el ends here +;;; org-irc.el --- Store links to IRC sessions +;; +;; Copyright (C) 2008, 2009, 2010, 2011 Free Software Foundation, Inc. +;; +;; Author: Philip Jackson +;; Keywords: erc, irc, link, org +;; Version: 6.33x +;; +;; 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 +;; 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 . + +;;; 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'. +;; +;; 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.freenode.net/#emacs/bob][chat with bob in #emacs on freenode]] +;; [[irc:/irc.freenode.net/#emacs][#emacs on freenode]] +;; [[irc:/irc.freenode.net/]] +;; +;; If, when the resulting link is visited, there is no connection to a +;; requested server then one will be created. + +;;; Code: + +(require 'org) + +;; Declare the function form ERC that we use. +(declare-function erc-current-logfile "erc-log" (&optional buffer)) +(declare-function erc-prompt "erc" ()) +(declare-function erc-default-target "erc" ()) +(declare-function erc-channel-p "erc" (channel)) +(declare-function erc-buffer-filter "erc" (predicate &optional proc)) +(declare-function erc-server-buffer "erc" ()) +(declare-function erc-get-server-nickname-list "erc" ()) +(declare-function erc-cmd-JOIN "erc" (channel &optional key)) + +(defvar org-irc-client 'erc + "The IRC client to act on.") +(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) + +(add-to-list 'org-store-link-functions 'org-irc-store-link) + +(org-add-link-type "irc" 'org-irc-visit nil) + +(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 + (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)) + (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 +(defun org-irc-store-link () + "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-elipsify-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))) + (replace-map (list (cons "^[ \t]*" "") + (cons "[ \t]*$" "") + (cons (concat "^\\(.\\{" after + "\\}\\).*") "\\1...")))) + (mapc (lambda (x) + (when (string-match (car x) string) + (setq string (replace-match (cdr x) nil nil string)))) + replace-map) + 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)) + (progn + (goto-char (point-at-bol)) + (when (search-backward-regexp "^[^ ]" nil t) + (buffer-substring-no-properties (point-at-bol) + (point-at-eol)))) + (when (search-backward erc-line nil t) + (buffer-substring-no-properties (point-at-bol) + (point-at-eol))))))) + +(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 + (point-at-bol) (point-at-eol))) + (parsed-line (org-irc-erc-get-line-from-log erc-line))) + (if (erc-logging-enabled nil) + (progn + (org-store-link-props + :type "file" + :description (concat "'" (org-irc-elipsify-description + (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 + (org-store-link-props + :type "irc" + :link (org-make-link "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." + (let* ((session-port (if (numberp erc-session-port) + (number-to-string erc-session-port) + erc-session-port)) + (link (concat erc-session-server ":" session-port))) + (concat link "/" + (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))))) + +(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." + (cond + ((stringp erc-session-port) + (string-to-number erc-session-port)) + ((numberp erc-session-port) + erc-session-port) + (t + erc-default-port))) + +(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 (or (string-to-number (cadr (pop link))) erc-default-port)) + (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 + (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 + (switch-to-buffer chan-buf) + ;; 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 + (switch-to-buffer server-buffer) + (erc-cmd-JOIN chan-name)))) + (switch-to-buffer server-buffer))) + ;; no server match, make new connection + (erc-select :server server :port port)))) + +(provide 'org-irc) + +;; arch-tag: 018d7dda-53b8-4a35-ba92-6670939e525a + +;;; org-irc.el ends here