Commit | Line | Data |
---|---|---|
0fc0f178 | 1 | ;;; org-irc.el --- Store links to IRC sessions |
20908596 | 2 | ;; |
0fc0f178 | 3 | ;; Copyright (C) 2008 Free Software Foundation, Inc. |
20908596 | 4 | ;; |
0fc0f178 CD |
5 | ;; Author: Philip Jackson <emacs@shellarchive.co.uk> |
6 | ;; Keywords: erc, irc, link, org | |
ce4fdcb9 | 7 | ;; Version: 6.13 |
20908596 | 8 | ;; |
0fc0f178 | 9 | ;; This file is part of GNU Emacs. |
20908596 | 10 | ;; |
b1fc2b50 | 11 | ;; GNU Emacs is free software: you can redistribute it and/or modify |
0fc0f178 | 12 | ;; it under the terms of the GNU General Public License as published by |
b1fc2b50 GM |
13 | ;; the Free Software Foundation, either version 3 of the License, or |
14 | ;; (at your option) any later version. | |
0fc0f178 CD |
15 | |
16 | ;; GNU Emacs is distributed in the hope that it will be useful, | |
17 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
19 | ;; GNU General Public License for more details. | |
20 | ||
21 | ;; You should have received a copy of the GNU General Public License | |
b1fc2b50 | 22 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. |
c1fbdbbb | 23 | |
0fc0f178 | 24 | ;;; Commentary: |
c1fbdbbb | 25 | |
20908596 CD |
26 | ;; This file implements links to an IRC session from within Org-mode. |
27 | ;; Org-mode loads this module by default - if this is not what you want, | |
28 | ;; configure the variable `org-modules'. | |
0fc0f178 | 29 | ;; |
ce4fdcb9 | 30 | ;; Please customize the variable `org-modules' to select |
20908596 CD |
31 | ;; extensions you would like to use, and to deselect those which you don't |
32 | ;; want. | |
0fc0f178 | 33 | ;; |
20908596 CD |
34 | ;; Please note that at the moment only ERC is supported. Other clients |
35 | ;; shouldn't be difficult to add though. | |
0fc0f178 CD |
36 | ;; |
37 | ;; Then set `org-irc-link-to-logs' to non-nil if you would like a | |
38 | ;; file:/ type link to be created to the current line in the logs or | |
39 | ;; to t if you would like to create an irc:/ style link. | |
40 | ;; | |
41 | ;; Links within an org buffer might look like this: | |
42 | ;; | |
43 | ;; [[irc:/irc.freenode.net/#emacs/bob][chat with bob in #emacs on freenode]] | |
44 | ;; [[irc:/irc.freenode.net/#emacs][#emacs on freenode]] | |
45 | ;; [[irc:/irc.freenode.net/]] | |
46 | ;; | |
47 | ;; If, when the resulting link is visited, there is no connection to a | |
48 | ;; requested server then one will be created. | |
0fc0f178 | 49 | |
c1fbdbbb | 50 | ;;; Code: |
0fc0f178 CD |
51 | |
52 | (require 'org) | |
20908596 CD |
53 | |
54 | ;; Declare the function fomr ERC that we use. | |
55 | (declare-function erc-current-logfile "erc-log" (&optional buffer)) | |
56 | (declare-function erc-prompt "erc" ()) | |
57 | (declare-function erc-default-target "erc" ()) | |
58 | (declare-function erc-channel-p "erc" (channel)) | |
59 | (declare-function erc-buffer-filter "erc" (predicate &optional proc)) | |
60 | (declare-function erc-server-buffer "erc" ()) | |
61 | (declare-function erc-get-server-nickname-list "erc" ()) | |
62 | (declare-function erc-cmd-JOIN "erc" (channel &optional key)) | |
0fc0f178 CD |
63 | |
64 | (defvar org-irc-client 'erc | |
20908596 | 65 | "The IRC client to act on.") |
0fc0f178 | 66 | (defvar org-irc-link-to-logs nil |
20908596 | 67 | "Non-nil will store a link to the logs, nil will store an irc: style link.") |
0fc0f178 | 68 | |
20908596 CD |
69 | (defvar erc-default-port) ; dynamically scoped from erc.el |
70 | (defvar erc-session-port) ; dynamically scoped form erc-backend.el | |
71 | (defvar erc-session-server) ; dynamically scoped form erc-backend.el | |
0fc0f178 CD |
72 | |
73 | ;; Generic functions/config (extend these for other clients) | |
74 | ||
20908596 | 75 | (add-to-list 'org-store-link-functions 'org-irc-store-link) |
0fc0f178 CD |
76 | |
77 | (org-add-link-type "irc" 'org-irc-visit nil) | |
78 | ||
79 | (defun org-irc-visit (link) | |
20908596 | 80 | "Parse LINK and dispatch to the correct function based on the client found." |
0fc0f178 CD |
81 | (let ((link (org-irc-parse-link link))) |
82 | (cond | |
83 | ((eq org-irc-client 'erc) | |
84 | (org-irc-visit-erc link)) | |
85 | (t | |
86 | (error "erc only known client"))))) | |
87 | ||
88 | (defun org-irc-parse-link (link) | |
20908596 CD |
89 | "Parse an IRC LINK and return the attributes found. |
90 | Parse a LINK that looks like server:port/chan/user (port, chan | |
e8cb0ef8 | 91 | and user being optional) and return any of the port, channel or user |
20908596 | 92 | attributes that are found." |
0fc0f178 CD |
93 | (let* ((parts (split-string link "/" t)) |
94 | (len (length parts))) | |
95 | (when (or (< len 1) (> len 3)) | |
20908596 | 96 | (error "Failed to parse link needed 1-3 parts, got %d" len)) |
0fc0f178 CD |
97 | (setcar parts (split-string (car parts) ":" t)) |
98 | parts)) | |
99 | ||
100 | ;;;###autoload | |
101 | (defun org-irc-store-link () | |
20908596 | 102 | "Dispatch to the appropriate function to store a link to an IRC session." |
0fc0f178 CD |
103 | (cond |
104 | ((eq major-mode 'erc-mode) | |
105 | (org-irc-erc-store-link)))) | |
106 | ||
107 | (defun org-irc-elipsify-description (string &optional after) | |
20908596 CD |
108 | "Remove unnecessary white space from STRING and add ellipses if necessary. |
109 | Strip starting and ending white space from STRING and replace any | |
110 | chars that the value AFTER with '...'" | |
0fc0f178 CD |
111 | (let* ((after (number-to-string (or after 30))) |
112 | (replace-map (list (cons "^[ \t]*" "") | |
113 | (cons "[ \t]*$" "") | |
114 | (cons (concat "^\\(.\\{" after | |
115 | "\\}\\).*") "\\1...")))) | |
116 | (mapc (lambda (x) | |
117 | (when (string-match (car x) string) | |
118 | (setq string (replace-match (cdr x) nil nil string)))) | |
119 | replace-map) | |
120 | string)) | |
121 | ||
122 | ;; ERC specific functions | |
123 | ||
124 | (defun org-irc-erc-get-line-from-log (erc-line) | |
20908596 CD |
125 | "Find the best line to link to from the ERC logs given ERC-LINE as a start. |
126 | If the user is on the ERC-prompt then search backward for the | |
127 | first non-blank line, otherwise return the current line. The | |
128 | result is a cons of the filename and search string." | |
0fc0f178 | 129 | (erc-save-buffer-in-logs) |
20908596 | 130 | (require 'erc-log) |
0fc0f178 CD |
131 | (with-current-buffer (find-file-noselect (erc-current-logfile)) |
132 | (goto-char (point-max)) | |
133 | (list | |
134 | (abbreviate-file-name buffer-file-name) | |
135 | ;; can we get a '::' part? | |
136 | (if (string= erc-line (erc-prompt)) | |
137 | (progn | |
138 | (goto-char (point-at-bol)) | |
139 | (when (search-backward-regexp "^[^ ]" nil t) | |
140 | (buffer-substring-no-properties (point-at-bol) | |
141 | (point-at-eol)))) | |
142 | (when (search-backward erc-line nil t) | |
143 | (buffer-substring-no-properties (point-at-bol) | |
144 | (point-at-eol))))))) | |
145 | ||
146 | (defun org-irc-erc-store-link () | |
20908596 CD |
147 | "Store a link to the IRC log file or the session itself. |
148 | Depending on the variable `org-irc-link-to-logs' store either a | |
149 | link to the log file for the current session or an irc: link to | |
0fc0f178 | 150 | the session itself." |
20908596 | 151 | (require 'erc-log) |
0fc0f178 CD |
152 | (if org-irc-link-to-logs |
153 | (let* ((erc-line (buffer-substring-no-properties | |
154 | (point-at-bol) (point-at-eol))) | |
155 | (parsed-line (org-irc-erc-get-line-from-log erc-line))) | |
156 | (if (erc-logging-enabled nil) | |
157 | (progn | |
158 | (org-store-link-props | |
159 | :type "file" | |
160 | :description (concat "'" (org-irc-elipsify-description | |
161 | (cadr parsed-line) 20) | |
162 | "' from an IRC conversation") | |
163 | :link (concat "file:" (car parsed-line) "::" | |
164 | (cadr parsed-line))) | |
165 | t) | |
166 | (error "This ERC session is not being logged"))) | |
167 | (let* ((link-text (org-irc-get-erc-link)) | |
168 | (link (org-irc-parse-link link-text))) | |
169 | (if link-text | |
170 | (progn | |
171 | (org-store-link-props | |
172 | :type "irc" | |
173 | :link (org-make-link "irc:/" link-text) | |
174 | :description (concat "irc session '" link-text "'") | |
175 | :server (car (car link)) | |
20908596 | 176 | :port (or (string-to-number (cadr (pop link))) erc-default-port) |
0fc0f178 CD |
177 | :nick (pop link)) |
178 | t) | |
179 | (error "Failed to create ('irc:/' style) ERC link"))))) | |
180 | ||
181 | (defun org-irc-get-erc-link () | |
20908596 CD |
182 | "Return an org compatible irc:/ link from an ERC buffer." |
183 | (let* ((session-port (if (numberp erc-session-port) | |
184 | (number-to-string erc-session-port) | |
185 | erc-session-port)) | |
186 | (link (concat erc-session-server ":" session-port))) | |
0fc0f178 CD |
187 | (concat link "/" |
188 | (if (and (erc-default-target) | |
189 | (erc-channel-p (erc-default-target)) | |
190 | (car (get-text-property (point) 'erc-data))) | |
191 | ;; we can get a nick | |
192 | (let ((nick (car (get-text-property (point) 'erc-data)))) | |
193 | (concat (erc-default-target) "/" nick)) | |
194 | (erc-default-target))))) | |
195 | ||
20908596 CD |
196 | (defun org-irc-get-current-erc-port () |
197 | "Return the current port as a number. | |
198 | Return the current port number or, if none is set, return the ERC | |
199 | default." | |
200 | (cond | |
201 | ((stringp erc-session-port) | |
202 | (string-to-number erc-session-port)) | |
203 | ((numberp erc-session-port) | |
204 | erc-session-port) | |
205 | (t | |
206 | erc-default-port))) | |
207 | ||
0fc0f178 | 208 | (defun org-irc-visit-erc (link) |
20908596 CD |
209 | "Visit an ERC buffer based on criteria found in LINK." |
210 | (require 'erc) | |
211 | (require 'erc-log) | |
0fc0f178 | 212 | (let* ((server (car (car link))) |
20908596 | 213 | (port (or (string-to-number (cadr (pop link))) erc-default-port)) |
0fc0f178 CD |
214 | (server-buffer) |
215 | (buffer-list | |
216 | (erc-buffer-filter | |
217 | (lambda nil | |
218 | (let ((tmp-server-buf (erc-server-buffer))) | |
219 | (and tmp-server-buf | |
220 | (with-current-buffer tmp-server-buf | |
221 | (and | |
20908596 CD |
222 | (eq (org-irc-get-current-erc-port) port) |
223 | (string= erc-session-server server) | |
0fc0f178 CD |
224 | (setq server-buffer tmp-server-buf))))))))) |
225 | (if buffer-list | |
226 | (let ((chan-name (pop link))) | |
227 | ;; if we got a channel name then switch to it or join it | |
228 | (if chan-name | |
c1fbdbbb GM |
229 | (let ((chan-buf (catch 'found |
230 | (dolist (x buffer-list) | |
231 | (if (string= (buffer-name x) chan-name) | |
232 | (throw 'found x)))))) | |
0fc0f178 CD |
233 | (if chan-buf |
234 | (progn | |
235 | (switch-to-buffer chan-buf) | |
236 | ;; if we got a nick, and they're in the chan, | |
237 | ;; then start a chat with them | |
238 | (let ((nick (pop link))) | |
239 | (when nick | |
c1fbdbbb | 240 | (if (member nick (erc-get-server-nickname-list)) |
0fc0f178 CD |
241 | (progn |
242 | (goto-char (point-max)) | |
243 | (insert (concat nick ": "))) | |
244 | (error "%s not found in %s" nick chan-name))))) | |
245 | (progn | |
246 | (switch-to-buffer server-buffer) | |
247 | (erc-cmd-JOIN chan-name)))) | |
248 | (switch-to-buffer server-buffer))) | |
249 | ;; no server match, make new connection | |
250 | (erc-select :server server :port port)))) | |
251 | ||
252 | (provide 'org-irc) | |
253 | ||
b7b8d8ed | 254 | ;; arch-tag: 018d7dda-53b8-4a35-ba92-6670939e525a |
b349f79f | 255 | |
0fc0f178 | 256 | ;;; org-irc.el ends here |