From 07963cfc7b5bd985bf01ef22c90970501104352d Mon Sep 17 00:00:00 2001 From: Alexander Sulfrian Date: Thu, 19 Nov 2009 01:44:52 +0100 Subject: added rudel (obby and other colab framework for emacs) --- emacs.d/lisp/rudel/.bzrignore | 2 + emacs.d/lisp/rudel/.svn/all-wcprops | 173 ++ emacs.d/lisp/rudel/.svn/dir-prop-base | 5 + emacs.d/lisp/rudel/.svn/entries | 1001 ++++++++ .../lisp/rudel/.svn/text-base/.bzrignore.svn-base | 2 + .../lisp/rudel/.svn/text-base/ChangeLog.svn-base | 2403 ++++++++++++++++++ emacs.d/lisp/rudel/.svn/text-base/INSTALL.svn-base | 58 + .../lisp/rudel/.svn/text-base/Project.ede.svn-base | 24 + emacs.d/lisp/rudel/.svn/text-base/README.svn-base | 88 + emacs.d/lisp/rudel/.svn/text-base/TODO.svn-base | 436 ++++ .../rudel/.svn/text-base/rudel-backend.el.svn-base | 305 +++ .../rudel/.svn/text-base/rudel-chat.el.svn-base | 103 + .../rudel/.svn/text-base/rudel-compat.el.svn-base | 158 ++ .../rudel/.svn/text-base/rudel-compile.el.svn-base | 46 + .../rudel/.svn/text-base/rudel-debug.el.svn-base | 215 ++ .../rudel/.svn/text-base/rudel-errors.el.svn-base | 66 + .../rudel/.svn/text-base/rudel-hooks.el.svn-base | 252 ++ .../rudel/.svn/text-base/rudel-icons.el.svn-base | 90 + .../.svn/text-base/rudel-interactive.el.svn-base | 181 ++ .../.svn/text-base/rudel-loaddefs.el.svn-base | 23 + .../rudel/.svn/text-base/rudel-mode.el.svn-base | 621 +++++ .../.svn/text-base/rudel-operations.el.svn-base | 133 + .../.svn/text-base/rudel-operators.el.svn-base | 172 ++ .../rudel/.svn/text-base/rudel-overlay.el.svn-base | 275 +++ .../.svn/text-base/rudel-protocol.el.svn-base | 83 + .../text-base/rudel-session-initiation.el.svn-base | 352 +++ .../.svn/text-base/rudel-speedbar.el.svn-base | 214 ++ .../.svn/text-base/rudel-state-machine.el.svn-base | 331 +++ .../rudel/.svn/text-base/rudel-tls.el.svn-base | 201 ++ .../.svn/text-base/rudel-transport.el.svn-base | 72 + .../rudel/.svn/text-base/rudel-util.el.svn-base | 261 ++ .../lisp/rudel/.svn/text-base/rudel.el.svn-base | 1012 ++++++++ emacs.d/lisp/rudel/ChangeLog | 2403 ++++++++++++++++++ emacs.d/lisp/rudel/INSTALL | 58 + emacs.d/lisp/rudel/Project.ede | 24 + emacs.d/lisp/rudel/README | 88 + emacs.d/lisp/rudel/TODO | 436 ++++ emacs.d/lisp/rudel/doc/.svn/all-wcprops | 23 + emacs.d/lisp/rudel/doc/.svn/entries | 130 + .../rudel/doc/.svn/text-base/Project.ede.svn-base | 20 + .../rudel/doc/.svn/text-base/card.pdf.svn-base | Bin 0 -> 68211 bytes .../rudel/doc/.svn/text-base/card.tex.svn-base | 329 +++ emacs.d/lisp/rudel/doc/Project.ede | 20 + emacs.d/lisp/rudel/doc/card.pdf | Bin 0 -> 68211 bytes emacs.d/lisp/rudel/doc/card.tex | 329 +++ emacs.d/lisp/rudel/icons/.svn/all-wcprops | 41 + emacs.d/lisp/rudel/icons/.svn/entries | 232 ++ .../icons/.svn/text-base/connected.svg.svn-base | 2479 +++++++++++++++++++ .../icons/.svn/text-base/disconnected.svg.svn-base | 2600 ++++++++++++++++++++ .../icons/.svn/text-base/document.svg.svn-base | 608 +++++ .../icons/.svn/text-base/encrypted.svg.svn-base | 902 +++++++ .../rudel/icons/.svn/text-base/person.svg.svn-base | 388 +++ .../icons/.svn/text-base/plaintext.svg.svn-base | 474 ++++ emacs.d/lisp/rudel/icons/connected.svg | 2479 +++++++++++++++++++ emacs.d/lisp/rudel/icons/disconnected.svg | 2600 ++++++++++++++++++++ emacs.d/lisp/rudel/icons/document.svg | 608 +++++ emacs.d/lisp/rudel/icons/encrypted.svg | 902 +++++++ emacs.d/lisp/rudel/icons/person.svg | 388 +++ emacs.d/lisp/rudel/icons/plaintext.svg | 474 ++++ emacs.d/lisp/rudel/jupiter/.svn/all-wcprops | 47 + emacs.d/lisp/rudel/jupiter/.svn/entries | 266 ++ .../jupiter/.svn/text-base/Project.ede.svn-base | 14 + .../.svn/text-base/jupiter-compound.el.svn-base | 89 + .../.svn/text-base/jupiter-delete.el.svn-base | 176 ++ .../.svn/text-base/jupiter-insert.el.svn-base | 165 ++ .../jupiter/.svn/text-base/jupiter-nop.el.svn-base | 59 + .../.svn/text-base/jupiter-operation.el.svn-base | 61 + .../jupiter/.svn/text-base/jupiter.el.svn-base | 135 + emacs.d/lisp/rudel/jupiter/Project.ede | 14 + emacs.d/lisp/rudel/jupiter/jupiter-compound.el | 89 + emacs.d/lisp/rudel/jupiter/jupiter-delete.el | 176 ++ emacs.d/lisp/rudel/jupiter/jupiter-insert.el | 165 ++ emacs.d/lisp/rudel/jupiter/jupiter-nop.el | 59 + emacs.d/lisp/rudel/jupiter/jupiter-operation.el | 61 + emacs.d/lisp/rudel/jupiter/jupiter.el | 135 + emacs.d/lisp/rudel/obby/.svn/all-wcprops | 53 + emacs.d/lisp/rudel/obby/.svn/entries | 300 +++ .../rudel/obby/.svn/text-base/Project.ede.svn-base | 14 + .../.svn/text-base/rudel-obby-client.el.svn-base | 973 ++++++++ .../.svn/text-base/rudel-obby-debug.el.svn-base | 122 + .../.svn/text-base/rudel-obby-errors.el.svn-base | 65 + .../.svn/text-base/rudel-obby-server.el.svn-base | 798 ++++++ .../.svn/text-base/rudel-obby-state.el.svn-base | 169 ++ .../.svn/text-base/rudel-obby-util.el.svn-base | 314 +++ .../obby/.svn/text-base/rudel-obby.el.svn-base | 488 ++++ emacs.d/lisp/rudel/obby/Project.ede | 14 + emacs.d/lisp/rudel/obby/rudel-obby-client.el | 973 ++++++++ emacs.d/lisp/rudel/obby/rudel-obby-debug.el | 122 + emacs.d/lisp/rudel/obby/rudel-obby-errors.el | 65 + emacs.d/lisp/rudel/obby/rudel-obby-server.el | 798 ++++++ emacs.d/lisp/rudel/obby/rudel-obby-state.el | 169 ++ emacs.d/lisp/rudel/obby/rudel-obby-util.el | 314 +++ emacs.d/lisp/rudel/obby/rudel-obby.el | 488 ++++ emacs.d/lisp/rudel/rudel-backend.el | 305 +++ emacs.d/lisp/rudel/rudel-chat.el | 103 + emacs.d/lisp/rudel/rudel-compat.el | 158 ++ emacs.d/lisp/rudel/rudel-compile.el | 46 + emacs.d/lisp/rudel/rudel-debug.el | 215 ++ emacs.d/lisp/rudel/rudel-errors.el | 66 + emacs.d/lisp/rudel/rudel-hooks.el | 252 ++ emacs.d/lisp/rudel/rudel-icons.el | 90 + emacs.d/lisp/rudel/rudel-interactive.el | 181 ++ emacs.d/lisp/rudel/rudel-loaddefs.el | 23 + emacs.d/lisp/rudel/rudel-mode.el | 621 +++++ emacs.d/lisp/rudel/rudel-operations.el | 133 + emacs.d/lisp/rudel/rudel-operators.el | 172 ++ emacs.d/lisp/rudel/rudel-overlay.el | 275 +++ emacs.d/lisp/rudel/rudel-protocol.el | 83 + emacs.d/lisp/rudel/rudel-session-initiation.el | 352 +++ emacs.d/lisp/rudel/rudel-speedbar.el | 214 ++ emacs.d/lisp/rudel/rudel-state-machine.el | 331 +++ emacs.d/lisp/rudel/rudel-tls.el | 201 ++ emacs.d/lisp/rudel/rudel-transport.el | 72 + emacs.d/lisp/rudel/rudel-util.el | 261 ++ emacs.d/lisp/rudel/rudel.el | 1012 ++++++++ emacs.d/lisp/rudel/telepathy/.svn/all-wcprops | 11 + emacs.d/lisp/rudel/telepathy/.svn/entries | 62 + .../.svn/text-base/rudel-telepathy.el.svn-base | 76 + emacs.d/lisp/rudel/telepathy/rudel-telepathy.el | 76 + emacs.d/lisp/rudel/wave/.svn/all-wcprops | 17 + emacs.d/lisp/rudel/wave/.svn/entries | 96 + .../rudel/wave/.svn/text-base/Project.ede.svn-base | 14 + .../wave/.svn/text-base/rudel-wave.el.svn-base | 77 + emacs.d/lisp/rudel/wave/Project.ede | 14 + emacs.d/lisp/rudel/wave/rudel-wave.el | 77 + emacs.d/lisp/rudel/zeroconf/.svn/all-wcprops | 17 + emacs.d/lisp/rudel/zeroconf/.svn/entries | 96 + .../zeroconf/.svn/text-base/Project.ede.svn-base | 14 + .../.svn/text-base/rudel-zeroconf.el.svn-base | 253 ++ emacs.d/lisp/rudel/zeroconf/Project.ede | 14 + emacs.d/lisp/rudel/zeroconf/rudel-zeroconf.el | 253 ++ 131 files changed, 42676 insertions(+) create mode 100644 emacs.d/lisp/rudel/.bzrignore create mode 100644 emacs.d/lisp/rudel/.svn/all-wcprops create mode 100644 emacs.d/lisp/rudel/.svn/dir-prop-base create mode 100644 emacs.d/lisp/rudel/.svn/entries create mode 100644 emacs.d/lisp/rudel/.svn/text-base/.bzrignore.svn-base create mode 100644 emacs.d/lisp/rudel/.svn/text-base/ChangeLog.svn-base create mode 100644 emacs.d/lisp/rudel/.svn/text-base/INSTALL.svn-base create mode 100644 emacs.d/lisp/rudel/.svn/text-base/Project.ede.svn-base create mode 100644 emacs.d/lisp/rudel/.svn/text-base/README.svn-base create mode 100644 emacs.d/lisp/rudel/.svn/text-base/TODO.svn-base create mode 100644 emacs.d/lisp/rudel/.svn/text-base/rudel-backend.el.svn-base create mode 100644 emacs.d/lisp/rudel/.svn/text-base/rudel-chat.el.svn-base create mode 100644 emacs.d/lisp/rudel/.svn/text-base/rudel-compat.el.svn-base create mode 100644 emacs.d/lisp/rudel/.svn/text-base/rudel-compile.el.svn-base create mode 100644 emacs.d/lisp/rudel/.svn/text-base/rudel-debug.el.svn-base create mode 100644 emacs.d/lisp/rudel/.svn/text-base/rudel-errors.el.svn-base create mode 100644 emacs.d/lisp/rudel/.svn/text-base/rudel-hooks.el.svn-base create mode 100644 emacs.d/lisp/rudel/.svn/text-base/rudel-icons.el.svn-base create mode 100644 emacs.d/lisp/rudel/.svn/text-base/rudel-interactive.el.svn-base create mode 100644 emacs.d/lisp/rudel/.svn/text-base/rudel-loaddefs.el.svn-base create mode 100644 emacs.d/lisp/rudel/.svn/text-base/rudel-mode.el.svn-base create mode 100644 emacs.d/lisp/rudel/.svn/text-base/rudel-operations.el.svn-base create mode 100644 emacs.d/lisp/rudel/.svn/text-base/rudel-operators.el.svn-base create mode 100644 emacs.d/lisp/rudel/.svn/text-base/rudel-overlay.el.svn-base create mode 100644 emacs.d/lisp/rudel/.svn/text-base/rudel-protocol.el.svn-base create mode 100644 emacs.d/lisp/rudel/.svn/text-base/rudel-session-initiation.el.svn-base create mode 100644 emacs.d/lisp/rudel/.svn/text-base/rudel-speedbar.el.svn-base create mode 100644 emacs.d/lisp/rudel/.svn/text-base/rudel-state-machine.el.svn-base create mode 100644 emacs.d/lisp/rudel/.svn/text-base/rudel-tls.el.svn-base create mode 100644 emacs.d/lisp/rudel/.svn/text-base/rudel-transport.el.svn-base create mode 100644 emacs.d/lisp/rudel/.svn/text-base/rudel-util.el.svn-base create mode 100644 emacs.d/lisp/rudel/.svn/text-base/rudel.el.svn-base create mode 100644 emacs.d/lisp/rudel/ChangeLog create mode 100644 emacs.d/lisp/rudel/INSTALL create mode 100644 emacs.d/lisp/rudel/Project.ede create mode 100644 emacs.d/lisp/rudel/README create mode 100644 emacs.d/lisp/rudel/TODO create mode 100644 emacs.d/lisp/rudel/doc/.svn/all-wcprops create mode 100644 emacs.d/lisp/rudel/doc/.svn/entries create mode 100644 emacs.d/lisp/rudel/doc/.svn/text-base/Project.ede.svn-base create mode 100644 emacs.d/lisp/rudel/doc/.svn/text-base/card.pdf.svn-base create mode 100644 emacs.d/lisp/rudel/doc/.svn/text-base/card.tex.svn-base create mode 100644 emacs.d/lisp/rudel/doc/Project.ede create mode 100644 emacs.d/lisp/rudel/doc/card.pdf create mode 100644 emacs.d/lisp/rudel/doc/card.tex create mode 100644 emacs.d/lisp/rudel/icons/.svn/all-wcprops create mode 100644 emacs.d/lisp/rudel/icons/.svn/entries create mode 100644 emacs.d/lisp/rudel/icons/.svn/text-base/connected.svg.svn-base create mode 100644 emacs.d/lisp/rudel/icons/.svn/text-base/disconnected.svg.svn-base create mode 100644 emacs.d/lisp/rudel/icons/.svn/text-base/document.svg.svn-base create mode 100644 emacs.d/lisp/rudel/icons/.svn/text-base/encrypted.svg.svn-base create mode 100644 emacs.d/lisp/rudel/icons/.svn/text-base/person.svg.svn-base create mode 100644 emacs.d/lisp/rudel/icons/.svn/text-base/plaintext.svg.svn-base create mode 100644 emacs.d/lisp/rudel/icons/connected.svg create mode 100644 emacs.d/lisp/rudel/icons/disconnected.svg create mode 100644 emacs.d/lisp/rudel/icons/document.svg create mode 100644 emacs.d/lisp/rudel/icons/encrypted.svg create mode 100644 emacs.d/lisp/rudel/icons/person.svg create mode 100644 emacs.d/lisp/rudel/icons/plaintext.svg create mode 100644 emacs.d/lisp/rudel/jupiter/.svn/all-wcprops create mode 100644 emacs.d/lisp/rudel/jupiter/.svn/entries create mode 100644 emacs.d/lisp/rudel/jupiter/.svn/text-base/Project.ede.svn-base create mode 100644 emacs.d/lisp/rudel/jupiter/.svn/text-base/jupiter-compound.el.svn-base create mode 100644 emacs.d/lisp/rudel/jupiter/.svn/text-base/jupiter-delete.el.svn-base create mode 100644 emacs.d/lisp/rudel/jupiter/.svn/text-base/jupiter-insert.el.svn-base create mode 100644 emacs.d/lisp/rudel/jupiter/.svn/text-base/jupiter-nop.el.svn-base create mode 100644 emacs.d/lisp/rudel/jupiter/.svn/text-base/jupiter-operation.el.svn-base create mode 100644 emacs.d/lisp/rudel/jupiter/.svn/text-base/jupiter.el.svn-base create mode 100644 emacs.d/lisp/rudel/jupiter/Project.ede create mode 100644 emacs.d/lisp/rudel/jupiter/jupiter-compound.el create mode 100644 emacs.d/lisp/rudel/jupiter/jupiter-delete.el create mode 100644 emacs.d/lisp/rudel/jupiter/jupiter-insert.el create mode 100644 emacs.d/lisp/rudel/jupiter/jupiter-nop.el create mode 100644 emacs.d/lisp/rudel/jupiter/jupiter-operation.el create mode 100644 emacs.d/lisp/rudel/jupiter/jupiter.el create mode 100644 emacs.d/lisp/rudel/obby/.svn/all-wcprops create mode 100644 emacs.d/lisp/rudel/obby/.svn/entries create mode 100644 emacs.d/lisp/rudel/obby/.svn/text-base/Project.ede.svn-base create mode 100644 emacs.d/lisp/rudel/obby/.svn/text-base/rudel-obby-client.el.svn-base create mode 100644 emacs.d/lisp/rudel/obby/.svn/text-base/rudel-obby-debug.el.svn-base create mode 100644 emacs.d/lisp/rudel/obby/.svn/text-base/rudel-obby-errors.el.svn-base create mode 100644 emacs.d/lisp/rudel/obby/.svn/text-base/rudel-obby-server.el.svn-base create mode 100644 emacs.d/lisp/rudel/obby/.svn/text-base/rudel-obby-state.el.svn-base create mode 100644 emacs.d/lisp/rudel/obby/.svn/text-base/rudel-obby-util.el.svn-base create mode 100644 emacs.d/lisp/rudel/obby/.svn/text-base/rudel-obby.el.svn-base create mode 100644 emacs.d/lisp/rudel/obby/Project.ede create mode 100644 emacs.d/lisp/rudel/obby/rudel-obby-client.el create mode 100644 emacs.d/lisp/rudel/obby/rudel-obby-debug.el create mode 100644 emacs.d/lisp/rudel/obby/rudel-obby-errors.el create mode 100644 emacs.d/lisp/rudel/obby/rudel-obby-server.el create mode 100644 emacs.d/lisp/rudel/obby/rudel-obby-state.el create mode 100644 emacs.d/lisp/rudel/obby/rudel-obby-util.el create mode 100644 emacs.d/lisp/rudel/obby/rudel-obby.el create mode 100644 emacs.d/lisp/rudel/rudel-backend.el create mode 100644 emacs.d/lisp/rudel/rudel-chat.el create mode 100644 emacs.d/lisp/rudel/rudel-compat.el create mode 100644 emacs.d/lisp/rudel/rudel-compile.el create mode 100644 emacs.d/lisp/rudel/rudel-debug.el create mode 100644 emacs.d/lisp/rudel/rudel-errors.el create mode 100644 emacs.d/lisp/rudel/rudel-hooks.el create mode 100644 emacs.d/lisp/rudel/rudel-icons.el create mode 100644 emacs.d/lisp/rudel/rudel-interactive.el create mode 100644 emacs.d/lisp/rudel/rudel-loaddefs.el create mode 100644 emacs.d/lisp/rudel/rudel-mode.el create mode 100644 emacs.d/lisp/rudel/rudel-operations.el create mode 100644 emacs.d/lisp/rudel/rudel-operators.el create mode 100644 emacs.d/lisp/rudel/rudel-overlay.el create mode 100644 emacs.d/lisp/rudel/rudel-protocol.el create mode 100644 emacs.d/lisp/rudel/rudel-session-initiation.el create mode 100644 emacs.d/lisp/rudel/rudel-speedbar.el create mode 100644 emacs.d/lisp/rudel/rudel-state-machine.el create mode 100644 emacs.d/lisp/rudel/rudel-tls.el create mode 100644 emacs.d/lisp/rudel/rudel-transport.el create mode 100644 emacs.d/lisp/rudel/rudel-util.el create mode 100644 emacs.d/lisp/rudel/rudel.el create mode 100644 emacs.d/lisp/rudel/telepathy/.svn/all-wcprops create mode 100644 emacs.d/lisp/rudel/telepathy/.svn/entries create mode 100644 emacs.d/lisp/rudel/telepathy/.svn/text-base/rudel-telepathy.el.svn-base create mode 100644 emacs.d/lisp/rudel/telepathy/rudel-telepathy.el create mode 100644 emacs.d/lisp/rudel/wave/.svn/all-wcprops create mode 100644 emacs.d/lisp/rudel/wave/.svn/entries create mode 100644 emacs.d/lisp/rudel/wave/.svn/text-base/Project.ede.svn-base create mode 100644 emacs.d/lisp/rudel/wave/.svn/text-base/rudel-wave.el.svn-base create mode 100644 emacs.d/lisp/rudel/wave/Project.ede create mode 100644 emacs.d/lisp/rudel/wave/rudel-wave.el create mode 100644 emacs.d/lisp/rudel/zeroconf/.svn/all-wcprops create mode 100644 emacs.d/lisp/rudel/zeroconf/.svn/entries create mode 100644 emacs.d/lisp/rudel/zeroconf/.svn/text-base/Project.ede.svn-base create mode 100644 emacs.d/lisp/rudel/zeroconf/.svn/text-base/rudel-zeroconf.el.svn-base create mode 100644 emacs.d/lisp/rudel/zeroconf/Project.ede create mode 100644 emacs.d/lisp/rudel/zeroconf/rudel-zeroconf.el (limited to 'emacs.d') diff --git a/emacs.d/lisp/rudel/.bzrignore b/emacs.d/lisp/rudel/.bzrignore new file mode 100644 index 0000000..5f6312e --- /dev/null +++ b/emacs.d/lisp/rudel/.bzrignore @@ -0,0 +1,2 @@ +*.elc +Makefile diff --git a/emacs.d/lisp/rudel/.svn/all-wcprops b/emacs.d/lisp/rudel/.svn/all-wcprops new file mode 100644 index 0000000..5ad96b9 --- /dev/null +++ b/emacs.d/lisp/rudel/.svn/all-wcprops @@ -0,0 +1,173 @@ +K 25 +svn:wc:ra_dav:version-url +V 33 +/svnroot/rudel/!svn/ver/480/trunk +END +.bzrignore +K 25 +svn:wc:ra_dav:version-url +V 44 +/svnroot/rudel/!svn/ver/353/trunk/.bzrignore +END +rudel-icons.el +K 25 +svn:wc:ra_dav:version-url +V 48 +/svnroot/rudel/!svn/ver/271/trunk/rudel-icons.el +END +rudel-errors.el +K 25 +svn:wc:ra_dav:version-url +V 49 +/svnroot/rudel/!svn/ver/261/trunk/rudel-errors.el +END +rudel-interactive.el +K 25 +svn:wc:ra_dav:version-url +V 54 +/svnroot/rudel/!svn/ver/322/trunk/rudel-interactive.el +END +rudel-util.el +K 25 +svn:wc:ra_dav:version-url +V 47 +/svnroot/rudel/!svn/ver/306/trunk/rudel-util.el +END +rudel-operators.el +K 25 +svn:wc:ra_dav:version-url +V 52 +/svnroot/rudel/!svn/ver/181/trunk/rudel-operators.el +END +rudel-chat.el +K 25 +svn:wc:ra_dav:version-url +V 47 +/svnroot/rudel/!svn/ver/333/trunk/rudel-chat.el +END +ChangeLog +K 25 +svn:wc:ra_dav:version-url +V 43 +/svnroot/rudel/!svn/ver/478/trunk/ChangeLog +END +rudel-overlay.el +K 25 +svn:wc:ra_dav:version-url +V 50 +/svnroot/rudel/!svn/ver/465/trunk/rudel-overlay.el +END +rudel-loaddefs.el +K 25 +svn:wc:ra_dav:version-url +V 51 +/svnroot/rudel/!svn/ver/464/trunk/rudel-loaddefs.el +END +rudel-state-machine.el +K 25 +svn:wc:ra_dav:version-url +V 56 +/svnroot/rudel/!svn/ver/397/trunk/rudel-state-machine.el +END +rudel-hooks.el +K 25 +svn:wc:ra_dav:version-url +V 48 +/svnroot/rudel/!svn/ver/307/trunk/rudel-hooks.el +END +rudel-operations.el +K 25 +svn:wc:ra_dav:version-url +V 53 +/svnroot/rudel/!svn/ver/181/trunk/rudel-operations.el +END +rudel-compat.el +K 25 +svn:wc:ra_dav:version-url +V 49 +/svnroot/rudel/!svn/ver/468/trunk/rudel-compat.el +END +rudel-mode.el +K 25 +svn:wc:ra_dav:version-url +V 47 +/svnroot/rudel/!svn/ver/469/trunk/rudel-mode.el +END +README +K 25 +svn:wc:ra_dav:version-url +V 40 +/svnroot/rudel/!svn/ver/397/trunk/README +END +rudel-speedbar.el +K 25 +svn:wc:ra_dav:version-url +V 51 +/svnroot/rudel/!svn/ver/235/trunk/rudel-speedbar.el +END +rudel-debug.el +K 25 +svn:wc:ra_dav:version-url +V 48 +/svnroot/rudel/!svn/ver/331/trunk/rudel-debug.el +END +rudel-backend.el +K 25 +svn:wc:ra_dav:version-url +V 50 +/svnroot/rudel/!svn/ver/330/trunk/rudel-backend.el +END +rudel-compile.el +K 25 +svn:wc:ra_dav:version-url +V 50 +/svnroot/rudel/!svn/ver/347/trunk/rudel-compile.el +END +rudel-session-initiation.el +K 25 +svn:wc:ra_dav:version-url +V 61 +/svnroot/rudel/!svn/ver/354/trunk/rudel-session-initiation.el +END +Project.ede +K 25 +svn:wc:ra_dav:version-url +V 45 +/svnroot/rudel/!svn/ver/471/trunk/Project.ede +END +rudel-transport.el +K 25 +svn:wc:ra_dav:version-url +V 52 +/svnroot/rudel/!svn/ver/316/trunk/rudel-transport.el +END +TODO +K 25 +svn:wc:ra_dav:version-url +V 38 +/svnroot/rudel/!svn/ver/480/trunk/TODO +END +INSTALL +K 25 +svn:wc:ra_dav:version-url +V 41 +/svnroot/rudel/!svn/ver/397/trunk/INSTALL +END +rudel-protocol.el +K 25 +svn:wc:ra_dav:version-url +V 51 +/svnroot/rudel/!svn/ver/245/trunk/rudel-protocol.el +END +rudel-tls.el +K 25 +svn:wc:ra_dav:version-url +V 46 +/svnroot/rudel/!svn/ver/326/trunk/rudel-tls.el +END +rudel.el +K 25 +svn:wc:ra_dav:version-url +V 42 +/svnroot/rudel/!svn/ver/471/trunk/rudel.el +END diff --git a/emacs.d/lisp/rudel/.svn/dir-prop-base b/emacs.d/lisp/rudel/.svn/dir-prop-base new file mode 100644 index 0000000..8f9583a --- /dev/null +++ b/emacs.d/lisp/rudel/.svn/dir-prop-base @@ -0,0 +1,5 @@ +K 13 +bzr:pointless +V 1 +2 +END diff --git a/emacs.d/lisp/rudel/.svn/entries b/emacs.d/lisp/rudel/.svn/entries new file mode 100644 index 0000000..df63eca --- /dev/null +++ b/emacs.d/lisp/rudel/.svn/entries @@ -0,0 +1,1001 @@ +10 + +dir +545 +https://rudel.svn.sourceforge.net/svnroot/rudel/trunk +https://rudel.svn.sourceforge.net/svnroot/rudel + + + +2009-11-01T06:23:01.081193Z +480 +scymtym +has-props + + + + + + + + + + + + + +694b31df-dcbb-44e8-af88-74c7ea918228 + +zeroconf +dir + +rudel-util.el +file + + + + +2009-11-18T14:01:46.000000Z +0cca36ebc59e3a59eb8209c4ad358c07 +2009-10-03T01:30:23.499430Z +306 +scymtym + + + + + + + + + + + + + + + + + + + + + +7858 + +rudel-interactive.el +file + + + + +2009-11-18T14:01:46.000000Z +b407a3920ab5c077cf0746220e0de31d +2009-10-03T01:57:16.152033Z +322 +scymtym + + + + + + + + + + + + + + + + + + + + + +6149 + +rudel-overlay.el +file + + + + +2009-11-18T14:01:46.000000Z +a3ba9b413bfefcea2631dd27464acfb1 +2009-10-16T01:47:25.421704Z +465 +scymtym + + + + + + + + + + + + + + + + + + + + + +9178 + +obby +dir + +rudel-operations.el +file + + + + +2009-11-18T14:01:46.000000Z +a3b2732504aee4fbddf77e8f5cb5d3f5 +2009-10-03T00:38:22.402651Z +181 +scymtym + + + + + + + + + + + + + + + + + + + + + +3546 + +rudel-hooks.el +file + + + + +2009-11-18T14:01:46.000000Z +fd6ed67d83a6678cac9fa25e414de1d8 +2009-10-03T01:30:48.800693Z +307 +scymtym + + + + + + + + + + + + + + + + + + + + + +7540 + +rudel-debug.el +file + + + + +2009-11-18T14:01:46.000000Z +8f020be45bb3787c27888ec5eda99c1f +2009-10-03T01:59:11.181331Z +331 +scymtym + + + + + + + + + + + + + + + + + + + + + +5778 + +rudel-compile.el +file + + + + +2009-11-18T14:01:46.000000Z +e9fdc6a7e7a557cef318ff5b4406b8c1 +2009-10-03T02:04:48.037280Z +347 +scymtym + + + + + + + + + + + + + + + + + + + + + +1286 + +wave +dir + +Project.ede +file + + + + +2009-11-18T14:01:46.000000Z +c2d7e6f2f3310e6275555ae30c68556b +2009-10-16T01:48:38.325631Z +471 +scymtym + + + + + + + + + + + + + + + + + + + + + +1007 + +doc +dir + +INSTALL +file + + + + +2009-11-18T14:01:46.000000Z +6d6fa2de5c8eab6c413fefc4a584de6e +2009-10-10T00:39:13.745463Z +397 +scymtym + + + + + + + + + + + + + + + + + + + + + +1977 + +rudel-protocol.el +file + + + + +2009-11-18T14:01:46.000000Z +30def601287f12146ec67c4f22dfedb2 +2009-10-03T00:56:18.517642Z +245 +scymtym + + + + + + + + + + + + + + + + + + + + + +2474 + +rudel-tls.el +file + + + + +2009-11-18T14:01:46.000000Z +aaacd0f1a0ee9432b08c0a6b7011be6d +2009-10-03T01:57:56.380090Z +326 +scymtym + + + + + + + + + + + + + + + + + + + + + +6653 + +.bzrignore +file + + + + +2009-11-18T14:01:46.000000Z +8869a5d05314da0eef07cc732c683b4c +2009-10-03T02:08:21.840187Z +353 +scymtym + + + + + + + + + + + + + + + + + + + + + +15 + +jupiter +dir + +rudel-icons.el +file + + + + +2009-11-18T14:01:46.000000Z +69c1f83b9fb9daeed59f82029c6bb2f5 +2009-10-03T01:13:36.139341Z +271 +scymtym + + + + + + + + + + + + + + + + + + + + + +2216 + +rudel-errors.el +file + + + + +2009-11-18T14:01:46.000000Z +b08feb690df0dad3641e67ab887edec0 +2009-10-03T01:07:06.053827Z +261 +scymtym + + + + + + + + + + + + + + + + + + + + + +1555 + +rudel-operators.el +file + + + + +2009-11-18T14:01:46.000000Z +d14817f262478370aa6407859a793f38 +2009-10-03T00:38:22.402651Z +181 +scymtym + + + + + + + + + + + + + + + + + + + + + +5620 + +ChangeLog +file + + + + +2009-11-18T14:01:46.000000Z +f373fa66be88d4b25681887e2d21c4f5 +2009-11-01T06:22:31.276953Z +478 +scymtym + + + + + + + + + + + + + + + + + + + + + +99450 + +rudel-chat.el +file + + + + +2009-11-18T14:01:46.000000Z +b95fa6378d432f42de7f422be624e3f8 +2009-10-03T01:59:22.171161Z +333 +scymtym + + + + + + + + + + + + + + + + + + + + + +2726 + +rudel-loaddefs.el +file + + + + +2009-11-18T14:01:46.000000Z +7f4b7008a58c307af7dc94f7a57ce509 +2009-10-16T01:47:11.584789Z +464 +scymtym + + + + + + + + + + + + + + + + + + + + + +836 + +rudel-state-machine.el +file + + + + +2009-11-18T14:01:46.000000Z +29237a64e50d3fe8585483cd3639c75d +2009-10-10T00:39:13.745463Z +397 +scymtym + + + + + + + + + + + + + + + + + + + + + +10305 + +rudel-compat.el +file + + + + +2009-11-18T14:01:46.000000Z +63bc6b3180525b022f9ddb9ea13ec7dc +2009-10-16T01:48:03.160288Z +468 +scymtym + + + + + + + + + + + + + + + + + + + + + +6110 + +rudel-mode.el +file + + + + +2009-11-18T14:01:46.000000Z +dc7a115e60aeddc080cb2e680f2824ca +2009-10-16T01:48:14.377417Z +469 +scymtym + + + + + + + + + + + + + + + + + + + + + +20825 + +rudel-speedbar.el +file + + + + +2009-11-18T14:01:46.000000Z +c899ca06cea2a20f3e760ae3a2f622b3 +2009-10-03T00:51:24.682586Z +235 +scymtym + + + + + + + + + + + + + + + + + + + + + +7471 + +README +file + + + + +2009-11-18T14:01:46.000000Z +d2ec3a641007417c8ee8bca69ab5f858 +2009-10-10T00:39:13.745463Z +397 +scymtym + + + + + + + + + + + + + + + + + + + + + +3235 + +rudel-backend.el +file + + + + +2009-11-18T14:01:46.000000Z +2ca1c3acc2d0ae469d1b014be4b0674e +2009-10-03T01:58:59.186010Z +330 +scymtym + + + + + + + + + + + + + + + + + + + + + +9610 + +telepathy +dir + +rudel-session-initiation.el +file + + + + +2009-11-18T14:01:46.000000Z +681ca33c2c1c328b551a9027ac4aab69 +2009-10-03T02:08:38.551425Z +354 +scymtym + + + + + + + + + + + + + + + + + + + + + +10953 + +rudel-transport.el +file + + + + +2009-11-18T14:01:46.000000Z +e22f3b1d0e1e1d73ddf96e5e4c47b5a1 +2009-10-03T01:55:52.956859Z +316 +scymtym + + + + + + + + + + + + + + + + + + + + + +1894 + +TODO +file + + + + +2009-11-18T14:01:46.000000Z +6579915878eaff18f620e6cd0174c9df +2009-11-01T06:23:01.081193Z +480 +scymtym + + + + + + + + + + + + + + + + + + + + + +12907 + +rudel.el +file + + + + +2009-11-18T14:01:46.000000Z +e043c1ef7127a53a4a1292dafde28e4e +2009-10-16T01:48:38.325631Z +471 +scymtym + + + + + + + + + + + + + + + + + + + + + +30406 + +icons +dir + diff --git a/emacs.d/lisp/rudel/.svn/text-base/.bzrignore.svn-base b/emacs.d/lisp/rudel/.svn/text-base/.bzrignore.svn-base new file mode 100644 index 0000000..5f6312e --- /dev/null +++ b/emacs.d/lisp/rudel/.svn/text-base/.bzrignore.svn-base @@ -0,0 +1,2 @@ +*.elc +Makefile diff --git a/emacs.d/lisp/rudel/.svn/text-base/ChangeLog.svn-base b/emacs.d/lisp/rudel/.svn/text-base/ChangeLog.svn-base new file mode 100644 index 0000000..03c4fab --- /dev/null +++ b/emacs.d/lisp/rudel/.svn/text-base/ChangeLog.svn-base @@ -0,0 +1,2403 @@ +2009-10-22 Jan Moringen + + * TODO (Future): added BEEP and SubEthaEdit tasks + +2009-10-15 Jan Moringen + + * rudel.el (rudel-version): bumped to 0.3 + * Project.ede (project rudel): bumped version to 0.3 + + * TODO: whitespace fixes + (Milestone rudel-0.4): new; focus on telepathy transport + (Milestone rudel-0.3): added infinote, document trees + +2009-10-13 Jan Moringen + + * TODO: new file; TODO items for future development and releases + +2009-10-12 Phil Hagelberg + + * rudel-mode.el + (rudel-mode-line-publish-state--add-indicator-to-mode-line): + Replace reference to mode-line indicator that was not present in + Emacs 22. + + * rudel-compat.el: add rudel-get-coding-system wrapper function. + * rudel-obby-util.el (with-parsed-args): Replace call to + Emacs23-specific coding-system-from-name function with + rudel-get-coding-system. + + rudel-compat.el: Add string-match-p if not present. (< Emacs 23) + + * rudel-mode.el, rudel-overlay.el: Move use of :safe flag from + defcustom to a separate put. Required for Emacs 22 compatibility. + + * rudel-loaddefs.el: Only require rudel-zeroconf if zeroconf is + available. + +2009-10-12 Jan Moringen + + * jupiter/jupiter-delete.el (jupiter-delete::jupiter-transform): + fixed error message + +2009-10-08 Jan Moringen + + * jupiter/jupiter.el (header): downcased keywords + (object-print): new method; add revisions and log length to string + * jupiter/jupiter-nop.el (header): downcased keywords; cosmetic + representation changes to "commentary" section + * jupiter/jupiter-insert.el (header): downcased keywords; cosmetic + changes to "commentary" section + (object-print): new method; add start, end, length and data to + string representation + * jupiter/jupiter-delete.el (header): downcased keywords; cosmetic + changes to "commentary" section + (jupiter-transform): cosmetic changes + (object-print): new method; add position and length to string + representation + * jupiter/jupiter-compound.el (header): downcased keywords; + cosmetic changes to "commentary" section + (object-print): new method; add number of children to string + representation + +2009-10-07 Jan Moringen + + * doc/card.tex (Session Initiation): mention configured sessions; + explain Zeroconf-advertised session in more detail + +2009-10-06 Jan Moringen + + * doc/card.tex (Joining a Session ...): added global and user + passwords + + * rudel.el (rudel-version): use list representation instead of + float + (rudel-allocate-buffer-function): added documentation string + + * INSTALL (COMPILING): fixed typo + +2009-10-06 Phil Hagelberg + + * README (JOINING): Mention `rudel-configured-sessions' + customization. + + * README (INTRODUCTION): emphasize obby backend being the only + supported one so far + (JOINING): update example session with passwords. + + * rudel-loaddefs.el: Add autoloads for rudel-host-session and + rudel-speedbar + +2009-10-05 Phil Hagelberg + + * README: Mention Eshell issue and license. + +2009-10-03 Jan Moringen + + * rudel-compat.el (make-pulsing-progress-reporter): removed; + `make-progress-reporter' is sufficient + (progress-reporter-pulse): store index in the car of the reporter; + set last update time of the reporter + * obby/rudel-obby.el (rudel-connect): use `make-progress-reporter' + instead of `make-pulsing-progress-reporter' + * obby/rudel-obby-client.el + (rudel-obby-connection::rudel-subscribe-to): cosmetic changes in + status messages + + * ChangeLog: updated + +2009-10-02 Phil Hagelberg + + * INSTALL: Mention CEDET's inclusion in Emacs + + * rudel-state-machine.el (rudel-state-wait): update progress + reporter usage to match rudel-compat.el + + * rudel-compat.el: Use updated progress reporters + (progress-reporter-update): may be used by pulsing reporters too + (make-progress-reporter): nil max value returns pulsing reporter + (progress-reporter-force-update): may be used by pulsing reporters + too + (progress-reporter-pulsing-p): added + (progress-reporter-pulse): removed message change option + +2009-10-02 Jan Moringen + + * rudel-state-machine.el (rudel-state-wait): accept callback + function instead of message; adjusted documentation string + * obby/rudel-obby.el (rudel-obby-backend::rudel-connect): when + calling `rudel-state-wait', provide a callback; the callback + controls a progress reporter + * obby/rudel-obby-client.el + (rudel-obby-connection::rudel-subscribe-to): when calling + `rudel-state-wait', provide a callback; the callback controls a + progress reporter + + * rudel-compat.el (progress-reporter-pulse): store index as float + + * obby/rudel-obby-client.el + (rudel-obby-client-state-idle::rudel-obby/obby_document_create): + cosmetic changes of printed messages + (rudel-obby-client-state-idle::rudel-obby/obby_document_remove): + cosmetic changes of printed messages + + * rudel.el (rudel-session::rudel-remove-document): when necessary, + detach document first; added documentation string + (rudel-document::rudel-attached-p): new method; return non-nil + when document is attached to a buffer + + * obby/rudel-obby-client.el + (rudel-obby-client-state-idle::rudel-obby/net6_client_part): + promoted warning severity to :warning + (rudel-obby-client-state-idle::rudel-obby/obby_document_remove): + fixed lookup of document; added warning message in case it is not + found + (rudel-obby-client-state-idle::rudel-obby/obby_document/rename): + promoted warning severity to :warning + (rudel-obby-connection::rudel-unpublish): new method; ask server + to remove a document + +2009-10-01 Jan Moringen + + * rudel-state-machine.el (rudel-state-wait): fixed progress range + [0, 100] -> [0, 1]; fixed reference to reporter object + * obby/rudel-obby.el (rudel-obby-send): removed remnants of calls + to `working-*' functions; call `progress-reporter-pulse' just + before `progress-reporter-done' + +2009-09-30 Jan Moringen + + * rudel-compat.el: only define pulsing progress reporter when + Emacs does not have one itself + (header): downcased keywords + (make-pulsing-progress-reporter): renamed + `make-progress-reporter-pulse' -> + `make-pulsing-progress-reporter'; cosmetic changes; explanatory + comment + (progress-reporter-pulse): added documentation string + Suggested by Phil Hagelberg + + * rudel-compat.el (progress-pulse-values): new variable; indicator + strings used by pulsing progress reporter + (make-progress-reporter-pulse): new function; creates pulsing + progress reporter + (progress-reporter-pulse): new function; updates pulsing progress + reporter + Suggested by Phil Hagelberg + +2009-09-29 Jan Moringen + + * obby/rudel-obby-server.el + (rudel-obby-server-state-before-join::rudel-obby/net6_client_login): + accept two additional arguments: global-password and user-password + + * rudel-session-initiation.el (rudel-configured-sessions): + improved documentation string + +2009-09-29 Jan Moringen + + * INSTALL (REQUIREMENTS): wording + (INSTALLING): mention other things that cause rudel to be + autoloaded; minor cosmetic changes + (COMPILING): fixed filename compile.el -> rudel-compile.el + + * rudel-compile.el (header): added copyright and meta data + (code): let-bind rudel-dir; call `byte-recompile-directory' just + once since it operates recursively + +2009-09-28 Jan Moringen + + * obby/rudel-obby-server.el (header): better documentation + (require rudel-state-machine): now required for state machine + style handling of client connections + (require rudel-obby-state): now required; provides base class for + states + (rudel-obby-server-state-new): new class; client connection state + new + (rudel-obby-server-state-encryption-negotiate): new class; client connection state + for negotiating encryption + (rudel-obby-server-state-before-join): new class; client connection state + for waiting for login request + (rudel-obby-server-state-new): new class; client connection state + entered after session setup and joining is complete + (rudel-obby-server-connection-states): new variable; list of + states and their symbolic names + (rudel-obby-client): now derived from rudel-state-machine + + * obby/rudel-obby.el (rudel-obby-backend::rudel-ask-connect-info): + check for :global-password and :user-password correctly; do not + put them into the list when they are "" + (rudel-obby-backend::rudel-connect): comment + + * rudel-session-initiation.el + (rudel-configured-sessions-backend::rudel-discover): let + `rudel-session-initiation-adjust-info' do the heavy lifting + (rudel-session-initiation-adjust-info): new function; adjust + arguments that need adjusting in a session information property + list + + * rudel-session-initiation.el (rudel-configured-sessions): more + precise specification of the customization type + +2009-09-27 Phil Hagelberg + + * compile.el: renamed compile.el -> rudel-compile.el + + * INSTALL: Mention new install process using compile.el and + rudel-loaddefs.el. + + * rudel-loaddefs.el: Autoload rudel as one unit instead of + piece-by-piece. Remove eieio dependency from autoloads. + + * compile.el: Perform compilation from Elisp + + * Makefile: Remove error-prone CEDET-autogenerated build scripts. + +2009-09-27 Jan Moringen + + * rudel-session-initiation.el + (rudel-configured-sessions-backend::rudel-discover): fixed a bug + that dropped the last option in each configured session + + * rudel-session-initiation.el (rudel-configured-sessions): new + customization option; contains a list of session information lists + (rudel-ask-protocol-backend::initialize-instance): maybe call next + method + (rudel-configured-sessions-version): new constant; version of the + configured-sessions backend + (rudel-configured-sessions-backend): new class; + configured-sessions backend + (rudel-configured-sessions-backend::initialize-instance): new + method; set version slot + (rudel-configured-sessions-backend::rudel-discover): new method; + "discover" configured sessions + (autoload rudel-add-backend): register + rudel-configured-sessions-backend as a protocol backend + + * rudel-chat.el (rudel-chat-buffer-name): new constant; name chat + log buffer + (rudel-chat-handle-buffer): raise buffer when logging a chat + message + + * rudel-debug.el (header): fixed meta-data and license + (rudel-debug-sent-data-face): added documentation string + (rudel-debug-received-data-face): added documentation string + (rudel-debug-received-processed-data-face): added documentation + string + (rudel-debug-state-face): added documentation string + (rudel-debug-special-face): added documentation string + (rudel-suspend-session-socket): added documentation string + (rudel-resume-session-socket): added documentation string + + * obby/rudel-obby-util.el (rudel-obby-dispatch): use + `display-warning' instead of `warn' + * obby/rudel-obby-state.el (rudel-obby-state::rudel-accept): use + `display-warning' instead of `warn' + (rudel-obby-document-handler::rudel-obby/obby_document): use + `display-warning' instead of `warn' + * obby/rudel-obby-client.el + (rudel-obby-client-state-idle::rudel-obby/net6_client_part): use + `display-warning' instead of `warn' + (rudel-obby-client-state-idle::rudel-obby/obby_document/record): + use `display-warning' instead of `warn' + * rudel-backend.el (rudel-backend-factory::rudel-load-backends): + use `display-warning' instead of `warn' + + * obby/rudel-obby-client.el (require rudel-chat): used when + handling chat messages + (rudel-obby-client-state-idle::rudel-obby/obby_message): new + method; handles obby 'message' messages by dispatching to + `rudel-chat-dispatch-message' + * rudel-chat.el (whole file): new file; handling of incoming chat + messages + * Project.ede (target rudel): added rudel-chat.el + * Makefile (target rudel_LISP): added rudel-chat.el + +2009-09-26 Jan Moringen + + * obby/rudel-obby-state.el (rudel-obby-server-connection-state): + new class; base class for server connection states + (rudel-obby-server-connection-state::rudel-broadcast): new method; + broadcast message to a set of receivers + +2009-09-25 Jan Moringen + + * obby/rudel-obby.el (rudel-obby-backend::rudel-connect): new + error state they-finalized; handle join-failed error specially + * obby/rudel-obby-client.el (rudel-obby-client-state-join-failed): + improved comments + (rudel-obby-client-state-they-finalized): new state class; used to + indicate that the connection was closed by the peer + (rudel-obby-client-connection-states): added they-finalized + (rudel-obby-connection::rudel-close): switch state machine to + they-finalized + (rudel-obby-connection::rudel-subscribe-to): new error state + they-finalized + + * rudel.el (rudel-client-session::connection): allow nil value + (rudel-client-session::rudel-end): only try to disconnect the + connection if it is non-nil; ignore errors during disconnect + + * obby/rudel-obby-client.el + (rudel-obby-client-state-encryption-start::rudel-obby/net6_encryption_begin): + do not require rudel-tls; do not try to start TLS encryption if + the connection does not support it + * rudel-tls.el (rudel-tls-make-process): mark process as + supporting TLS encryption + + * obby/rudel-obby-state.el (rudel-obby/net6_ping): return nil to + prevent erratic behavior of the state machine + + * rudel-interactive.el (rudel-allocate-buffer-clear-existing): + added missing whitespace in prompt string + +2009-09-24 Jan Moringen + + * rudel-tls.el (rudel-tls-start-tls): changed displayed message + (rudel-tls-wait-init): ignore all lines until "- Simple Client + Mode.*" is received; then switch back old filter + (rudel-tls-wait-handshake): ignore all lines until "- + Compression.*" is received; then switch to established filter + (rudel-tls-established): do not ignore any lines other than + "- Peer has closed the GNUTLS connection" + + * obby/rudel-obby.el (rudel-ask-connect-info): ask for global and + user passwords + * obby/rudel-obby-client.el + (rudel-obby-client-state-joining::rudel-enter): transmit global + and user passwords when available + + * obby/rudel-obby-errors.el + (rudel-obby-error-wrong-global-password): fixed error code + (rudel-obby-error-wrong-user-password): fixed error code + (rudel-obby-error-protocol-version-mismatch): fixed error code + (rudel-obby-error-not-encrypted): fixed error code + * obby/rudel-obby-client.el + (rudel-obby-client-state-joining::rudel-obby/net6_login_failed): + recognize wrong global/user password error codes + + * obby/rudel-obby-debug.el (whole file): new file; debugging + functions for the obby backend + * rudel-debug.el (whole file): new file; debugging functions + + * obby/rudel-obby-errors.el + (rudel-obby-error-wrong-global-password): new constant; error code + for wrong global password + (rudel-obby-error-wrong-user-password): new constant; error code + for wrong user password + (rudel-obby-error-protocol-version-mismatch): new constant; error + code for protocol version mismatch + (rudel-obby-error-not-encrypted): new constant; error code for not + encrypted + + * Project.ede (target rudel): added rudel-transport.el + * Makefile (target rudel_LISP): added rudel-transport.el + * rudel-transport.el (whole file): new file; interface for + transport backends + +2009-09-20 Jan Moringen + + * obby/rudel-obby-server.el + (rudel-obby-client::rudel-remote-operation): added byte -> char + conversion before the operation is applied to the server-side + document; updated comments + + * obby/rudel-obby-server.el (rudel-obby-client::rudel-broadcast): + cosmetic changes + (rudel-obby-client::rudel-obby/net6_client_login): cosmetic + changes; improved comments + (rudel-obby-client::rudel-obby/obby_document): changed + documentation string; cosmetic changes + (rudel-obby-client::rudel-obby/obby_document/record): added a + comment + (rudel-obby-client::rudel-remote-operation): improved comments + + * obby/rudel-obby-server.el (header): added header comment + (rudel-obby-client::rudel-obby/obby_document_create): changed + documentation comment + (rudel-obby-client::rudel-obby/obby_document/subscribe): changed + documentation comment; cosmetic changes + (rudel-obby-client::rudel-obby/obby_document/unsubscribe): + cosmetic changes + (rudel-obby-server::initialize-instance): do not run :after; call + next method + (rudel-obby-server::rudel-broadcast): signal wrong-type-argument + instead of just error; cosmetic changes + (rudel-obby-server::rudel-check-username-and-color): changed + comments + (rudel-obby-server::object-print): new method; generate string + representation with number of clients + +2009-09-19 Jan Moringen + + * obby/rudel-obby-server.el + (rudel-obby-client::rudel-obby/obby_document/record/ins): + construct the operation and use `rudel-remote-operation' + (rudel-obby-client::rudel-obby/obby_document/record/del): + construct the operation and use `rudel-remote-operation' + (rudel-obby-client::rudel-remote-operation): new method; transform + and apply an operation object to the server-side document; send + operation to all other clients + (rudel-obby-server::rudel-broadcast): cosmetic changes + + * obby/rudel-obby-server.el + (rudel-obby-client::rudel-obby/obby_document/record/del): swapped + local and remote revisions in the operation name to be consistent + with record/ins; does not affect behavior + +2009-09-12 Jan Moringen + + * rudel-mode.el (rudel-header-subscriptions--update-from-document): + force mode line update + (rudel-header-subscriptions--update-from-buffer): force mode line + update + (rudel-header-subscriptions-minor-mode): force mode line update + + * rudel-mode.el + (rudel-mode-line-publish-state-unpublished-string): new + customization option; string used to indicate that a buffer is not + published + (rudel-mode-line-publish-state-published-string): new + customization option; string used to indicate that a buffer is + published + (rudel-mode-line-publish-state--update-string): use + `rudel-mode-line-publish-state-unpublished-string' and + `rudel-mode-line-publish-state-unpublished-string' + + * rudel.el (rudel-session::end-hook): new slot; stores handlers of + the end hook + (rudel-session::rudel-end): run end hook + (rudel-session::rudel-join-session): install handler on session + end hook to set `rudel-current-session' to nil + (rudel-session::rudel-end-session): no need to run + rudel-session-end-hook or reset `rudel-current-session' + * rudel-hooks.el (rudel-hooks--session-start): add handler for the + end hook of the session + (rudel-hooks--session-end): remove the handler from end hook of + the session; run the rudel-session-end hook + (rudel-hooks--install-handlers): do install handler for + rudel-session-end hook; this is now done by installing the in the + session object + (rudel-hooks--uninstall-handlers): no need to remove + rudel-session-end hook + + * rudel-util.el (rudel-socket-owner::rudel-state-change): cover + more states + + * obby/rudel-obby-client.el + (rudel-obby-client-state-idle::rudel-obby/obby_document_remove): + implemented, was stub; untested though + +2009-09-10 Jan Moringen + + * obby/rudel-obby-client.el + (rudel-obby-client-state-idle::rudel-obby/net6_client_join): check + whether we have a user object for the specified user id; modify + the existing object if there is one + +2009-09-09 Jan Moringen + + * zeroconf/Makefile (whole file): regenerated + * wave/Makefile (whole file): regenerated + * Makefile (target all): build target doc + (target doc): new target; build documentation + (target tags): build target tags in doc directory + (target dist): build target dist in doc directory + +2009-09-06 Jan Moringen + + * telepathy/rudel-telepathy.el (header): fixes + (require rudel-backend): required, since we define a backend + (require rudel-transport): it is a transport backend + (class rudel-telepathy-backend): derived from + rudel-transport-backend + (rudel-telepathy-backend::initialize-instance): new method; set + version slot + (autoloading): upgraded to new backend registration style + + * INSTALL (REQUIREMENTS): mention Avahi + +2009-09-05 Jan Moringen + + * rudel.el (require rudel-util): required for `rudel-hook-object' + + * rudel-util.el (property svn:executable): removed property + * rudel-overlay.el (property svn:executable): removed property + + * doc/Project.ede (whole file): new file; project file for the + documentation directory + * doc/Makefile (whole file): new file; generated Makefile for the + documentation directory + * doc/card.tex (whole file): new file; reference card for Rudel; + source + * doc/card.pdf (whole file): new file; reference card for Rudel; + PDF + +2009-09-04 Jan Moringen + + * rudel-interactive.el (rudel-read-document): added comment + (rudel-allocate-buffer-clear-existing): handle the case in which + case a buffer with the desired name exists and is attached to a + different document; added some comments + + * rudel-mode.el (header): list all provided modes; bump version + (require rudel-hooks): required for global hooks + (rudel-mode-line-publish-state-string): new variable; buffer + local, holds publish state string for buffer + (rudel-mode-line-publish-state--add-indicator-to-mode-line): new + function; add publish state indicator to mode line + (rudel-mode-line-publish-state--remove-indicator-from-mode-line): + new function; remove publish state indicator from mode line + (rudel-mode-line-publish-state--update-string): new function; + update publish state indicator according to buffer state + (rudel-mode-line-publish-state--document-attach): new function; + handle document attaching to buffer + (rudel-mode-line-publish-state--document-detach): new function; + handle document detaching from buffer + (rudel-mode-line-publish-state-minor-mode): new minor mode; + displays publish state of buffer in mode line + (global-rudel-mode-line-publish-state-mode): new minor mode; + globalization of `rudel-mode-line-publish-state-minor-mode' + (rudel-minor-keymap): menu entries for buffer local and global + mode line publish state mode + + * rudel.el (require rudel-hooks): required or hook variables + (rudel-session-start-hook): moved to rudel-hooks.el + (rudel-session-end-hook): moved to rudel-hooks.el + * rudel-hooks.el (whole file): new file; contains hook variables + and mapping from object hooks to global hooks + * Project.ede (target rudel): added file rudel-hooks.el + * Makefile (target rudel_LISP): added file rudel-hooks.el + +2009-09-03 Jan Moringen + + * rudel-mode.el (rudel-header-subscriptions-minor-mode): improved + documentation string + +2009-08-30 Jan Moringen + + * zeroconf/Makefile (whole file): new file; generated Makefile for + the zeroconf subproject + + * wave/rudel-wave.el (whole file): new file; main class of the + wave backend + * wave/Project.ede (whole file): new file; project file for the + wave subproject + * wave/Makefile (whole file): new file; generated Makefile for the + wave subproject + * Project.ede (target autoloads): added wave directory + * Makefile (LOADDIRS): added wave and zeroconf directories + (VERSION): bumped to 0.2 + (target all): added wave and zeroconf + (tags): descend into zeroconf and wave directories + (dist): descend into zeroconf and wave directories + +2009-08-28 Jan Moringen + + * rudel.el (rudel-change-color): run the change hook of the self + user + + * rudel-overlay.el (rudel-overlay-set-face-attributes): check the + face actually exists + +2009-08-27 Jan Moringen + + * rudel-mode.el (rudel-header-subscriptions-use-images): new + variable; controls whether images are used when formatting user + names + (rudel-header-subscriptions-separator): new variable; separator + used when formatting user names + (rudel-header-subscriptions--make-format): new function; make a + format object for header line + (rudel-header-subscriptions--update-from-document): new function; + update header line from document + (rudel-header-subscriptions--update-from-buffer): new function; + update header line from buffer + (rudel-header-subscriptions--options-changed): new function; + update header line in all buffers that have + rudel-header-subscriptions-minor-mode enabled after customization + option change + (rudel-header-subscriptions--user-change): new function; update + header line after a user object change + (rudel-header-subscriptions--add-user): new function; watch newly + subscribed user and update header line + (rudel-header-subscriptions--remove-user): new function; stop + watching user and update header line + (minor mode rudel-header-subscriptions-minor-mode): new minor + mode; display subscribed users in buffer's header line + (rudel-header-subscriptions--attach): new function; enable header + subscription minor mode when attaching + (rudel-header-subscriptions--detach): new function; disable header + subscription minor mode when detaching + (rudel-header-subscriptions--add-document): new function; monitor + attaching/detaching of new document + (rudel-header-subscriptions--remove-document): new function; stop + monitoring attaching/detaching of new document + (rudel-header-subscriptions--session-start): new function; watch + documents being added/removed to/from the session + (rudel-header-subscriptions--session-end): new function; stop + watching documents being added/removed to/from the session + (minor mode global-rudel-header-subscriptions-mode): global minor + mode that controls `rudel-header-subscriptions-minor-mode' in + buffers + (advice global-rudel-header-subscriptions-mode): controls + adding/removing watches for added/removed documents when the + global mode is enabled/disabled + (rudel-minor-keymap): Added entries for + `rudel-header-subscriptions-minor-mode' and + `global-rudel-header-subscriptions-mode' + +2009-08-26 Jan Moringen + + * . (property svn:ignore): added patterns + * jupiter (property svn:ignore): added patterns + * obby (property svn:ignore): added patterns + + * rudel.el (rudel-session::add-user-hook): new slot; add user hook + function list + (rudel-session::remove-user-hook): new slot; remove user hook + function list + (rudel-session::add-document-hook): updated documentation string + (rudel-session::remove-document-hook): updated documentation + string + (rudel-session::rudel-add-user): run add user hook + (rudel-session::rudel-remove-user): run remove user hook + + * rudel.el (rudel-session-start-hook): new variable; session start + hook function list + (rudel-session-end-hook): new variable; session end hook function + list + (rudel-join-session): run session start hook + (rudel-end-session): run session end hook + + * obby/rudel-obby-server.el + (rudel-obby-client::rudel-obby/obby_document_create): call + `generate-new-buffer-name' on complete buffer name; not just name + part + + * obby/rudel-obby-server.el + (rudel-obby-client::rudel-obby/obby_document/subscribe): send + number of bytes instead of number of characters + + * obby/rudel-obby-client.el + (rudel-obby-client-state-document-synching::remaining-bytes): + fixed initarg num-bytes -> remaining-bytes + + * rudel.el (rudel-session::add-document-hook): new slot; run when + a document gets added to the session + (rudel-session::remove-document-hook): new slot; run when a + document gets removed from the session + (rudel-session::rudel-add-document): run add document hook + (rudel-session::rudel-remove-document): run remove document hook + (rudel-document::unsubscribe-hook): fixed initarg subscribe-hook + -> unsubscribe-hook + +2009-08-25 Jan Moringen + + * www/index.html (Download): link to http://bazaar-vcs.org; + improved wording + + * www/index.html (Download): changed link to source; add browse + source link for bzr + + * INSTALL (REQUIREMENTS): precise CEDET release version + + * obby/rudel-obby-server.el + (rudel-obby-client::rudel-obby/obby_user_colour): run change hook + after setting slot + (rudel-obby-server::rudel-remove-client): run change hook after + setting slot + * obby/rudel-obby-client.el + (rudel-obby-client-state-idle::rudel-obby/net6_client_part): run + change hook after setting slots + (rudel-obby-client-state-idle::rudel-obby/obby_user_colour): run + change hook after setting slot + * rudel.el (class rudel-user): derive from `rudel-hook-object' + (rudel-user::change-hook): new slot; stores change hook functions + + * obby/rudel-obby-server.el + (rudel-obby-client::rudel-obby/obby_user_colour): cosmetic changes + (rudel-obby-client::rudel-obby/obby_document/subscribe): use + `rudel-add-user' + (rudel-obby-client::rudel-obby/obby_document/unsubscribe): use + `rudel-remove-user' + (rudel-obby-server::rudel-check-username-and-color): whitespace + fixes + +2009-08-23 Jan Moringen + + * rudel.el (rudel-document::attach-hook): new slot; attach hook + function list + (rudel-document::detach-hook): new slot; detach hook function list + (rudel-document::rudel-attach-to-buffer): run hook `attach-hook' + (rudel-document::rudel-detach-from-buffer): run hook `detach-hook' + (rudel-document::rudel-add-user): improved documentation string + (rudel-document::rudel-remove-user): improved documentation string + + * obby/rudel-obby.el + (rudel-obby-user::eieio-speedbar-object-buttonname): fixed typo + +2009-08-21 Jan Moringen + + * obby/rudel-obby-client.el + (rudel-obby-client-state-idle::rudel-obby/obby_document/subscribe): + call `rudel-add-user' + (rudel-obby-client-state-idle::rudel-obby/obby_document/unsubscribe): + call `rudel-remove-user' + * rudel.el (class rudel-document): mixin rudel-hook-object + (rudel-document::subscribe-hook): new slot; subscribe-hook + function list + (rudel-document::unsubscribe-hook): new slot; unsubscribe-hook + function list + (rudel-document::rudel-add-user): new method; add user to list of + subscribed users and run subscribe-hook + (rudel-document::rudel-remove-user): new method; remove user from + list of subscribed users and run unsubscribe-hook + + * obby/rudel-obby.el (header): cosmetic changes + (include rudel-icons): `rudel-display-string' uses icons + (rudel-obby-user::eieio-speedbar-object-buttonname): use + `rudel-display-string' + (rudel-obby-user::rudel-display-string): new method; textual + representation of user object + (rudel-obby-parse-message): cosmetic changes + * rudel.el (include rudel-icons): `rudel-display-string' uses + icons + (rudel-user::rudel-display-string): new method; textual + representation of user object + + * rudel-util.el (rudel-hook-object): new class; abstract mixin for + classes that offer hooks + (rudel-hook-object::object-add-hook): new method; add function to + hook list + (rudel-hook-object::object-remove-hook): new method; remove + function from hook list + (rudel-hook-object::object-run-hook-with-args): new method; run + hook functions + +2009-08-20 Jan Moringen + + * icons/plaintext.svg (new file): plaintext icon + * icons/person.svg (new file): person icon + * icons/encrypted.svg (new file): encrypted icon + * icons/document.svg (new file): document icon + * icons/disconnected.svg (new file): disconnected icon + * icons/connected.svg (new file): connected icon + * rudel-icons.el (new file): loading icons + * Project.ede (target rudel): added rudel-icons.el + +2009-08-19 Jan Moringen + + * rudel.el (rudel-join-session): renamed local variable backend to + session-initiation-backend + +2009-08-17 Jan Moringen + + * obby/rudel-obby-state.el (rudel-obby-state::rudel-accept): fixed + format of warning message + + * rudel-state-machine.el (require working): needed by + `rudel-state-wait' + + * obby/rudel-obby-client.el + (rudel-obby-client-state-idle::rudel-obby/obby_user_colour): set + face attributes + * rudel.el (rudel-change-color): set face attributes + * rudel-overlay.el (rudel-overlay-make-face): use + `rudel-overlay-set-face-attributes' + (rudel-overlay-set-face-attributes): new function; set face + attributes + + * rudel-overlay.el (rudel-overlay-author-set-properties): call + `rudel-overlay-make-face-symbol' + (rudel-overlay-make-face-symbol): new function; return face symbol + +2009-08-16 Jan Moringen + + * rudel-overlay.el (rudel-overlay-author-display): option that + controls display of author overlays + (rudel-make-author-overlay): call + `rudel-overlay-author-set-properties' to set the overlay + properties + (rudel-overlay-author-set-properties): new function; set overlay + properties; respects rudel-overlay-author-display + (rudel-overlay-author-update): new function; update overlay + properties based on associated user object + (rudel-overlay-options-changed): new function; call + `rudel-overlay-author-update' on all Rudel overlays in all buffers + * rudel-mode.el (header): cosmetic changes + (rudel-minor-menu): added menu entry to toggle display of author + overlays + + * rudel-overlay.el (rudel-make-author-overlay): use `intern' + instead of `make-symbol' when allocating the face name; this way, + faces can actually be created lazily + (rudel-overlay-make-face): call `make-face' for the new face + + * obby/rudel-obby-client.el + (rudel-obby-client-state-document-synching::object-print): fixed + slot name remaining-bytes + + * rudel.el (rudel-host-session): the backend object is the cdr of + the result of `rudel-backend-choose' + +2009-08-15 Jan Moringen + + * telepathy/rudel-telepathy.el (header): added file comment + * obby/rudel-obby-state.el (header): fixed email address + (whole file): whitespace fixes + * jupiter/jupiter.el (header): cosmetic changes + (whole file): whitespace fixes + * rudel-util.el (header): cosmetic changes + * rudel-mode.el (header): cosmetic changes + * rudel-errors.el (header): fixed type + (whole file): whitespace fixes + + * obby/rudel-obby.el (rudel-ask-connect-info): added optional + argument info; do not ask for things that are already specified in + info + (autoload): register obby service with the Zeroconf backend + * zeroconf/rudel-zeroconf.el (new file): Zeroconf session + initiation for Rudel + * zeroconf/Project.ede (new file): subproject zeroconf + * rudel.el (rudel-join-session): call `rudel-ask-info' to augment + connect info + * Project.ede (target autoloads): added directory zeroconf + * INSTALL (INSTALLING): mention zeroconf subdirectory + (COMPILING): mention zeroconf target + + * rudel-backend.el (rudel-backend-factory::rudel-get-backend): + return backend as a cons + (rudel-backend-get): new function; convenience function for + getting backends + + * obby/rudel-obby.el (header): extended commentary and history + (rudel-obby-version): bumped to 0.2 + (rudel-obby-backend::initialize-instance): set :version slot in + constructor instead of using obsolete lambda expression in + initform + +2009-08-14 Jan Moringen + + * Project.ede (project rudel): bumped version to 0.2; added + mailing list and path to web files + + * rudel-backend.el (rudel-backend-factory): do not initialize + backends with lambda expression + (rudel-backend-factory::initialize-instance): new method; + initialize backends + (rudel-backend-cons-p): use `object-p' instead of `eieio-object-p' + (rudel-backend-dump): changed format slightly + +2009-08-13 Jan Moringen + + * rudel.el (include rudel-session-initiation): required for + `rudel-join-session' + (rudel-join-session): mostly rewritten; moved user interaction to + `interactive' form + * rudel-session-initiation.el (new file): session initiation + backend interface and high-level programming interface + * Project.ede (target rudel): added rudel-session-initiation.el + * Makefile (rudel_LISP): added rudel-session-initiation.el + + * rudel-interactive.el (rudel-read-session): discriminate sessions + vs. session generating objects using `rudel-backend-cons-p' + + * rudel-backend.el (rudel-backend-cons-p): new function; checks + whether a cons consists of a backend name and object + +2009-08-12 Jan Moringen + + * rudel-interactive.el (rudel-read-session): new function; read + session by name and return it + + * rudel-interactive.el (rudel-read-backend): argument backends is + no longer optional; always return a cons of backend name and + object; updated documentation string + (whole file): whitespace fixes + * rudel-backend.el (rudel-backend-choose): always return a cons of + backend name and object; updated documentation string + +2009-08-11 Jan Moringen + + * rudel.el (file header): added project URL + (whole file): improved some comments + +2009-08-10 Jan Moringen + + * rudel.el (include eieio-base): needed for eieio-named + +2009-08-04 Jan Moringen + + * rudel-state-machine.el (header section commentary): updated + (rudel-state-machine::initialize-instance): use + `rudel--switch-to-return-value' to allow immediate switch to + another state + (rudel-state-machine::rudel-switch): use + `rudel--switch-to-return-value' to switch to successor state + (rudel-state-machine::rudel--switch-to-return-value): new function + switch to successor state for different kinds of specifications of + the successor state + +2009-08-03 Jan Moringen + + * obby/rudel-obby.el (require rudel-backend): now necessary + (require rudel-protocol): now necessary + (class rudel-obby-backend): now derived from `rudel-backend'; + autoloaded + (autoloading): use `rudel-add-backend' + * rudel.el (require rudel-backend): backends have their own file + (class rudel-backend): moved to rudel-backend.el + (rudel-load-backends): moved to rudel-backend.el + (rudel-suitable-backends): moved to rudel-backend.el + (rudel-choose-backend): moved to rudel-backend.el + (rudel-join-session): use `rudel-backend-choose' + (rudel-host-session): use `rudel-backend-choose' + + * rudel-protocol.el (new file): interface for Rudel protocol + backends + * Project.ede (target rudel): added rudel-protocol.el + * Makefile (rudel_LISP): added rudel-protocol.el + + * rudel-backend.el (rudel-backend-factory::rudel-get-backend): + improved documentation string + (rudel-backend-factory::rudel-suitable-backends): improved + documentation string + (rudel-backend-suitable-backends): improved documentation string + (rudel-backend-choose): When only one backend matches, do not + check interactivity using `interactive-p', that does not work; + call `sit-for' correctly + +2009-08-02 Jan Moringen + + * jupiter/Makefile (whole file): regenerated; reflects CEDET + changes + * rudel-backend.el (new file): generic backend management, query + and loading functions + * Project.ede (target rudel): added rudel-backend.el to sources + * Makefile (rudel_LISP): added rudel-backend.el + + * obby/rudel-obby-server.el + (rudel-obby-client::rudel-obby/net6_client_login): use slot + :object-name instead of calling object-name-string + (rudel-obby-server::rudel-add-context): use slot :object-name + instead of calling object-name-string + * obby/rudel-obby-client.el + (rudel-obby-client-state-idle::rudel-obby/obby_document/rename): + use slot :object-name instead of calling object-name-string + (rudel-obby-connection::rudel-add-context): use slot :object-name + instead of calling object-name-string + (rudel-obby-connection::rudel-publish): use slot :object-name + instead of calling object-name-string + * rudel.el (class rudel-user): added base class eieio-named for + virtual slot :object-name; made abstract + (class rudel-document): added base class eieio-named for virtual + slot :object-name + * rudel-overlay.el (rudel-make-author-overlay): use slot + :object-name instead of calling object-name-string + + * rudel-state-machine.el + (rudel-state-machine::initialize-instance): use &rest slots + instead of just slots + +2009-07-15 Jan Moringen + + * rudel.el (rudel-document::rudel-detach-from-buffer): call + `rudel-overlays-remove-all'; remove `rudel-unpublish-buffer' hook + early + * rudel-overlay.el (rudel-overlays-remove-all): new function; + remove all overlays from the current buffer + (whole file): cosmetic changes; typo fixes; whitespace fixes + + * obby/rudel-obby.el (rudel-obby-document::rudel-unique-name): + Check `next-method-p' before calling the next method + * obby/rudel-obby-client.el + (rudel-obby-connection::initialize-instance): Check + `next-method-p' before calling the next method + (rudel-obby-connection::rudel-register-state): Check + `next-method-p' before calling the next method + (rudel-obby-connection::rudel-disconnect): Check + `next-method-p' before calling the next method + * rudel.el (rudel-client-session::rudel-end): Check + `next-method-p' before calling the next method + +2009-07-13 Jan Moringen + + * obby/rudel-obby.el (rudel-obby-backend::rudel-connect): only + start the network process after everything is ready; wait for the + connection state machine to reach a success or error state + (rudel-obby-backend::rudel-host): cosmetic changes + (class rudel-obby-user): cosmetic changes + * rudel.el (rudel-join-session): reversed order of creation for + session and connection; do not catch errors to give error messages + a chance + + * jupiter/jupiter-insert.el (jupiter-insert::jupiter-transform): + cosmetic changes + (whole file): whitespace fixes + + * rudel.el (rudel-session::rudel-find-user): added documentation + string; cosmetic changes + (rudel-session::rudel-find-document): added documentation string; + cosmetic changes + (whole file): whitespace fixes + +2009-07-12 Jan Moringen + + * rudel-speedbar.el (eieio-speedbar-object-buttonname): use + `rudel-unique-name' instead of `object-name-string' + + * obby/rudel-obby-state.el (rudel-enter): return nil + + * obby/rudel-obby-client.el + (rudel-obby-client-state-joining::rudel-enter): return nil + (rudel-obby-client-state-join-failed::rudel-enter): return nil + (rudel-obby-client-state-session-synching::rudel-enter): return + nil + (rudel-obby-client-state-session-synching::object-print): new + method; add number of remaining items to string representation + (rudel-obby-client-state-subscribing::rudel-enter): nil + (rudel-obby-client-state-document-synching::rudel-enter): nil + (rudel-obby-client-state-document-synching::object-print): new + method; add number of remaining bytes to string representation + +2009-07-11 Jan Moringen + + * obby/rudel-obby-util.el (with-parsed-arguments): added debug + declaration + (whole file): whitespace fixes + * rudel-util.el (rudel-assemble-line-fragments): added debug + declaration + (rudel-loop-lines): added debug declaration + (rudel-loop-chunks): fixed documentation string; added debug + declaration + + * rudel-state-machine.el (rudel-no-start-state): new error symbol + (rudel-state-machine::initialize-instance): try hard to find a + suitable start sate; call `rudel-switch' instead of just + `rudel-enter' + (rudel-state-machine::rudel-switch): always return the new current + state; accept successor state from `rudel-enter' + (rudel-state-machine::object-print): new method; add current state + of state machine to string representation + (rudel-state-machine::rudel-state-wait): whitespace fixes + +2009-07-07 Jan Moringen + + * obby/rudel-obby.el (class rudel-obby-backend): better + documentation string + (rudel-obby-backend::rudel-ask-connect-info): added documentation + string + (rudel-obby-backend::rudel-connect): added documentation string + (rudel-obby-backend::rudel-ask-host-info): added documentation + string + (rudel-obby-backend::rudel-host): added documentation string + (rudel-obby-backend::rudel-make-document): added documentation + string + (rudel-obby-send): cosmetic changes + (whole file): whitespace fixes + +2009-07-05 Jan Moringen + + * INSTALL (REQUIREMENTS): added Emacs itself and GNUTls + + * rudel-tls.el (rudel-tls-start-tls): added a message + (rudel-tls-wait-handshake): switch to filter + `rudel-tls-established' instead of restoring the original filter + (rudel-tls-established): new function; filters GNUTls messages in + encrypted connections + (whole file): whitespace fixes + +2009-07-04 Jan Moringen + + * README (INTRODUCTION): extended a bit + (JOINING A SESSION): added prompt/input example and an explanation + of encryption issues in the obby backend + (KNOWN BUGS): new section; no known bugs yet, though + +2009-06-17 Jan Moringen + + * obby/rudel-obby-client.el (require rudel-state-machine): the + connection now is a state machine + (require rudel-obby-errors): used when analyzing login failures + (require rudel-obby-state): useful base classes for states + (rudel-obby-client-state-new): new class; initial state of new + connections + (rudel-obby-client-state-encryption-negotiate): new class; + first encryption state + (rudel-obby-client-state-encryption-start): new class; second + encryption state + (rudel-obby-client-state-joining): new class + (rudel-obby-client-state-join-failed): new class; entered after + failed login attempt + (rudel-obby-client-state idle): new class; default state of + established connections + (rudel-obby-client-state-session-synching): new class; + synchronizing session state to client + (rudel-obby-client-state-subscribing): new class; first state of + document subscription + (rudel-obby-client-state-document-synching): new class; + synchronizing document state to client + (rudel-obby-client-connection-states): new variable; alist of + name symbols and associated state classes + (rudel-obby-connection::initialize-instance): register states + (rudel-obby-connection::rudel-register-state): new method; set + connection slot of state to its connection + (rudel-obby-connection::rudel-add-context): cleanup + (rudel-obby-connection::rudel-message): dispatch message using + `rudel-accept' + (rudel-obby-connection::rudel-subscribe-to): initiate subscription + by switching to state 'subscribing' + + * obby/rudel-obby-state.el (rudel-obby-document-handler): new + class; mixin class that provides handling of obby 'document' + messages + + * rudel-state-machine.el + (rudel-state-machine::initialize-instance): find start state in + slots and switch into it + (while-file): whitespace fixes + +2009-06-15 Jan Moringen + + * www/index.html (section download): fixed link to download area + (whole file): whitespace cleanup + + * obby/rudel-obby-state.el (new file): finite state machine states + for the rudel backend + * obby/Project.ede (target rudel/obby): added rudel-obby-state.el + * obby/Makefile (target obby_LISP): added rudel-obby-state.el + + * rudel-util.el (require rudel-errors): required for dispatch + errors + (symbol rudel-dispatch-error): new condition symbol for dispatch + errors + (rudel-dispatch): new function; dispatch to method based on method + name + (whole file): whitespace fixes + +2009-06-14 Jan Moringen + + * rudel-mode.el (global-rudel-minor-mode): removed; the variable + is created by `define-minor-mode' + (minor-mode-alist): managed by `define-minor-mode' + + * rudel-mode.el (require easy-mmode): used to define global rudel + minor mode + (rudel-minor-keymap): cosmetic changes + (global-rudel-minor-mode): use `define-minor-mode' to define the + mode + (whole file): whitespace cleanup + + * rudel-telepathy.el (whole file): moved to + telepathy/rudel-telepathy.el + * telepathy/rudel-telepathy.el (whole file): moved from + rudel-telepathy.el + + * obby/rudel-obby-server.el (whole file): whitespace cleanup + * obby/rudel-obby-client.el (require rudel-obby): removed; + unnecessary + (rudel-obby-connection::initialize-instance): use &rest for `slots' + argument; cosmetic changes + (rudel-obby-connection::rudel-change-color-): use own `rudel-send' + method instead of the socket's + (rudel-obby-connection::rudel-subscribe-to): cosmetic changes + (rudel-obby-connection::rudel-unsubscribe-from): cosmetic changes + (rudel-obby-connection::rudel-local-operation): cosmetic changes + (whole file): whitespace cleanup + +2009-06-13 Jan Moringen + + * rudel-errors.el (new file): error data + * rudel-state-machine.el (new file): a simple finite state machine + implementation + * Project.ede (target rudel): added rudel-errors.el and + rudel-state-machine.el + * Makefile (target rudel_LISP): added rudel-errors.el and + rudel-state-machine.el + +2009-06-12 Jan Moringen + + * obby/rudel-obby-server.el (require rudel-obby-errors): now + required + (rudel-obby-client::rudel-obby/net6_client_login): check username + and color before adding the client + (rudel-obby-server::rudel-check-username-and-color): new method; + make sure username and color are valid and there are no duplicates + * obby/rudel-obby-errors.el (new file): error data for the obby + backend + * obby/Project.ede (rudel/obby): added rudel-obby-errors.el + * obby/Makefile (obby_LISP): added rudel-obby-errors.el + + * rudel.el (rudel-user::rudel-color): added accessor `rudel-color' + + * obby/rudel-obby-server.el (require cl): required + + * obby/rudel-obby-server.el + (rudel-obby-client::rudel-obby/net6_client_login): send suffices + of synchronized documents + +2009-06-11 Jan Moringen + + * obby/rudel-obby-server.el + (rudel-obby-client::rudel-obby/obby_document_create): send + document/rename message to client when the document suffix changes + + * obby/rudel-obby-server.el + (rudel-obby-client::rudel-obby/net6_client_login): use + `with-parsed-arguments' + (rudel-obby-client::rudel-obby/obby_user_colour): use + `with-parsed-arguments' + (rudel-obby-client::rudel-obby/obby_document_create): use + `with-parsed-arguments' + (rudel-obby-client::rudel-obby/obby_document): use + `with-parsed-arguments' + (rudel-obby-client::rudel-obby/obby_document/subscribe): use + `with-parsed-arguments' + (rudel-obby-client::rudel-obby/obby_document/unsubscribe): use + `with-parsed-arguments' + (rudel-obby-client::rudel-obby/obby_document/record): use + `with-parsed-arguments' + (rudel-obby-client::rudel-obby/obby_document/record/ins): use + `with-parsed-arguments' + (rudel-obby-client::rudel-obby/obby_document/record/del): use + `with-parsed-arguments' + + * obby/rudel-obby-server.el (header): fixed version + +2009-06-10 Jan Moringen + + * obby/rudel-obby-client.el + (rudel-obby-connection::rudel-local-operation): fixed wording in + comment + + * www/images/development.png (whole file): changed size 48x48 -> + 64x64 + * www/images/development.svg (whole file): changed size 48x48 -> + 64x64 + * www/images/download.png (whole file): changed size 48x48 -> + 64x64 + * www/images/download.svg (whole file): changed size 48x48 -> + 64x64 + * www/images/info.png (whole file): changed size 48x48 -> 64x64 + * www/images/info.svg (whole file): changed size 48x48 -> 64x64 + + * index.html (section introduction): wording fixes; link to Gobby + (whole file): removed external link class for sourceforge links + (section download): added link to INSTALL file in svn code browser + (section footer): fixed copyright + + * compatibility.html (section footer): fixed copyright + +2009-06-09 Jan Moringen + + * INSTALL (section REQUIREMENTS): removed ERT which is not + currently used + (section INSTALL): some wording and file name fixes + (section COMPILING): precise make command + + * www/index.html (section development): fixed mailing list and + issue tracker links; removed email address + + * www/compatibility.html (head): fixed charset + (section semantic): added missing

tag + + * www/compatibility.html (new file): compatibility information + * www/index.html (new file): start page + * www/style.css (new file): stylesheet + * www/images/development.png (new file): development icon + * www/images/development.svg (new file): development icon + * www/images/download.png (new file): download icon + * www/images/download.svg (new file): download icon + * www/images/email-link.png (new file): icon for email links + * www/images/external-link.png (new file): icon for external links + * www/images/info.png (new file): info icon + * www/images/info.svg (new file): info icon + * www/images/screenshot.png (new file): screenshot for the start + page + + * obby/rudel-obby-serverl.el + (rudel-obby-client::rudel-obby/obby_document_create): find unique + suffix for the new document; send suffix to clients + + * obby/rudel-obby.el (header): fixed license text + * obby/rudel-obby-util.el (header): fixed license text + * obby/rudel-obby-server.el (header): fixed license text + * obby/rudel-obby-client.el (header): fixed license text + * jupiter/jupiter.el (header): fixed license text + * jupiter/jupiter-operation.el (header): fixed license text + * jupiter/jupiter-nop.el (header): fixed license text + * jupiter/jupiter-insert.el (header): fixed license text + * jupiter/jupiter-delete.el (header): fixed license text + * jupiter/jupiter-compound.el (header): fixed license text + * rudel.el (header): fixed license text + * rudel-util.el (header): fixed license text + * rudel-tls.el (header): fixed license text + * rudel-telepathy.e (header): fixed license text + * rudel-speedbar.el (header): fixed license text + * rudel-overlay.el (header): fixed license text + * rudel-operators.el (header): fixed license text + * rudel-operations.el (header): fixed license text + * rudel-mode.el (header): fixed license text + * rudel-interactive.el (header): fixed license text + * rudel-compat.el (header): fixed license text + + * obby/rudel-obby-util.el (require cl): now required + (generic rudel-obby-char->byte): new generic; char positions -> + byte positions + (jupiter-insert::rudel-obby-char->byte): new method; char + positions -> byte positions + (jupiter-delete::rudel-obby-char->byte): new method; char + positions -> byte positions + (jupiter-compound::rudel-obby-char->byte): new method; char + positions -> byte positions + (jupiter-nop::rudel-obby-char->byte): new method; char positions + -> byte positions + (generic rudel-obby-byte->char): new generic; byte positions -> + char positions + (jupiter-insert::rudel-obby-byte->char): new method; byte + positions -> char positions + (jupiter-delete::rudel-obby-byte->char): new method; byte + positions -> char positions + (jupiter-compound::rudel-obby-byte->char): new method; byte + positions -> char positions + (jupiter-nop::rudel-obby-byte->char): new method; byte positions + -> char positions + * obby/rudel-obby-client.el + (rudel-obby-connection::rudel-local-operation): call + `rudel-obby-char->byte' before processing + (rudel-obby-connection::rudel-remote-operation): call + `rudel-obby-byte->char' before processing + * rudel.el (rudel-buffer-change-workaround-data): new variable; + holds change data for later use + (rudel-document::rudel-attach-to-buffer): add + `rudel-buffer-change-workaround' to 'before-change-functions' + (rudel-document::rudel-detach-from-buffer): remove + `rudel-buffer-change-workaround' from 'before-change-functions' + (rudel-buffer-change-workaround): new function; stores change data + for later use + +2009-06-07 Jan Moringen + + * obby/rudel-obby-client.el + (rudel-obby-connection::rudel-obby/net6_client_join): use + `with-parsed-arguments' + (rudel-obby-connection::rudel-obby/net6_client_part): use + `with-parsed-arguments' + (rudel-obby-connection::rudel-obby/obby_welcome): use + `with-parsed-arguments' + (rudel-obby-connection::rudel-obby/obby_sync_init): use + `with-parsed-arguments' + (rudel-obby-connection::rudel-obby/obby_sync_usertable_user): use + `with-parsed-arguments' + (rudel-obby-connection::rudel-obby/obby_user_colour): use + `with-parsed-arguments' + (rudel-obby-connection::rudel-obby/obby_sync_doclist_document): + use `with-parsed-arguments' + (rudel-obby-connection::rudel-obby/obby_document_create): use + `with-parsed-arguments' + (rudel-obby-connection::rudel-obby/obby_document_remove): use + `with-parsed-arguments' + (rudel-obby-connection::rudel-obby/obby_document): use + `with-parsed-arguments'; cleanup + (rudel-obby-connection::rudel-obby/obby_document/rename): use + `with-parsed-arguments' + (rudel-obby-connection::rudel-obby/obby_document/subscribe): use + `with-parsed-arguments' + (rudel-obby-connection::rudel-obby/obby_document/unsubscribe): use + `with-parsed-arguments' + (rudel-obby-connection::rudel-obby/obby_document/sync_chunk): use + `with-parsed-arguments' + (rudel-obby-connection::rudel-obby/obby_document/record): use + `with-parsed-arguments' + (rudel-obby-connection::rudel-obby/obby_document/record/ins): use + `with-parsed-arguments' + (rudel-obby-connection::rudel-obby/obby_document/record/del): use + `with-parsed-arguments' + +2009-06-06 Jan Moringen + + * obby/rudel-obby-client.el + (rudel-obby-connection::rudel-obby/net6_client_part): use `eql', + not `=' when calling `rudel-find-user' since the client id can be + nil + + * obby/rudel-obby-util.el (require jupiter): silence byte compiler + + * obby/rudel-obby-util.el (rudel-obby-dispatch): moved inside file + (with-parsed-arguments): new macro; executed forms with variables + bound to parsed arguments + +2009-06-04 Jan Moringen + + * rudel.el (require rudel-interactive): interactive functions use + `rudel-read-backend' and `rudel-read-document' + + * rudel.el (rudel-buffer-document): mark as permanent local + variable to prevent deletion in the event of a major-mode change + (rudel-document::rudel-attach-to-buffer): add (buffer-locally) + `rudel-handle-major-mode-change' to 'change-major-mode-hook' such + that it can repair damage caused by major-mode changes + (rudel-document::rudel-detach-from-buffer): remove + `rudel-handle-major-mode-change' from 'change-major-mode-hook' + (rudel-mode-changed-buffers) new variable; temporarily stores + buffers that underwent major-mode changes + (rudel-handle-major-mode-change): new function; schedules buffers + for repair after major-mode changes + (rudel-after-major-mode-change): new function; repairs buffer + objects after major-mode changes + +2009-06-03 Jan Moringen + + * rudel.el (rudel-buffer-has-document-p): use `buffer-local-value' + (rudel-buffer-document): use `buffer-local-value' + (rudel-set-buffer-document): added documentation string + +2009-06-02 Jan Moringen + + * rudel.el (rudel-handle-buffer-change): There are three cases + now: insert, delete and arbitrary changes; arbitrary changes + generate a delete and insert operation + + * rudel-mode.el (rudel-minor-keymap): added some comments + (global-rudel-minor-mode): extended documentation string; cleaned + up code; added comments + + * obby/rudel-obby-client.el + (rudel-obby-connection::rudel-obby/net6_client_part): fixed typo + in variable name client-id-numeric + +2009-05-28 Jan Moringen + + * obby/rudel-obby-util.el (header): Fixed version (1.0 -> 0.1) + + * obby/rudel-obby-client.el (header): Fixed version (1.0 -> 0.1) + (rudel-obby-connection::rudel-obby/obby_document/record/split): + introduced temporary variable + +2009-??-?? Jan Moringen + + * rudel.el (rudel-buffer-document): removed; replaced by + rudel-buffer-documents hash-table + (rudel-buffer-documents): new variable; a hash-table, which + associates documents to buffers + (rudel-buffer-has-document-p): + (rudel-buffer-document): + (rudel-set-buffer-document): + +2009-03-16 Jan Moringen + + * obby/rudel-obby-client.el + (rudel-obby-connection::rudel-obby/net6_client_part): do not crash + if the client id cannot be found + + * obby/rudel-obby.el (rudel-obby-backend::rudel-make-document): + specify value 1 for slot suffix + (rudel-obby-document::suffix): new slot; contains the suffix + number of the document + (rudel-obby-document::rudel-unique-name): new method; return + unique name based on document name and suffix + * obby/rudel-obby-client.el + (rudel-obby-connection::rudel-obby/obby_sync_doclist_document): do + not ignore the suffix when creating the document object + (rudel-obby-connection::rudel-obby/obby_document_create): do not + ignore the suffix when creating the document object + (rudel-obby-connection::rudel-obby/obby_document/rename): change + document name and suffix as requested + * rudel.el (rudel-document::rudel-unique-name): new method; + returns a unique name for the document + (rudel-document::rudel-suggested-buffer-name): new method; returns + a suggested name for the buffer attached to the document + (rudel-subscribe): use `rudel-suggested-buffer-name' instead of + the object name + * rudel-interactive.el (rudel-read-document): use the unique names + of the documents instead of the object names + +2009-02-27 Jan Moringen + + * rudel.el (rudel-document::rudel-attach-to-buffer): add hook to + detach document from the buffer when the buffer is killed + (rudel-document::rudel-detach-from-buffer): remove unpublish + function kill buffer hook + +2009-02-23 Jan Moringen + + * rudel.el (rudel-document): minor cleanup + (rudel-document::rudel-attach-to-buffer): stylistic changes + (rudel-document::rudel-detach-from-buffer): fixed argument order in + call to `rudel-set-buffer-document' + + * obby/rudel-obby-server.el + (rudel-obby-server::rudel-remove-client): Make sure there is a + user object before setting the status to offline + + * obby/rudel-obby-client.el (rudel-obby/net6_encryption_failed): + only fail if encryption has been requested in the first + place. otherwise, just carry on + + * rudel.el (rudel-document::rudel-attach-to-buffer): use + `rudel-set-buffer-document' + (rudel-document::rudel-detach-from-buffer): use + `rudel-set-buffer-document' + (rudel-buffer-has-document-p): new function; test whether a buffer + has an associated document object + (rudel-buffer-document): new function; returns associated document + object of a buffer + (rudel-set-buffer-document): new functions; sets associated + document object of a buffer + (rudel-handle-buffer-change): use `rudel-buffer-has-document-p' + (rudel-publish-buffer): use `rudel-buffer-has-document-p' + (rudel-unpublish-buffer): use `rudel-buffer-has-document-p' and + `rudel-buffer-document' + * rudel-mode.el (rudel-minor-keymap): use + `rudel-buffer-has-document-p' + + * obby/rudel-obby-client.el (rudel-obby/obby_document/rename): + new method; dummy implementation + + * obby/rudel-obby-client.el (rudel-obby/net6_client_join): + stylistic change + +2009-02-21 Jan Moringen + + * obby/rudel-obby-util.el (generic rudel-operation->message): new + generic function; serializes an operation + (jupiter-insert::rudel-operation->message): new method + (jupiter-delete::rudel-operation->message): new method + (jupiter-compound::rudel-operation->message): new method + (jupiter-nop::rudel-operation->message): new method + (rudel-message->operation): new function; deserializes an + operation from a received message + * obby/rudel-obby-client.el + (rudel-obby-connection::rudel-local-insert): do not construct + message string; use `rudel-operation->message' + (rudel-obby-connection::rudel-local-delete): do not construct + message string; use `rudel-operation->message' + (rudel-obby-connection::rudel-local-operation): new method; + handles operation objects that represent local operations + (rudel-obby-connection::rudel-remote-operation): new method; + handles operation objects that represent remote operations + (rudel-obby-connection::rudel-obby/obby_document/record/ins): + construct operation name correctly; do not call jupiter context to + transform operation + (rudel-obby-connection::rudel-obby/obby_document/record/del): + construct operation name correctly; do not call jupiter context to + transform operation + (rudel-obby-connection::rudel-obby/obby_document/record/split): + new method; handles split operation messages + (rudel-obby-connection::rudel-obby/obby_document/record/noop): new + method; handles nop messages + +2009-02-12 Jan Moringen + + * obby/rudel-obby.el (rudel-obby-backend::rudel-ask-connect-info): + ask whether to encrypt the connection + (rudel-obby-backend::rudel-connect): create connection object + capable of StartTLS encryption when encryption was requested + * obby/rudel-obby-client.el + (rudel-obby-connection::rudel-obby/net6_encryption): do not fail + when the server requests encryption + (rudel-obby-connection::rudel-obby/net6_encryption_begin): start + TLS encryption for the connection + (rudel-obby-connection::rudel-obby/net6_encryption_failed): new + method; stub + * rudel-tls.el (new file): StartTLS encryption for Rudel + * Project.ede ("rudel"): added rudel-tls.el + * Makefile (rudel_LISP): added rudel-tls.el + +2009-02-06 Jan Moringen + + * rudel-compat.el (header): fixed email address, keywords, legal + notice and file commentary + +2009-02-05 Jan Moringen + + * obby/rudel-obby.el (rudel-compat): require rudel-compat for + `read-color' + * rudel.el (rudel-compat): require rudel-compat for `read-color' + * rudel-interactive.el (rudel-compat): require rudel-compat for + `read-color' + * rudel-compat.el (new file): compatibility code + * Project.ede (rudel): added rudel-compat.el + * Makefile (rudel_LISP): regenerated: added rudel-compat.el + +2009-02-04 Jan Moringen + + * obby/rudel-obby.el (require rudel) + * obby/rudel-obby-util.el (require rudel) + * obby/rudel-obby-client.el (require rudel-obby): make compilation + succeed + + * rudel.el (include eieio-speedbar): I need it for now; I should + get rid of it later + + * INSTALL (REQUIREMENTS): added note that CVS version of cedet is + required + (INSTALLING): added subdirectories jupiter and obby in load path + listing; fixed name of autoload file + +2009-02-02 Jan Moringen + + * obby/rudel-obby.el (rudel-obby-backend::rudel-ask-connect-info) + (rudel-obby-backend::rudel-host, rudel-obby-replace-in-string) + * obby/rudel-obby-util.el (rudel-obby-dispatch) + * obby/rudel-obby-server.el (rudel-obby-client::rudel-obby/obby_document) + (rudel-obby-server::rudel-broadcast, rudel-obby-server::rudel-make-user) + * obby/rudel-obby-client.el (rudel-obby-connection::rudel-obby/net6_client_join) + (rudel-obby-connection::rudel-obby/obby_document) + * jupiter/jupiter-operation.el (jupiter-operation) + * rudel.el (rudel-backend, rudel-session, rudel-server-session) + (rudel-connection, rudel-document) + (rudel-document::rudel-attach-to-buffer) + (rudel-document::rudel-detach-from-buffer) + (rudel-document::rudel-insert, rudel-document::rudel-delete) + (rudel-change-color) + * rudel-util.el (rudel-assemble-line-fragments, rudel-loop-lines) + * rudel-overlay.el (rudel-make-author-overlay) + * rudel-interactive.el (rudel-read-backend, rudel-read-user-color) + (rudel-read-user, rudel-read-document): replaced 't by t + + * rudel-operators.el (rudel-overlay-operators::rudel-insert): + Fixed computation of insertion offset when appending to the end of + the buffer string + + * rudel.el (rudel-document::rudel-chunks): fixed invalid access to + last chunk for empty buffer + + * rudel.el (rudel-document::rudel-attach-to-buffer): fixed + incorrect slot reference + + * obby/rudel-obby-server.el + (rudel-obby-client::rudel-obby/obby_document/subscribe): minor + rearrangement of expressions + (rudel-obby-client::rudel-obby/obby_document/unsubscribe): minor + rearrangement of expressions + + * obby/rudel-obby-server.el + (rudel-obby-client::rudel-obby/obby_document/record/ins): added + documentation string + (rudel-obby-client::rudel-obby/obby_document/record/del): added + documentation string + (rudel-obby-server): cosmetic change + + * jupiter/jupiter.el (jupiter-context::jupiter-remote-operation): + improved documentation string; cosmetic changes + + * jupiter/jupiter-insert.el (jupiter-insert::jupiter-transform): + improved some comments + + * rudel.el (rudel-document::rudel-attach-to-buffer): renamed + some variables; added documentation string + (rudel-document::rudel-insert): improved documentation string + (rudel-document::rudel-chunks): do not create chunks when buffer + string is empty; improved comments + (rudel-choose-backend): compare number using `=' not `eq' + + * obby/rudel-obby-server.el + (rudel-obby-client::rudel-obby/obby_document/record/ins): use + `rudel-remote-operation' instead of `rudel-remote-insert' + (rudel-obby-client::rudel-obby/obby_document/record/del): use + `rudel-remote-operation' instead of `rudel-remote-delete' + * obby/rudel-obby-client.el (include rudel-operations): for + rudel-insert-op and rudel-delete-op + (rudel-obby-connection::rudel-obby/obby_document/sync_chunk): use + `rudel-remote-operation' with rudel-insert-op to insert chunks + (rudel-obby-connection::rudel-obby/obby_document/record/ins): use + `rudel-remote-operation' instead of `rudel-remote-insert' + (rudel-obby-connection::rudel-obby/obby_document/record/del): use + `rudel-remote-operation' instead of `rudel-remote-delete' + * jupiter/jupiter-operation.el (include rudel-operations): for + rudel-operation + (jupiter-operation): derived from rudel-operation + (jupiter-operation::jupiter-apply): removed; replaced by generic + `rudel-apply' + * jupiter/jupiter-nop.el (jupiter-nop::jupiter-apply): removed; + replaced by `rudel-apply' + (jupiter-nop::rudel-apply): new method; implements generic + `rudel-apply' + * jupiter/jupiter-insert.el (include rudel-operations): for + jupiter-insert-op + (jupiter-insert): derived from jupiter-insert-op + (jupiter-insert::jupiter-apply): removed; inherited from + jupiter-insert-op + (jupiter-insert::slot-missing): removed; inherited from + jupiter-insert-op + * jupiter/jupiter-delete.el (include rudel-operations): for + jupiter-delete-op + (jupiter-delete): derived from jupiter-delete-op + (jupiter-delete::jupiter-apply): removed; inherited from + jupiter-delete-op + (jupiter-delete::slot-missing): removed; inherited from + jupiter-delete-op + * jupiter/jupiter-compound.el (jupiter-compound::jupiter-apply): + removed; replaced by `rudel-apply' + (jupiter-compound::rudel-apply): new method; implements generic + `rudel-apply' + * rudel.el (include rudel-operations): everything is represented + in terms of operations + (include rudel-operators): operations apply changes to objects + through operators + (rudel-document::rudel-insert): new method; performs insert + operation + (rudel-document::rudel-delete): new method; performs delete + operation + (rudel-document::rudel-local-insert): removed; document does not + deal with aspects other than the actual insert and delete + (rudel-document::rudel-local-delete): removed; document does not + deal with aspects other than the actual insert and delete + (rudel-document::rudel-remote-insert): removed; document does not + deal with aspects other than the actual insert and delete + (rudel-document::rudel-remote-delete): removed; document does not + deal with aspects other than the actual insert and delete + (rudel-document::rudel-local-operation): new method; apply + operation using overlay and connection operators + (rudel-document::rudel-remote-operation): new method; apply + operation using document and overlay operators + (rudel-handle-buffer-change): realize buffer changes using + operations + * rudel-operators.el (new file): collections of operations on + Rudel data types + * rudel-operations.el (new file): operation classes + +2009-02-01 Jan Moringen + + * jupiter/jupiter-insert.el (jupiter-insert::jupiter-transform): + handle jupiter-nop + + * jupiter/jupiter-delete.el (jupiter-delete::jupiter-transform): + in inner cond, use matching pattern but empty body for + no-operation cases; in outer cond, handle jupiter-nop + + * jupiter/jupiter-compound.el (jupiter-compound): now derived from + jupiter-operation; should have been right from the start + +2009-01-31 Jan Moringen + + * rudel.el (rudel-default-username): Default name used when + prompting for user name; required by rudel-interactive + + * rudel-interactive.el (rudel-read-backend): fixed typo + +2009-01-30 Jan Moringen + + * jupiter/jupiter-insert.el (jupiter-insert::jupiter-transform): + fixed two offset calculations + * jupiter/jupiter-delete.el (jupiter-delete::jupiter-transform): + fixed offset calculation + + * rudel.el (rudel-backend::rudel-ask-connect-info): changed from + method to generic + (rudel-backend::rudel-connect): changed from method to generic + (rudel-backend::rudel-ask-host-info): changed from method to + generic + (rudel-backend::rudel-host): changed from method to generic + (rudel-backend::rudel-make-document): changed from method to + generic + (rudel-session::rudel-disconnect): changed from method to generic + (rudel-session::rudel-change-color-): changed from method to + generic + (rudel-session::rudel-publish): changed from method to generic + (rudel-session::rudel-subscribe-to): changed from method to + generic + (rudel-session::rudel-unsubscribe-from): changed from method to + generic + (rudel-session::rudel-local-insert): changed from method to + generic + (rudel-session::rudel-local-delete): changed from method to + generic + (rudel-session::rudel-remote-insert): changed from method to + generic + (rudel-session::rudel-remote-delete): changed from method to + generic + +2009-01-28 Jan Moringen + + * rudel-overlay.el (header): fixed version + (whole file): cosmetic changes + (rudel-author-overlay-p): added documentation string + (rudel-author-overlays): added documentation string + + * rudel-mode.el (rudel-minor-keymap): cosmetic changes + + * rudel-mode.el (rudel-minor-keymap): Separated session + participation and hosting items + + * obby/rudel-obby.el (rudel-obby-long-message-threshold): Added + documentation string + (rudel-obby-long-message-chunk-size): Added documentation string + (rudel-obby-backend::rudel-connect): Do not set process object; + this is done in the `initialize-instance' method of the base class + (rudel-obby-format-color): retrieve color components with + `color-values' + + * obby/rudel-obby-client.el + (rudel-obby-connection::rudel-remove-context): improved + documentation string + (rudel-obby-connection::rudel-publish): added a comment + (rudel-obby-connection::rudel-subscribe-to): added some comments; + cleaned up code + (rudel-obby-connection::rudel-unsubscribe-from): added a comment + (rudel-obby-connection::rudel-obby/net6_ping): added documentation + string + (rudel-obby-connection::rudel-obby/net6_encryption): added + documentation string + (rudel-obby-connection::rudel-obby/net6_login_failed): added + documentation string + (rudel-obby-connection::rudel-obby/net6_client_part): use `=' + instead of `eq' to compare client ids; fixed documentation string; + improved comments + (rudel-obby-connection::rudel-obby/obby_user_colour): use `=' + instead of `eq' to compare user ids + (rudel-obby-connection::rudel-obby/obby_document/sync_chunk): use + `=' instead of `eq' to compare user ids; use accessor + `user-id-numeric' + + * obby/rudel-obby-util.el (rudel-obby-dispatch): new functions; + dispatches to class methods based on message name; handles errors + properly + * obby/rudel-obby-server.el (rudel-obby-client::rudel-message): + use `rudel-obby-dispatch' to dispatch message + (rudel-obby-client::rudel-obby/obby_document): use + `rudel-obby-dispatch' to dispatch message + (rudel-obby-client::rudel-obby/obby_document/record): use + `rudel-obby-dispatch' to dispatch message + * obby/rudel-obby-client.el + (rudel-obby-connection::rudel-message): use `rudel-obby-dispatch' + to dispatch message; moved to a different location + (rudel-obby-connection::rudel-obby/obby_document): use + `rudel-obby-dispatch' to dispatch message + (rudel-obby-connection::rudel-obby/obby_document/record): use + `rudel-obby-dispatch' to dispatch message + + * obby/rudel-obby-util.el (generic rudel-message): made the method + a generic function and updated the documentation string + +2009-01-26 Jan Moringen + + * obby/rudel-obby.el (rudel-obby-document::revision): removed; The + slot is no longer needed + * obby/rudel-obby-server.el (require jupiter): uses jupiter + algorithm + (rudel-obby-client::rudel-obby/obby_document_create): add a + jupiter context for the document + (rudel-obby-client::rudel-obby/obby_document/subscribe): add a + jupiter context for the document + (rudel-obby-client::rudel-obby/obby_document/unsubscribe): remove + the jupiter context associated to the document + (rudel-obby-client::rudel-obby/obby_document/record/ins): + transformed the operation before applying it to the buffer; use + the respective jupiter contexts of the receivers when sending the + operation + (rudel-obby-client::rudel-obby/obby_document/record/del): + transformed the operation before applying it to the buffer; use + the respective jupiter contexts of the receivers when sending the + operation + (rudel-obby-server::contexts): new slot; stores jupiter contexts + for pairs of clients and documents + (rudel-obby-server::initialize-instance): new method; store an + empty hash-table in the `contexts' slot + (rudel-obby-server::rudel-find-context): find the jupiter context + for a pair of a client and a document + (rudel-obby-server::rudel-add-context): add a jupiter context for + a pair of a client and a document + (rudel-obby-server::rudel-remove-context): remove the jupiter + context for a pair of a client and a document + (rudel-obby-context-key): return a list of client id and document + id + * obby/rudel-obby-client.el (require jupiter): uses jupiter + algorithm + (rudel-obby-connection::contexts): new slot; stores jupiter + contexts for documents + (rudel-obby-connection::initialize-instance): new method; store an + empty hash-table in the `contexts' slot + (rudel-obby-connection::rudel-find-context): new method; return + the jupiter context for a document + (rudel-obby-connection::rudel-add-context): new method; add a + jupiter context for a document + (rudel-obby-connection::rudel-remove-context): new method; remove + the jupiter context for a document + (rudel-obby-connection::rudel-publish): add a jupiter context for + the new document + (rudel-obby-connection::rudel-subscribe-to): add a jupiter context + for the new document + (rudel-obby-connection::rudel-unsubscribe-from): remove the + jupiter context associated to the document + (rudel-obby-connection::rudel-local-insert): use revision + information from the jupiter context instead of the document; + supply the operation to the jupiter context + (rudel-obby-connection::rudel-local-delete): use revision + information from the jupiter context instead of the document; + supply the operation to the jupiter context + (rudel-obby-connection::rudel-obby/obby_document/record/ins): + transform the operation using the jupiter context instead of using + it unmodified + (rudel-obby-connection::rudel-obby/obby_document/record/del): + transform the operation using the jupiter context instead of using + it unmodified + +2009-01-22 Jan Moringen + + * obby/rudel-obby-client.el (rudel-obby-connection): removed + redundant slot `socket' (inherited from base class) + +2009-01-21 Jan Moringen + + * rudel-interactive.el (rudel-read-user): added comments + (rudel-allocate-buffer-clear-existing): added documentation string + (rudel-allocate-buffer-make-unique): added documentation string + +2009-01-19 Jan Moringen + + * rudel.el, rudel-util.el, rudel-telepathy.el, rudel-speedbar.el, + rudel-overlay.el, rudel-mode.el, jupiter/jupiter.el, + jupiter/jupiter-operation.el, jupiter/jupiter-nop.el, + jupiter/jupiter-insert.el, jupiter/jupiter-delete.el, + jupiter/jupiter-compound.el, obby/rudel-obby.el, + obby/rudel-obby-util.el, obby/rudel-obby-server.el, + obby/rudel-obby-client.el (header): changed email address + -> + + + * rudel-interactive.el (header): added keywords to file header + comment + + * jupiter/jupiter.el (new file): core Jupiter algorithm + * jupiter/jupiter-operation.el (new file): base class for Jupiter + operations + * jupiter/jupiter-insert.el (new file): insert operation for + Jupiter algorithm + * jupiter/jupiter-delete.el (new file): delete operation for + Jupiter algorithm + * jupiter/jupiter-nop.el (new file): no-operation for Jupiter + algorithm + * jupiter/jupiter-compound.el (new file): compound operation for + Jupiter algorithm + +2009-01-11 Jan Moringen + + * obby/rudel-obby.el (rudel-obby-user::client-id): added rationale + for type (or null integer) + * obby/rudel-obby-client.el + (rudel-obby-connection::rudel-obby/net6_client_join): added + documentation string; cosmetic changes + (rudel-obby-connection::rudel-obby/net6_client_part): use accessor + `rudel-client-id' when searching for the user object; set + client-id to nil in the user object; added documentation string + (rudel-obby-connection::rudel-obby/obby_sync_usertable_user): + store parsed user-id and color in temporaries + (rudel-obby-connection::rudel-obby/obby_user_colour):store parsed + color in a temporary; use accessor `rudel-id' when finding the + user object + + * obby/rudel-obby-client.el + (rudel-obby-connection::rudel-change-color-): new function; + implements changing the color + + * obby/rudel-obby-util.el + (rudel-obby-socket-owner::rudel-receive): improved documentation + string + +2009-01-05 Jan Moringen + + * INSTALL (REQUIREMENTS): proper list of requirements and sources + from which they can be obtained + (INSTALLING): initial version of installation instructions + (COMPLETING): some notes about compiling + * README (INTRODUCTION): short introduction + (GETTING STARTED): some notes about enabling Rudel, joining and + hosting sessions + +2009-01-04 Jan Moringen + + * obby/rudel-obby.el (rudel-obby-long-message-threshold): new + variable; threshold for message size, above which messages are + sent in multiple chunks + (rudel-obby-long-message-chunk-size): Chunk size used, when + chunking long messages. + (rudel-obby-user::client-id): allow value nil; added accessor; + added documentation string + (rudel-obby-send): new function; handles low-level aspects of + sending obby protocol messages + * obby/rudel-obby-util.el: new file; contains helper + functionality, mainly the class `rudel-obby-socket-owner', which + handles sending and receiving message + * obby/rudel-obby-server.el (includes): replaced rudel-obby with + rudel-obby-util, since it contains `rudel-obby-socket-owner' + (class rudel-obby-client): added base class + `rudel-obby-socket-owner' + (rudel-obby-client::rudel-receive): deleted, the functionality is + provided by the base class `rudel-obby-socket-owner' + (rudel-obby-client::rudel-send): deleted, the functionality is + provided by the base class `rudel-obby-socket-owner' + (rudel-obby-client::rudel-message): new method; called by base + class when a message is received; dispatches to appropriate + handler method + (rudel-obby-client::rudel-obby/obby_user_colour): minor change in + documentation string + * obby/rudel-obby-client.el (includes): replaced rudel-obby with + rudel-obby-util, since it contains `rudel-obby-socket-owner' + (class rudel-obby-connection): added base class + `rudel-obby-socket-owner' + (rudel-obby-connection::rudel-disconnect): just call next method; + it does what this method formerly did + (rudel-obby-connection::rudel-close): new method; end the session, + when the connection is closed + (rudel-obby-connection::rudel-receive): deleted, the functionality + is provided by the base class `rudel-obby-socket-owner' + (rudel-obby-connection::rudel-send): deleted, the functionality is + provided by the base class `rudel-obby-socket-owner' + (rudel-obby-connection::rudel-message): new method; called by + base class when a message is received; dispatches to appropriate + handler method + + * rudel.el (rudel-document::rudel-detach-from-buffer): do nothing, + if the document is not attached to any buffer + + * obby/rudel-obby.el (rudel-obby-user): added missing accessor + `rudel-connected' + + * obby/rudel-obby-server.el + (rudel-obby-client::rudel-obby/net6_client_login): transmit number + of synchronization items; transmit list of disconnected users + + * obby/rudel-obby-server.el + (rudel-obby-client::rudel-obby/obby_document/record/ins): + broadcast only to clients, which are subscribed to the document; + send user id of author instead of client id + (rudel-obby-client::rudel-obby/obby_document/record/del): + broadcast only to clients, which are subscribed to the document; + send user id of author instead of client id + (rudel-obby-client::rudel-subscribed-clients-not-self): new + method; return a list of clients subscribed to a document + excluding the client itself. + + * obby/rudel-obby-server.el (rudel-obby-server::next-client-id): + first id should be 1, not 0; fixed initform accordingly + (rudel-obby-server::next-user-id): + first id should be 1, not 0; fixed initform accordingly + + * rudel.el (rudel-document::rudel-chunks): fixed void variable + `chunks-' -> `augmented-chunks' + + * obby/rudel-obby-server.el + (rudel-obby-client::rudel-obby/obby_document/subscribe): send + individual buffer chunks with their respective authors instead of + one large string without author information + * rudel.el (rudel-document::rudel-chunks): new method; return a + list of buffer position ranges and the respective authors, that + wrote the text + +2009-01-03 Jan Moringen + + * obby/rudel-obby.el (rudel-obby-backend::rudel-host): cleanup + * obby/rudel-obby-client.el + (rudel-obby-connection::rudel-local-insert): accept arguments + `position' and `data' instead of `from', `to' and `what'; since + position is zero-based, transmit it literally + (rudel-obby-connection::rudel-local-delete): instead of `from' and + `to' accept argument `position'; since position is + zero-based. transmit it literally + (rudel-obby-connection::rudel-obby/obby_document/record): + identified remaining arguments; dispatch actions to appropriate + methods; identify methods by interning their symbols + (rudel-obby-connection::rudel-obby/obby_document/record/ins): new + method; handle remote insert actions + (rudel-obby-connection::rudel-obby/obby_document/record/del): new + method; handle remote delete actions + * rudel.el (includes): include rudel-overlay + (rudel-document::rudel-detach-from-buffer): improved readability + (rudel-document::rudel-local-insert): instead of redundant + arguments `from', `to' and `what' accept only `position' and + `data'; update overlays + (rudel-document::rudel-local-delete): instead of redundant + arguments `from', `to' and `length' accept only `position' and + `length'; update overlays + (rudel-document::rudel-remote-insert): renamed arguments `from' -> + `position', `what' -> `data'; update overlays + (rudel-document::rudel-remote-delete): replaced arguments `from' + and `to' by `position'; update overlays + (rudel-handle-buffer-change): call rudel-local-{insert, delete} + with changed arguments + +2009-01-01 Jan Moringen + + * rudel.el (rudel-session::rudel-unsubscribed-documents): new + method; returns documents, to which the user associated with the + session is not yet subscribed + (rudel-subscribe): when called interactively, use + `rudel-unsubscribed-documents' to offer only unsubscribed + documents in completing read + + * rudel-interactive.el (rudel-read-user-name): new function; read + a user name; could be used to enforce certain constraints on the + name + (rudel-read-user-color): new function; read a user color; could be + used to enforce certain constraints on the color + + * obby/rudel-obby.el (rudel-obby-backend::rudel-ask-host-info): + new method; ask user for port number + (rudel-obby-backend::rudel-host): new method; require obby server + component, make the network process and construct the server + * obby/rudel-obby-server.el (new file): initial revision of obby + server for rudel + * obby/rudel-obby-client.el (header section): added keyword and + x-rcs + (rudel-obby-connection::rudel-publish): new method; send document + to server + (rudel-obby-connection::rudel-unsubscribe-from): send unsubscribe + notification to server + (rudel-obby-connection::rudel-local-insert): cleanup + (rudel-obby-connection::rudel-local-delete): new method; send + delete record to server and increase local revision + (rudel-obby-connection::rudel-obby/obby_document/sync_chunk): + improved user locating code; do not complain, when the user is not + found + (rudel-obby-connection::rudel-obby/obby_document/record): removed + useless debug message + * rudel.el (class rudel-session): update documentation string + (class rudel-server-session): new class; base class for server + sessions + (rudel-choose-backend): fixed void-variable when called + interactively + (rudel-host-session): provided initial implementation, which uses + the selected backend to create a server + (rudel-subscribe): call `set-window-buffer' instead of + `show-buffer' + + * obby/rudel-obby.el (header section): fixed history + (rudel-obby-version): new constant; holds version of the obby + backend + (rudel-obby-protocol-version): new constant; holds the obby + protocol version understood by the backend + (rudel-obby-document::rudel-both-ids): new method; useful when + locating documents by means of owner and document id + + * rudel-mode.el (header section): added keywords + + * rudel-interactive.el (header section): added file comment + +2008-12-30 Jan Moringen + + * rudel.el (class rudel-session): converted to base class for + other session classes; removed slots `connection' and `self' which + are specific for client sessions + (rudel-session::rudel-end): empty now; derived classes do the work + (rudel-session::rudel-add-user): use `object-add-to-list' + (rudel-session::rudel-remove-user): use `object-remove-from-list' + (rudel-session::rudel-add-document): use `object-add-to-list' + (rudel-session::rudel-remove-document): use + `object-remove-from-list' + (class rudel-client-session): derived from `rudel-session'; + additional slots `connection' and `self' + (rudel-client-session::rudel-end): detach buffers from documents + and call `rudel-disconnect' on connection + (class rudel-connection): documentation string + (rudel-connection::rudel-disconnect): remove hook + `after-change-functions' only locally + (rudel-join-session): construct a proper session name; store + backend object in the session; some comments + + * obby/rudel-obby.el (rudel-obby-document): cleanup; improved + documentation strings + + * rudel-overlay.el (new file): functions for managing overlays, + which indicate the authors of contributions in collaborative + buffers + + * rudel.el (rudel-allocate-buffer-function): customization option + for buffer allocation function + (rudel-subscribe): call buffer allocation function instead of just + using the provided name + * rudel-interactive.el (rudel-allocate-buffer-clear-existing): new + function; in case of a conflict, allocate buffer for subscription + by clearing the existing buffer + (rudel-allocate-buffer-make-unique): new function; in case of a + conflict, allocate buffer for subscription by producing a unique + name + + * rudel.el (customization): added customization group definition + for `rudel' + + * obby/rudel-obby.el (includes): require `rudel-util' instead of + `rudel' + (rudel-connect): attach connection to socket object + (rudel-obby-document): removed slot `subscribed' as it is now + contained in the base class `rudel-document' + (rudel-obby-escape-string): call `rudel-obby-replace-in-string' + instead of `obby-replace-in-string' + (rudel-obby-unescape-string): call `rudel-obby-replace-in-string' + instead of `obby-replace-in-string' + * obby/rudel-obby-client.el + (rudel-obby-connection::rudel-state-change): required by + `rudel-sentinel-dispatch' + (rudel-obby-connection::rudel-subscribe-to): do not touch slot + `subscribed' + (rudel-obby-connection::rudel-obby/obby_sync_doclist_document): + retrieve subscribed users and add to `subscribed' slot + (rudel-obby-connection::rudel-obby/obby_document_create): add + document owner to `subscribed' slot + (rudel-obby-connection::rudel-obby/obby_document/subscribe): add + user to `subscribed' slot + (rudel-obby-connection::rudel-obby/obby_document/unsubscribe): + remove user from `subscribed' slot + * rudel.el (rudel-document): added slot `subscribed' which + contains a list of subscribed users + (rudel-subscribe): do not use `rudel-unsubscribed-documents'; + instead list all documents for now + (rudel-publish-buffer): add self to `subscribed' slot + + * rudel-util.el (rudel-state-change): cleanup; added comments + + * rudel-mode.el (rudel-minor-keymap): Fixed invalid menu + definition + + * obby/rudel-obby.el (whole file): moved class + `rudel-obby-connection' and related methods into file + `rudel-obby-client.el' + (rudel-obby-backend): added capability `track-subscriptions' + (rudel-obby-backend::rudel-connect): require `rudel-obby-client' + before constructing the connection object + * obby/rudel-obby-client.el (new file): moved class + `rudel-obby-connection' and related methods into this file + +2008-12-29 Jan Moringen + + * rudel.el (rudel-connection::rudel-change-color-): new method + handles color changes + (rudel-change-color): added basic implementation, which checks the + backend, asks the user for a new color and calls the connection + object + + * rudel-util.el (rudel-socket-owner::rudel-state-change): called + when the state of the connection changes + (rudel-socket-owner::rudel-close): called when the connection is + closed + (rudel-sentinel-dispatch): the argument is a message, not the + actual state, the state is retrieved with `process-state' + + * rudel-speedbar.el (whole file): cleanup; improved comments + + * rudel-mode.el (whole file): improved comments + (rudel-read-{backend, document}): moved to rudel-interactive.el + (rudel-minor-keymap): added key binding for `rudel-change-color'; + added `options' menu item + + * rudel-interactive.el (whole file): user interaction functions + used by all Rudel components + + * rudel-util.el (whole file): utility functions used by all Rudel + components + + * rudel.el (whole file): improved comments + (rudel-backend::make-document): new function create an appropriate + document object for the backend + (rudel-session::rudel-end): added documentation string + (rudel-session::rudel-add-user): added documentation string + (rudel-session::rudel-remove-user): added documentation string + (rudel-session::rudel-remove-document): new method; remove + document from session + (rudel-connection::rudel-publish): new function; called when a + buffer is published + (rudel-connection::rudel-unsubscribe-from): new function; called + when a subscription is canceled + (class rudel-user): added documentation strings + (class rudel-document): added documentation strings + (rudel-document::rudel-attach-to-buffer): add to + `after-change-functions' hook only for the buffer in question; + added some comments + (rudel-document::rudel-detach-from-buffer): cleanup + (rudel-document::rudel-remote-insert): added comments + (rudel-document::rudel-remote-delete): added comments + (rudel-handle-buffer-change): added comments + (rudel-choose-backend): added comments + (rudel-end-session): additional error check + (rudel-subscribe): call `rudel-unsubscribed-documents' when + completing document name; added comments + (rudel-unpublish-buffer): call `rudel-detach-from-buffer' and + `rudel-unsubscribe-from'; added comments + + * obby/rudel-obby.el (whole file): improved comments + (rudel-obby-backend::rudel-ask-connect-info): removed :override + tag; added comments + (rudel-obby-backend::rudel-connect): removed :override tag; use + `make-network-process' instead of `open-network-stream' and attach + filter and sentinel right away; removed some debug code + (rudel-obby-backend::rudel-disconnect): removed :override tag + (rudel-obby-backend::rudel-subscribe-to): removed :override tag + (rudel-obby-backend::rudel-local-insert): removed :override tag + (rudel-obby-backend::rudel-local-delete): removed :override tag + (rudel-obby-backend::rudel-make-document): new method; creates a + new rudel-obby-document object + (rudel-obby-backend::rudel-available-document-id): obtains an + unused document id, which can be assigned to a new document + (class rudel-obby-connection): removed useless `host' and `port' + slots + (rudel-obby-connection::rudel-receive): removed some debug code + (rudel-obby-connection::rudel-send): removed some debug code + (rudel-obby-connection::rudel-obby/net6_client_join): fixed syntax + error + (class rudel-obby-user): added accessors for slots `client-id' and + `user-id' + (rudel-obby-user::eieio-speedbar-description): removed :override + tag + (rudel-obby-user::eieio-speedbar-object-buttonname): removed + :override tag + (class rudel-obby-document): added accessors and documentation for + slot `id' + (rudel-obby-document::eieio-speedbar-object-buttonname): removed + :override tag + (rudel-obby-replace-in-string): new function; replace a set of + patterns in a string + (rudel-obby-escape-string): new function; replace obby control + characters with their escape sequences + (rudel-obby-unescape-string): new function; inverse of + `rudel-obby-escape-string' + (rudel-obby-parse-color): added documentation + (rudel-obby-format-color): added documentation + (rudel-obby-assemble-message): properly escape message components + (rudel-obby-parse-message): properly unescape message components + + * README (whole file): some initial notes + * INSTALL (whole file): some initial notes + +2008-12-02 Jan Moringen + + * obby (directory): new directory for files belonging to the obby + backend + * rudel-obby.el (whole file): moved to `obby' directory + * obby/rudel-obby.el (whole file): moved here from parent + directory + + * Changelog (whole file): renamed to `ChangeLog?' + * ChangeLog? (whole file): fixed name + + * INSTALL (whole file): added + + * rudel.el (whole file): fixed some comments, removed some test + code + (rudel-version): new variable; global Rudel version + (rudel-sessions): removed; we only allow one session for now + (rudel-session): cleaned up + (rudel-session::rudel-end): cleaned up; added some comments + (rudel-session::rudel-add-user): cosmetic changes + (rudel-session::rudel-remove-user): cosmetic changes + (rudel-session::rudel-find-user): cosmetic changes + (rudel-session::rudel-add-document): cosmetic changes + (rudel-session::rudel-find-document): cosmetic changes + (rudel-backend::rudel-connect): improved documentation string + (rudel-backend::rudel-ask-host-info): renamed from + `rudel-ask-listen-info' + (rudel-backend::rudel-host): renamed from `rudel-listen' + (rudel-document::rudel-attach-to-buffer): cosmetic changes + (rudel-document::rudel-remote-insert): cleaned up + (rudel-document::rudel-remote-delete): cleaned up + (rudel-load-backends): cosmetic changes + (rudel-choose-backend): fixed message display + (rudel-host-session): improved documentation string + (rudel-change-color): raise an error since this is not yet + implemented + (rudel-subscribe): added comments + (rudel-unpublish-buffer): raise an error if the buffer has not + been published + + * rudel.el (whole file): cleanup up some obsolete code diff --git a/emacs.d/lisp/rudel/.svn/text-base/INSTALL.svn-base b/emacs.d/lisp/rudel/.svn/text-base/INSTALL.svn-base new file mode 100644 index 0000000..2555d09 --- /dev/null +++ b/emacs.d/lisp/rudel/.svn/text-base/INSTALL.svn-base @@ -0,0 +1,58 @@ +* REQUIREMENTS + + Rudel is developed and tested only with GNU Emacs and therefore + unlikely to run on other Emacs variants like XEmacs. + + To use Rudel, the following software is required: + +** GNU Emacs 22 or above + Rudel should work with recent versions of GNU Emacs starting from + version 22. Older versions of GNU Emacs or XEmacs may or not work + but are not actively tested. + +** Collection of Emacs Development Environment Tools (CEDET) + Cedet contains the object system Eieio, which is used in Rudel's + object-oriented implementation. Cedet can be obtained from + http://cedet.sourceforge.net/ + + IMPORTANT: It is necessary to use at least the 1.0pre6 version of + CEDET since it fixes a serious problem in the object system Eieio. + + As of October 2009, Eieio is included in GNU Emacs. If you are + using a version built since then, you do not have to install it + yourself. + +** GnuTLS (optional) + Connections to Gobby servers require the gnutls-cli program. + +** Avahi (optional) + The Avahi daemon (http://avahi.org) is required for automatic + session discovery and advertising. + + A version of GNU Emacs with Zeroconf support (GNU Emacs 23 or + above) is required to talk to the Avahi daemon. + +* INSTALLING + + To install Rudel, download a released version or the current + development version from http://sourceforge.net/projects/rudel/ and + place the code in any directory you like. + + Once Eieio (see CEDET in the REQUIREMENTS section above) is + installed, add the following to your personal Emacs configuration: + + (load-file "/PATH/TO/RUDEL/rudel-loaddefs.el") + + This will set Rudel up to be loaded on demand when one of the + commands `rudel-join-session', `rudel-host-session' or + `global-rudel-minor-mode' is invoked. + +* COMPILING + + In order to achieve better performance, Emacs can byte-compile the + Rudel code. This can be done by opening rudel-compile.el in Emacs + and invoking M-x eval-buffer. + +Local variables: +mode: org +end: diff --git a/emacs.d/lisp/rudel/.svn/text-base/Project.ede.svn-base b/emacs.d/lisp/rudel/.svn/text-base/Project.ede.svn-base new file mode 100644 index 0000000..ff3a264 --- /dev/null +++ b/emacs.d/lisp/rudel/.svn/text-base/Project.ede.svn-base @@ -0,0 +1,24 @@ +;; Object rudel +;; EDE project file. +(ede-proj-project "rudel" + :name "rudel" + :version "0.3" + :file "Project.ede" + :targets (list + (ede-proj-target-elisp-autoloads "autoloads" + :name "autoloads" + :path "" + :autoload-file "rudel-loaddefs.el" + :autoload-dirs '("." "jupiter" "obby" "wave" "zeroconf") + ) + (ede-proj-target-elisp "compile" + :name "rudel" + :path "" + :source '("rudel.el" "rudel-util.el" "rudel-mode.el" "rudel-interactive.el" "rudel-overlay.el" "rudel-speedbar.el" "rudel-operators.el" "rudel-operations.el" "rudel-compat.el" "rudel-tls.el" "rudel-errors.el" "rudel-state-machine.el" "rudel-backend.el" "rudel-protocol.el" "rudel-session-initiation.el" "rudel-icons.el" "rudel-hooks.el" "rudel-transport.el" "rudel-chat.el") + ) + ) + :mailinglist "rudel-devel@lists.sourceforge.net" + :web-site-url "http://rudel.sourceforge.net/" + :web-site-directory "/scymtym,rudel@web.sourceforge.net:/home/groups/r/ru/rudel/htdocs" + :configuration-variables 'nil + ) diff --git a/emacs.d/lisp/rudel/.svn/text-base/README.svn-base b/emacs.d/lisp/rudel/.svn/text-base/README.svn-base new file mode 100644 index 0000000..aeacba7 --- /dev/null +++ b/emacs.d/lisp/rudel/.svn/text-base/README.svn-base @@ -0,0 +1,88 @@ +* INTRODUCTION + + Rudel is collaborative editing environment for GNU Emacs. Its + purpose is to share buffers with other users in order to edit the + contents of those buffers collaboratively. Rudel supports multiple + backends to enable communication with other collaborative editors + using different protocols, though currently Obby (for use with the + Gobby editor) is the only fully-functional one. + + Since Rudel is not an application, but an extension to Emacs, it is + not started and used like most applications (not even Emacs + applications like Gnus). Rudel mostly works in the background to + change the behavior of the set of Emacs buffers for which it has + been activated. + + The user interface consists of a set of key bindings, a menu entry + and some visual status indicators, which are added to the text and + mode line of buffers for which Rudel has been activated. + +* GETTING STARTED + + Assuming Rudel has already been installed and auto loading has been + set up, a global Rudel mode can be enabled as follows: + + : M-x global-rudel-minor-mode + + This will enabled Rudel's key bindings and menu entry. + +** JOINING A SESSION + + : M-x rudel-join-session [ C-c c j ] + + Depending on the installed Rudel backends, system environment and + configuration, a number of questions will be asked, followed by an + attempt to join session described by your answers. + + A typical example of the questions asked when joining a session may + look like this: + + Server: localhost RET + Port (default 6522): RET + Username: jan RET + Color: light sky blue RET + Use Encryption (y or n): n RET + Global Password: RET + User Password: RET + + IMPORTANT: For sessions using the obby backend (like in the example + above), the following restriction has to be taken into account: + + When the server is Rudel inside an Emacs process: + Encryption cannot be used currently in this case. Consequently + the answer to the `Use Encryption (y or n):' prompt above has to + be `n RET'. + + When the server is a Gobby process: + Gobby only supports encrypted connections. So the answer has to + be `y RET' is this case. + + It is possible to configure frequently used sessions using the + customization options `rudel-configured-sessions'. When one or more + sessions are configured, `rudel-join-session' will provide choices + like "my-configured-session", ... and "ask-protocol". Selecting + "ask-protocol" invokes the behavior described above. Selecting one + of the configured sessions connects to that session without asking + for all the data. + +** HOSTING A SESSION + + : M-x rudel-host-session [ C-c c h ] + + Note that the session starts out without any participating users + (This is sometimes referred to as being a dedicated server). If you + want to participate in the session you host, you have to join it as + described above. + +* KNOWN ISSUES + + + Publishing eshell buffers will cause your session to be + disconnected since eshell disables the hooks that Rudel uses to + catch changes to the buffer. As a workaround, you can use M-x + ansi-term or another terminal emulator. + +* LICENSE + + Rudel is licensed under the same terms as GNU Emacs. + +Local variables: +mode: org +end: diff --git a/emacs.d/lisp/rudel/.svn/text-base/TODO.svn-base b/emacs.d/lisp/rudel/.svn/text-base/TODO.svn-base new file mode 100644 index 0000000..e5e4bf4 --- /dev/null +++ b/emacs.d/lisp/rudel/.svn/text-base/TODO.svn-base @@ -0,0 +1,436 @@ +* Future +** NEW Handle messages spanning multiple frames + + Component :: beep-transport + + Type :: defect + + Reporter :: jan + + Assigned :: +** NEW Operation log can grow beyond all bounds (#37) + + Component :: obby-general + + Type :: defect + + Reporter :: jan + + Assigned :: + When no remote operations are received, the log of local operation + is not reset and therefore grows beyond all bounds. +** NEW Terminating sessions does not work (#47) + + Component :: rudel-general + + Type :: defect + + Reporter :: jan + + Assigned :: + There is a menu entry for terminating sessions which are hosted by + Rudel, but it does not do anything. +** NEW Rename document message is not understood (#7) + + Component :: obby-client + + Type :: defect + + Reporter :: jan + + Assigned :: +** NEW Rename document message is not understood (#8) + + Component :: obby-backend + + Type :: defect + + Reporter :: jan + + Assigned :: +** TODO Notification mechanism + + Component :: user-interface + + Type :: task + + Reporter :: jan + + Type :: task + + Assigned :: +** TODO SubEthaEdit client functionality + + Component :: subethaedit-backend + + Type :: task + + Reporter :: jan + + Assigned :: +** TODO Show cursor positions of other users (#5) + + Component :: rudel-user-interface + + Type :: enhancement + + Reporter :: jan + + Assigned :: +** TODO Some kind of server log buffer (#11) + + Component :: rudel-general + + Type :: enhancement + + Reporter :: jan + + Assigned :: + It would be nice to log server events. This could be done in a + separate buffer or using a dedicated mechanism like + rudel-notification. +** TODO Backends should be able to offer additional menu items (#14) + + Component :: rudel-general + + Type :: enhancement + + Reporter :: jan + + Assigned :: +** TODO Obby session can be protected by passwords (#15) + + Component :: obby-general + + Type :: enhancement + + Reporter :: jan + + Assigned :: +** TODO Obby users can protect their accounts with passwords (#16) + + Component :: obby-general + + Type :: enhancement + + Reporter :: jan + + Assigned :: + The Gobby implementation is in obby/inc/server_buffer.hpp:851 +** TODO Zeroconf session notification (#52) + + Component :: zeroconf + + Type :: task + + Reporter :: jan + + Assigned :: + Watch interesting Zeroconf services and use `rudel-notify` if new + services are discovered +** TODO State machine diagram (#59) + + Component :: obby-client + + Type :: task + + Reporter :: jan + + Assigned :: +** TODO State machine diagram (#60) + + Component :: obby-backend + + Type :: task + + Reporter :: jan + + Assigned :: +** TODO Send key presses as chat messages (#61) + + Component :: rudel-general + + Type :: task + + Reporter :: Jan + + Assigned :: + Sending key presses as chat messages could be really useful for + somebody something using rudel. +** STARTED BEEP transport + + Component :: beep-transport + + Type :: task + + Reporter :: jan + + Assigned :: jan +** STARTED Reference manual (#46) + + Component :: documentation + + Type :: task + + Reporter :: jan + + Assigned :: jan + In addition to the `README`, a proper reference manual would be + nice. At some point, complete info documentation may be + desirable. Docbook seems to be the best approach since we get (at + least): + + Pdf + + Html + + Info + + +* Milestone rudel-0.4 +** TODO Telepathy transport + + Component :: telepathy-backend + + Type :: task + + Reporter :: jan + + Assigned :: + +* Milestone rudel-0.3 +** TODO Multiple username/password attempts in one login attempt + + Component :: rudel-general + + Type :: task + + Reporter :: jan + + Assigned :: +** TODO Infinote client functionality + + Component :: infinote-backend + + Type :: task + + Reporter :: jan + + Assigned :: +** TODO Support for trees of documents + + Component :: rudel-general + + Type :: task + + Reporter :: jan + + Assigned :: +** NEW Get rid of error calls in the server (#58) + + Component :: obby-backend + + Type :: defect + + Reporter :: jan + + Assigned :: + It makes no sense to call `error` when something goes wrong in + server code that is called from the process filter. Instead, we + should try to recover. +** NEW Global mode line publish state mode does not work for all new buffers (#55) + + Component :: rudel-user-interface + + Type :: defect + + Reporter :: jan + + Assigned :: + `global-mode-line-publish-state-mode` is define using + `define-globalized-mode`. This seems to only enabled the associated + minor mode for buffers create by `find-file` and after major mode + changes. The minor mode is not activated for buffers create by + `create-buffer`. Since this is used when subscribing to documents, + this is a problem. +** NEW Handle net6_encryption_info messages (#57) + + Component :: obby-backend + + Type :: defect + + Reporter :: jan + + Assigned :: +** TODO Only read color hue, not complete colors (#53) + + Component :: rudel-user-interface + + Type :: enhancement + + Reporter :: jan + + Assigned :: + Taking control over saturation and value away from the user makes + it impossible to choose unreadable colors. + + +* Milestone rudel-0.2 +** DONE Use state pattern (#18) + + Component :: obby-backend + + Resolution :: fixed + + Type :: enhancement + + Priority :: minor + + Reporter :: jan +** FIXED Server buffers go out of sync when multi-byte characters are used (#56) + + Component :: obby-backend + + Resolution :: fixed + + Type :: defect + + Priority :: major + + Reporter :: jan +** FIXED Terminate connections properly when something goes wrong (#51) + + Component :: rudel-general + + Resolution :: fixed + + Type :: defect + + Priority :: major + + Reporter :: jan +** FIXED Removing documents does not work (#45) + + Component :: obby-backend + + Resolution :: fixed + + Type :: defect + + Priority :: major + + Reporter :: jan +** FIXED Search list of offline users when new users log in (#44) + + Component :: obby-backend + + Resolution :: fixed + + Type :: defect + + Priority :: major + + Reporter :: jan +** DONE Reference card (#2) + + Component :: documentation + + Resolution :: fixed + + Type :: enhancement + + Priority :: minor + + Reporter :: jan +** DONE ode-line indicator of buffer status (#6) + + Component :: rudel-user-interface + + Resolution :: fixed + + Type :: enhancement + + Priority :: minor + + Reporter :: jan +** FIXED Author overlay face may not exist (#54) + + Component :: rudel-user-interface + + Resolution :: fixed + + Type :: defect + + Priority :: major + + Reporter :: jan +** DONE Visualization of user status (#9) + + Component :: rudel-user-interface + + Resolution :: fixed + + Type :: enhancement + + Priority :: minor + + Reporter :: jan +** FIXED Update overlays when users change colors (#23) + + Component :: rudel-user-interface + + Resolution :: fixed + + Type :: defect + + Priority :: minor + + Reporter :: jan +** DONE Allow to toggle display of author overlays (#33) + + Component :: rudel-user-interface + + Resolution :: fixed + + Type :: enhancement + + Priority :: minor + + Reporter :: jan +** DONE Update file headers (#50) + + Component :: documentation + + Resolution :: fixed + + Type :: task + + Priority :: trivial + + Reporter :: jan +** DONE Proper Zeroconf support (#21) + + Component :: zeroconf + + Resolution :: fixed + + Type :: enhancement + + Priority :: minor + + Reporter :: jan +** DONE Add discovery component (#22) + + Component :: obby-general + + Resolution :: fixed + + Type :: enhancement + + Priority :: minor + + Reporter :: jan +** DONE Define initialize-instance with slots or &rest slots? (#49) + + Component :: rudel-general + + Resolution :: fixed + + Type :: task + + Priority :: major + + Reporter :: jan +** DONE Use oref to get object names (#24) + + Component :: rudel-general + + Resolution :: fixed + + Type :: enhancement + + Priority :: minor + + Reporter :: jan +** DONE Overlays should be removed when a buffer is detached from its document (#39) + + Component :: rudel-user-interface + + Resolution :: fixed + + Type :: enhancement + + Priority :: minor + + Reporter :: jan +** FIXED Unsafe use of (call-next-method) (#48) + + Component :: rudel-general + + Resolution :: fixed + + Type :: defect + + Priority :: major + + Reporter :: jan +** FIXED Handle `net6_login_failed' message (#10) + + Component :: obby-backend + + Resolution :: fixed + + Type :: defect + + Priority :: minor + + Reporter :: jan +** DONE Add debug hints to macros (#43) + + Component :: rudel-general + + Resolution :: fixed + + Type :: enhancement + + Priority :: minor + + Reporter :: jan +** DONE Use state pattern (#17) + + Component :: obby-backend + + Resolution :: fixed + + Type :: enhancement + + Priority :: minor + + Reporter :: jan +** DONE Use with-parsed-arguments (#40) + + Component :: obby-general + + Resolution :: fixed + + Type :: enhancement + + Priority :: minor + + Reporter :: jan + + +* Milestone rudel-0.1 +** FIXED User names and colors are not checked for conflicts (#12) + + Component :: obby-backend + + Resolution :: fixed + + Type :: defect + + Priority :: minor + + Reporter :: jan +** DONE Write some html for rudel.sourceforge.net (#27) + + Component :: www + + Resolution :: fixed + + Type :: task + + Priority :: major + + Reporter :: jan +** INVALID Repeated publishing leads to multiple document instances (#30) + + Component :: obby-backend + + Resolution :: invalid + + Type :: defect + + Priority :: minor + + Reporter :: jan +** FIXED Document suffixes are not handled properly (#42) + + Component :: obby-backend + + Resolution :: fixed + + Type :: defect + + Priority :: major + + Reporter :: jan +** DONE Fix license texts (#32) + + Component :: rudel-general + + Resolution :: fixed + + Type :: enhancement + + Priority :: trivial + + Reporter :: jan +** WONTFIX Overlays break on last character (#29) + + Component :: rudel-user-interface + + Resolution :: worksforme + + Type :: defect + + Priority :: minor + + Reporter :: jan +** FIXED Encodings are not handled in obby backend (#1) + + Component :: obby-general + + Resolution :: fixed + + Type :: defect + + Priority :: major + + Reporter :: jan +** FIXED Major mode changes break subscribed buffers (#19) + + Component :: rudel-general + + Resolution :: fixed + + Type :: defect + + Priority :: major + + Reporter :: jan +** FIXED Editing in overwrite mode breaks synchronization (#35) + + Component :: rudel-general + + Resolution :: fixed + + Type :: defect + + Priority :: major + + Reporter :: jan +** FIXED Yanking produces insertion and immediate deletion of the region (#36) + + Component :: rudel-general + + Resolution :: fixed + + Type :: defect + + Priority :: major + + Reporter :: jan +** FIXED Documents with identical names but distinct suffixes map to same buffer (#41) + + Component :: obby-backend + + Resolution :: fixed + + Type :: defect + + Priority :: major + + Reporter :: jan +** FIXED Killing a buffer does not detach it from its document (#38) + + Component :: rudel-general + + Resolution :: fixed + + Type :: defect + + Priority :: major + + Reporter :: jan +** INVALID Rudel client crashes Gobby (#25) + + Component :: obby-general + + Resolution :: invalid + + Type :: defect + + Priority :: major + + Reporter :: jan +** DONE Add screenshot of session with Gobby (#20) + + Component :: www + + Resolution :: fixed + + Type :: task + + Priority :: trivial + + Reporter :: jan +** DONE Replace 't with t (#34) + + Component :: rudel-general + + Resolution :: fixed + + Type :: enhancement + + Priority :: minor + + Reporter :: jan +** DONE Operations of type jupiter-compound cannot be applied to buffers + (#31) + + Component :: rudel-general + + Resolution :: fixed + + Type :: defect + + Priority :: major + + Reporter :: jan +** DONE Do not sync any chunks when buffer is empty (#28) + + Component :: obby-backend + + Resolution :: fixed + + Type :: enhancement + + Priority :: minor + + Reporter :: jan +** DONE Implement Jupiter algorithm (#13) + + Component :: obby-general + + Resolution :: fixed + + Type :: enhancement + + Priority :: major + + Reporter :: jan +** DONE Replace email address (#26) + + Component :: rudel-general + + Resolution :: fixed + + Type :: task + + Priority :: major + + Reporter :: jan +** FIXED Mark contributions using overlays (#4) + + Component :: rudel-user-interface + + Resolution :: fixed + + Type :: enhancement + + Priority :: major + + Reporter :: jan +** FIXED When a user leaves and joins a second user object is created (#3) + + Component :: obby-general + + Resolution :: fixed + + Type :: defect + + Priority :: major + + Reporter :: jan + +Local variables: +mode: org +end: diff --git a/emacs.d/lisp/rudel/.svn/text-base/rudel-backend.el.svn-base b/emacs.d/lisp/rudel/.svn/text-base/rudel-backend.el.svn-base new file mode 100644 index 0000000..79cfef6 --- /dev/null +++ b/emacs.d/lisp/rudel/.svn/text-base/rudel-backend.el.svn-base @@ -0,0 +1,305 @@ +;;; rudel-backend.el --- A generic backend management mechanism for Rudel +;; +;; Copyright (C) 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Rudel, backend, factory +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; This file contains a generic mechanism that handles registration, +;; query and instantiation of Rudel backends for any number of +;; functional categories. +;; +;; The class and collaboration design is as follows: for each +;; category, which is identified by a symbol, there is a factory +;; object (an instance of `rudel-backend-factory') that is responsible +;; for creating backend objects of the category. Examples of +;; categories are 'transport', 'protocol' and 'session-initiation'. +;; In addition to creating backend object, factories also allow +;; querying backends based on desired capabilities and load backend +;; implementations only when required. + + +;;; History: +;; +;; 0.1 - Initial revision + + +;;; Code: +;; + +(eval-when-compile + (require 'cl)) + +(require 'eieio) + + +;;; Class rudel-backend +;; + +(defclass rudel-backend () + ((version :initarg :version + :type list + :documentation + "A list of the form (MAJOR MINOR [MICRO +WHATEVER*]) describing the version of the backend.") + (capabilities :initarg :capabilities + :type list + :initform nil + :documentation + "A list of symbols, or lists whose car is a +symbol, that each describe one capability of the backend.")) + "Base class for backend classes." + :abstract t) + +(defmethod rudel-capable-of-p ((this rudel-backend) capability) + "Return t if the backend THIS is capable of CAPABILITY." + (with-slots (capabilities) this + (member capability capabilities))) + + +;;; Class rudel-backend-factory +;; + +(defclass rudel-backend-factory () + ((backends :initarg :backends + :type hash-table + :documentation + "Mapping of symbolic names to classes (prior to +instantiation) or objects (after instantiation) for all backends +known to the factory object.") + (factories :type hash-table + :allocation :class + :documentation + "Mapping of backend categories to responsible +factory objects.")) + "Factory class that holds an object for each known backend +category. Objects manage backend implementation for one backend +category each.") +(oset-default rudel-backend-factory factories + (make-hash-table :test #'eq)) + +(defmethod initialize-instance ((this rudel-backend-factory) &rest slots) + "Initialize slots of THIS with SLOTS." + (when (next-method-p) + (call-next-method)) + (oset this :backends (make-hash-table :test #'eq))) + +;;;###autoload +(defmethod rudel-get-factory :static ((this rudel-backend-factory) + category) + "Return the factory responsible for CATEGORY. +If there is no responsible factory, create one and return it." + (with-slots (factories) this + (or (gethash category factories) + (puthash category (rudel-backend-factory category) factories))) + ) + +;;;###autoload +(defmethod rudel-add-backend ((this rudel-backend-factory) + name class &optional replace) + "Add factory class CLASS with name NAME to THIS. +if REPLACE is non-nil, replace a registered implementation of the +same name." + (with-slots (backends) this + (when (or (not (gethash name backends)) + replace) + (puthash name class backends)))) + +(defmethod rudel-get-backend ((this rudel-backend-factory) name) + "Return backend object for name NAME or nil if there is none. +The returned backend is of the form (NAME . OBJECT). + +Backends are loaded, if necessary." + ;; Load all available backends + (rudel-load-backends this) + + ;; Find the backend and return it. + (with-slots (backends) this + (let ((backend (gethash name backends))) + (when backend + (cons name backend)))) + ) + +(defmethod rudel-all-backends ((this rudel-backend-factory)) + "Return a list of all backends registered with THIS. +Each list element is of the form (NAME . CLASS-OR-OBJECT)." + (let ((backend-list)) + (with-slots (backends) this + (maphash (lambda (name class) + (push (cons name class) backend-list)) + backends)) + backend-list) + ) + +(defmethod rudel-suitable-backends ((this rudel-backend-factory) predicate) + "Return a list of backends which satisfy PREDICATE. +Each list element is of the form (NAME . OBJECT). +Backends are loaded, if necessary." + ;; Load all available backends + (rudel-load-backends this) + + ;; Retrieve and return all backends, possibly filtering the list + ;; using PREDICATE. + (if predicate + (remove-if-not + (lambda (cell) + (and (object-p (cdr cell)) + (funcall predicate (cdr cell)))) + (rudel-all-backends this)) + (rudel-all-backends this)) + ) + +(defmethod rudel-load-backends ((this rudel-backend-factory)) + "Load backends in THIS factory if necessary. +Loading errors are not reported explicitly, but can be detected +by checking for backends that still are classes rather than +objects." + ;; Map lambda that loads unloaded backends over all backends. Store + ;; objects back after loading. + (with-slots (backends) this + (maphash + (lambda (name class) + (unless (object-p class) + (condition-case error + (puthash name (make-instance + class (symbol-name name)) backends) + (error (display-warning + '(rudel backend) + (format "Could not load backend `%s': %s" + name + (error-message-string error)) + :warning))))) + backends)) + ) + + +;;; High-level frontend functions +;; + +(defsubst rudel-backend-cons-p (cell) + "Check whether CELL is a cons of a backend name and object." + (and (consp cell) + (symbolp (car cell)) + (object-p (cdr cell)))) + +;;;###autoload +(defun rudel-backend-get (category name) + "A shortcut for getting backend NAME of category CATEGORY. +The returned backend is of the form (NAME . OBJECT)." + (rudel-get-backend (rudel-backend-get-factory category) name)) + +;;;###autoload +(defun rudel-backend-get-factory (category) + "A shortcut for getting the factory object for CATEGORY." + (rudel-get-factory rudel-backend-factory category)) + +(defun rudel-backend-suitable-backends (category predicate) + "Return backends from category CATEGORY that satisfy PREDICATE. +Each list element is of the form (NAME . OBJECT)." + (rudel-suitable-backends + (rudel-backend-get-factory category) + predicate)) + +(defun rudel-backend-choose (category &optional predicate) + "Choose a backend from CATEGORY satisfying PREDICATE automatically or by asking the user. +The returned backend is of the form (NAME . CLASS-OR-OBJECT)." + (let ((backends (rudel-backend-suitable-backends + category predicate))) + (unless backends + (error "No backends available")) + + (if (= (length backends) 1) + ;; If there is only one backend, we can choose that one right + ;; away displaying a message to avoid confusing the user. + (let ((backend (nth 0 backends))) + (message "Using backend `%s'" (symbol-name (car backend))) + (sit-for 0.5) + backend) + + ;; When we have more than one backend, we have to ask the user, + ;; which one she wants. + (require 'rudel-interactive) + (rudel-read-backend backends nil 'object))) + ) + + +;;; User interaction functions +;; + +(defun rudel-backend-dump (&optional load) + "Create display information about backends in a buffer. +If LOAD is non-nil, load all backends before display. This makes +available information available for the backends" + (interactive "p") + (save-excursion + ;; Setup a new buffer. + (set-buffer (get-buffer-create "*Rudel Backends*")) + (erase-buffer) + (set-window-buffer nil (current-buffer)) + (maphash + (lambda (category factory) + ;; Load backends if requested. + (unless (zerop load) + (rudel-load-backends factory)) + + ;; Insert header for this category. + (insert (propertize + (format "Category %s\n" category) + 'face 'bold)) + (insert (apply #'format + " %-20s %-6s %-7s %s\n" + (mapcar + (lambda (header) + (propertize header 'face 'italic)) + '("name" "loaded" "version" "capabilities")))) + + ;; Insert all backends provided by this factory. + (dolist (backend (rudel-all-backends factory)) + (insert (format " %-20s %-6s %-7s (%s)\n" + (propertize + (symbol-name (car backend)) + 'face 'font-lock-type-face) + (propertize + (prin1-to-string (object-p (cdr backend))) + 'face 'font-lock-variable-name-face) + (propertize + (if (object-p (cdr backend)) + (mapconcat #'prin1-to-string + (oref (cdr backend) :version) + ".") + "?") + 'face 'font-lock-constant-face) + (propertize + (if (object-p (cdr backend)) + (mapconcat #'prin1-to-string + (oref (cdr backend) :capabilities) + " ") + "?") + 'face 'font-lock-constant-face)))) + + ;; One empty line between backend categories. + (insert "\n")) + (oref rudel-backend-factory factories))) + ) + +(provide 'rudel-backend) +;;; rudel-backend.el ends here diff --git a/emacs.d/lisp/rudel/.svn/text-base/rudel-chat.el.svn-base b/emacs.d/lisp/rudel/.svn/text-base/rudel-chat.el.svn-base new file mode 100644 index 0000000..c7e992f --- /dev/null +++ b/emacs.d/lisp/rudel/.svn/text-base/rudel-chat.el.svn-base @@ -0,0 +1,103 @@ +;;; rudel-chat.el --- Handling of chat messages +;; +;; Copyright (C) 2008, 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Rudel, chat, message +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; This file contains some functions that deal with incoming chat +;; messages. Backends that support receiving chat message should +;; dispatch them using `rudel-chat-dispatch-message'. Chat messages +;; will be processed in a customizable way from there. + + +;;; History: +;; +;; 0.1 - Initial revision. + + +;;; Code: +;; + + +;;; Customization +;; + +(defcustom rudel-chat-handler-function #'rudel-chat-handle-buffer + "A function that is called when chat messages arrive." + :group 'rudel + :type '(choice (const :tag "Display messages in the echo area" + rudel-chat-handle-message) + (const :tag "Log messages into a buffer" + rudel-chat-handle-buffer) + (function :tag "Other function")) + ) + + +;;; Variables and constants +;; + +(defconst rudel-chat-buffer-name "*rudel-chat-log*" + "Name of the buffer into which received chat message should be +inserted.") + + +;;; Interface functions +;; + +(defun rudel-chat-dispatch-message (sender message) + "Dispatch SENDER and MESSAGE to customizable handler function." + (funcall rudel-chat-handler-function sender message)) + + +;;; Handler functions +;; + +(defun rudel-chat-handle-message (sender text) + "Display SENDER and MESSAGE in the echo area." + (message "%s says: %s" + (rudel-chat-format-sender sender) + text)) + +(defun rudel-chat-handle-buffer (sender text) + "Insert SENDER and MESSAGE in a buffer." + (let ((buffer (or (get-buffer rudel-chat-buffer-name) + (pop-to-buffer rudel-chat-buffer-name)))) + (with-current-buffer buffer + (goto-char (point-min)) + (insert (format "%s: %s\n" + (rudel-chat-format-sender sender) + text)))) + ) + + +;;; Miscellaneous functions +;; + +(defun rudel-chat-format-sender (user) + "Format USER handling nil values." + (if user + (object-name-string user) + "")) + +(provide 'rudel-chat) +;;; rudel-chat.el ends here diff --git a/emacs.d/lisp/rudel/.svn/text-base/rudel-compat.el.svn-base b/emacs.d/lisp/rudel/.svn/text-base/rudel-compat.el.svn-base new file mode 100644 index 0000000..630496c --- /dev/null +++ b/emacs.d/lisp/rudel/.svn/text-base/rudel-compat.el.svn-base @@ -0,0 +1,158 @@ +;;; rudel-compat.el --- Compatibility code for Rudel +;; +;; Copyright (C) 2009 Jan Moringen +;; Copyright (C) 2009 Phil Hagelberg +;; +;; Author: Jan Moringen +;; Phil Hagelberg +;; Keywords: rudel, compatibility +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; This file contains compatibility code required to make Rudel work +;; with different versions of Emacs. + + +;;; History: +;; +;; 0.1 - Initial revision + + +;;; Code: +;; + +(unless (fboundp 'read-color) + (defun read-color (prompt &rest ignored) + "Poor man's read color without completion. +You have to take care to only enter valid color names." + (read-string prompt))) + + +;;; Spinner Progress Reporter +;; + +(unless (functionp 'progress-reporter-spin) + (defvar progress-spinner-values ["-" "\\" "|" "/"]) + + (defsubst progress-reporter-update (reporter &optional value) + "Report progress of an operation in the echo area. + +The first parameter, REPORTER, should be the result of a call to +`make-progress-reporter'. For reporters for which the max value +is known, the second argument determines the actual progress of +operation; it must be between MIN-VALUE and MAX-VALUE as passed +to `make-progress-reporter'. + +However, if the change since last echo area update is too small +or not enough time has passed, then do nothing (see +`make-progress-reporter' for details). + +In this case, this function is very inexpensive, you need not +care how often you call it." + (if (progress-reporter-spinner-p reporter) + (progress-reporter-spin reporter) + (when (>= value (car reporter)) + (progress-reporter-do-update reporter value)))) + + (defun make-progress-reporter (message &optional min-value max-value + current-value min-change min-time) + "Return progress reporter object to be used with `progress-reporter-update'. + +MESSAGE is shown in the echo area. When at least 1% of operation +is complete, the exact percentage will be appended to the +MESSAGE. When you call `progress-reporter-done', word \"done\" +is printed after the MESSAGE. You can change MESSAGE of an +existing progress reporter with `progress-reporter-force-update'. + +If provided, MIN-VALUE and MAX-VALUE designate starting (0% +complete) and final (100% complete) states of operation. The +latter should be larger; if this is not the case, then simply +negate all values. Optional CURRENT-VALUE specifies the progress +by the moment you call this function. You should omit it or set +it to nil in most cases since it defaults to MIN-VALUE. + +Optional MIN-CHANGE determines the minimal change in percents to +report (default is 1%.) Optional MIN-TIME specifies the minimal +time before echo area updates (default is 0.2 seconds.) If +`float-time' function is not present, then time is not tracked +at all. If OS is not capable of measuring fractions of seconds, +then this parameter is effectively rounded up. + +If MIN-VALUE and MAX-VALUE are unknown, they may be omitted to +return a \"pulsing\" progress reporter." + (unless min-time + (setq min-time 0.2)) + (let ((reporter + (cons min-value ;; Force a call to `message' now + (vector (if (and (fboundp 'float-time) + (>= min-time 0.02)) + (float-time) nil) + (or min-value 0) + max-value + message + (if min-change (max (min min-change 50) 1) 1) + min-time)))) + (progress-reporter-update reporter (or current-value min-value)) + reporter)) + + (defun progress-reporter-force-update (reporter &optional value new-message) + "Report progress of an operation in the echo area unconditionally. + +First two parameters are the same as for +`progress-reporter-update'. Optional NEW-MESSAGE allows you to +change the displayed message." + (let ((parameters (cdr reporter))) + (when new-message + (aset parameters 3 new-message)) + (when (aref parameters 0) + (aset parameters 0 (float-time))) + (if (progress-reporter-spinner-p reporter) + (progress-reporter-spin reporter) + (progress-reporter-do-update reporter value)))) + + (defun progress-reporter-spinner-p (reporter) + "Return t if REPORTER has an unknown max value." + (null (aref (cdr reporter) 2))) + + (defun progress-reporter-spin (reporter) + "Advance indicator of spinning REPORTER." + (let* ((parameters (cdr reporter)) + (index (+ (aref parameters 1) 1))) + (aset parameters 1 index) + (let ((message-log-max nil)) ; No logging + (message "%s %s" + (aref progress-spinner-values (mod index 4)) + (aref parameters 3)))))) + +(unless (functionp 'string-match-p) + (defsubst string-match-p (regexp string &optional start) + "Same as `string-match' except this function does not change the match data" + (let ((inhibit-changing-match-data t)) + (string-match regexp string start)))) + +(defun rudel-get-coding-system (name) + (if (functionp 'coding-system-from-name) + (coding-system-from-name name) + ;; May need to try a little harder here for Emacs 22 depending on + ;; what kind of encoding names are given us. + (intern name))) + +(provide 'rudel-compat) +;;; rudel-compat.el ends here diff --git a/emacs.d/lisp/rudel/.svn/text-base/rudel-compile.el.svn-base b/emacs.d/lisp/rudel/.svn/text-base/rudel-compile.el.svn-base new file mode 100644 index 0000000..aeba765 --- /dev/null +++ b/emacs.d/lisp/rudel/.svn/text-base/rudel-compile.el.svn-base @@ -0,0 +1,46 @@ +;;; rudel-compile.el --- Byte-compile Rudel +;; +;; Copyright (C) 2009 Phil Hagelberg +;; +;; Author: Phil Hagelberg +;; Keywords: Rudel, compile +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; Press M-x eval-buffer to byte-compile Rudel. + + +;;; History: +;; +;; 0.1 - Initial revision + + +;;; Code: +;; + +(let ((rudel-dir (file-name-directory + (or (buffer-file-name) load-file-name)))) + ;; Adjust load path for compilation. + (dolist (dir '("." "jupiter" "obby" "zeroconf")) + (let ((d (concat rudel-dir "/" dir))) + (add-to-list 'load-path d))) + + ;; Byte compile everything. + (byte-recompile-directory rudel-dir 0)) diff --git a/emacs.d/lisp/rudel/.svn/text-base/rudel-debug.el.svn-base b/emacs.d/lisp/rudel/.svn/text-base/rudel-debug.el.svn-base new file mode 100644 index 0000000..747bf68 --- /dev/null +++ b/emacs.d/lisp/rudel/.svn/text-base/rudel-debug.el.svn-base @@ -0,0 +1,215 @@ +;;; rudel-debug.el --- Debugging functions for Rudel +;; +;; Copyright (C) 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Rudel, debugging +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 rudel. If not, see . + + +;;; Commentary: +;; +;; Debugging functions for Rudel. + + +;;; History: +;; +;; 0.1 - Initial revision. + + +;;; Code: +;; + +(require 'data-debug) +(require 'eieio-datadebug) + +(require 'rudel-util) + + +;;; Customization +;; + +(defgroup rudel-debug nil + "Customization options related to Rudel's debugging functions." + :group 'rudel) + +(defface rudel-debug-sent-data-face + '((default (:background "orange"))) + "Face used for sent data." + :group 'rudel-debug) + +(defface rudel-debug-received-data-face + '((default (:background "light sky blue"))) + "Face used for received (but not yet processed) data." + :group 'rudel-debug) + +(defface rudel-debug-received-processed-data-face + '((default (:background "DeepSkyBlue1"))) + "Face used for received data after processing." + :group 'rudel-debug) + +(defface rudel-debug-state-face + '((default (:background "light gray"))) + "Face used when indicating state changes." + :group 'rudel-debug) + +(defface rudel-debug-special-face + '((default (:background "light sea green"))) + "Face used for additional information." + :group 'rudel-debug) + +(defvar rudel-debug-tag-faces + '((:sent . (rudel-debug-sent-data-face "< ")) + (:received . (rudel-debug-received-data-face "> ")) + (:received-processed . (rudel-debug-received-processed-data-face ">> ")) + (:state . (rudel-debug-state-face "| ")) + (:special . (rudel-debug-special-face "; "))) + "Associate tag to faces and prefixes.") + + +;;; Data debug functions +;; + +(defun rudel-adebug-session () + "Analyze current session in data debug buffer." + (interactive) + + ;; Make sure we have a session. + (unless rudel-current-session + (error "No active Rudel session")) + + (with-current-buffer (data-debug-new-buffer "RUDEL-SESSION") + (data-debug-insert-thing rudel-current-session "# " ""))) + +(defun rudel-adebug-server (server) + "Analyze server in data debug buffer." + (interactive) + + (with-current-buffer (data-debug-new-buffer "RUDEL-SERVER") + (data-debug-insert-thing server "# " ""))) + + +;;; Advice stuff +;; + +(defadvice rudel-join-session (after rudel-debug last activate) + "Run data-debug inspection on newly created session objects." + (require 'rudel-debug) + (rudel-adebug-session)) + +(defadvice rudel-host-session (after rudel-debug last activate) + "Run data-debug inspection on newly created server objects." + (require 'rudel-debug) + (rudel-adebug-server ad-return-value)) + + +;;; Network functions +;; + +(defun rudel-suspend-session-socket () + "Suspend the socket associated to the current session." + (interactive) + + ;; Make sure we have a session. + (unless rudel-current-session + (error "No active Rudel session")) + + (with-slots (connection) rudel-current-session + (with-slots (socket) connection + (stop-process socket)))) + +(defun rudel-resume-session-socket () + "Resume the socket associated to the current session." + (interactive) + + ;; Make sure we have a session. + (unless rudel-current-session + (error "No active Rudel session")) + + (with-slots (connection) rudel-current-session + (with-slots (socket) connection + (continue-process socket)))) + + +;;; Reset functions +;; + +(defun rudel-kill-processes () + "TODO" + (interactive) + (mapc #'delete-process (process-list))) + +(defun rudel-reset () + "TODO" + (interactive) + (dolist (buffer (buffer-list)) + (with-current-buffer buffer + (when rudel-buffer-document + (setq rudel-buffer-document nil)))) + (rudel-kill-processes) + (setq rudel-current-session nil)) + + +;;; Socket debugging +;; + +(defmethod rudel-state-change :before ((this rudel-socket-owner) + state message) + "Print STATE and MESSAGE to debug stream." + (with-slots (socket) this + (rudel-debug-stream-insert + (rudel-debug-stream-name socket) + :state + (format "connection state changed to %s: \"%s\"" + (upcase (symbol-name state)) + ;; MESSAGE ends with a newline; remove it + (substring message 0 -1)))) + ) + + +;;; Utility functions +;; + +(defun rudel-debug-stream-name (socket) + "Return debug stream name for SOCKET." + (process-name socket)) + +(defun rudel-debug-stream-insert (stream tag data &optional object) + "Insert DATA and possibly OBJECT into stream using TAG as style." + (let* ((buffer-name (format "*%s stream*" stream)) + (buffer (or (get-buffer buffer-name) + (data-debug-new-buffer buffer-name))) + (appearance (cdr (assoc tag rudel-debug-tag-faces))) + (face (when appearance + (or (nth 0 appearance) + 'default))) + (prefix (or (nth 1 appearance) + ""))) + (save-excursion + (set-buffer buffer) + (goto-char 0) + (insert prefix + (propertize data 'face face) + (if (string= (substring data -1) "\n") + "" "\n")) + (when object + (data-debug-insert-thing object prefix "")))) + ) + +(provide 'rudel-debug) +;;; rudel-debug.el ends here diff --git a/emacs.d/lisp/rudel/.svn/text-base/rudel-errors.el.svn-base b/emacs.d/lisp/rudel/.svn/text-base/rudel-errors.el.svn-base new file mode 100644 index 0000000..9811511 --- /dev/null +++ b/emacs.d/lisp/rudel/.svn/text-base/rudel-errors.el.svn-base @@ -0,0 +1,66 @@ +;;; rudel-errors.el --- Error data used in Rudel +;; +;; Copyright (C) 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Rudel, errors, conditions +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; The following condition hierarchy is defined: +;; +;; error +;; +- rudel-error +;; +- rudel-join-error +;; +- rudel-host-error + + +;;; History: +;; +;; 0.1 - Initial revision + + +;;; Code: +;; + +;; rudel-join-error + +(intern "rudel-join-error") + +(put 'rudel-join-error 'error-conditions + '(error + rudel-error rudel-join-error)) + +(put 'rudel-join-error 'error-message + "Could not join session") + +;; rudel-host-error + +(intern "rudel-host-error") + +(put 'rudel-host-error 'error-conditions + '(error + rudel-error rudel-host-error)) + +(put 'rudel-host-error 'error-message + "Could not host session") + +(provide 'rudel-errors) +;;; rudel-errors.el ends here diff --git a/emacs.d/lisp/rudel/.svn/text-base/rudel-hooks.el.svn-base b/emacs.d/lisp/rudel/.svn/text-base/rudel-hooks.el.svn-base new file mode 100644 index 0000000..0cc9a3e --- /dev/null +++ b/emacs.d/lisp/rudel/.svn/text-base/rudel-hooks.el.svn-base @@ -0,0 +1,252 @@ +;;; rudel-hooks.el --- Hooks for Rudel events +;; +;; Copyright (C) 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Rudel, hook +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; This file contains all global hooks (as opposed to hooks provided +;; by individual objects) provided by Rudel. + + +;;; History: +;; +;; 0.1 - Initial revision. + + +;;; Code: +;; + +(require 'eieio) + + +;;; Hook variables +;; + +(defvar rudel-session-start-hook nil + "This hook is run when a new session is started. +The only argument is the session object.") + +(defvar rudel-session-end-hook nil + "This hook is run when a session ends. +The only argument is the session object.") + +(defvar rudel-session-add-user-hook nil + "This hook is run when a user is added to a session. +The arguments are the session and the user.") + +(defvar rudel-session-remove-user-hook nil + "This hook is run when a user is removed from a session. +The arguments are the session and the user.") + +(defvar rudel-session-add-document-hook nil + "This hook is run when a document is added to a session. +The arguments are the session and the document.") + +(defvar rudel-session-remove-document-hook nil + "This hook is run when a document is removed from a session. +The arguments are the session and the document.") + + +(defvar rudel-user-change-hook nil + "This hooks is run when a user object changes. +The only argument is the user object.") + + +(defvar rudel-document-attach-hook nil + "This hook is run when a document is attached to a buffer. +The arguments are the document and the buffer.") + +(defvar rudel-document-detach-hook nil + "This hook is run when document is detached from its buffer. +The arguments are the document and the buffer.") + + +;;; Handlers +;; + +(defun rudel-hooks--session-start (session) + "Watch SESSION for added/removed users and documents." + ;; Install handlers for the hooks of the session. + (with-slots (users documents) session + + ;; Watch for session end. + (object-add-hook session 'end-hook + #'rudel-hooks--session-end) + + ;; Watch all users in the session. + (dolist (user users) + (rudel-hooks--session-add-user session user)) + + ;; Watch session for added/removed users. + (object-add-hook + session 'add-user-hook + #'rudel-hooks--session-add-user) + (object-add-hook + session 'remove-user-hook + #'rudel-hooks--session-remove-user) + + ;; Watch all documents in the session. + (dolist (document documents) + (rudel-hooks--session-add-document session document)) + + ;; Watch session for added/removed documents. + (object-add-hook + session 'add-document-hook + #'rudel-hooks--session-add-document) + (object-add-hook + session 'remove-document-hook + #'rudel-hooks--session-remove-document)) + ) + +(defun rudel-hooks--session-end (session) + "Stop watching SESSION for added/removed users and documents." + ;; Remove handlers from the session. + (with-slots (users documents) session + + ;; Stop watching for session end. + (object-remove-hook session 'end-hook + #'rudel-hooks--session-end) + + ;; Stop watching all users in the session. + (dolist (user users) + (rudel-hooks--session-remove-user session user)) + + ;; Stop watching session for added/removed users. + (object-remove-hook + session 'add-user-hook + #'rudel-hooks--session-add-user) + (object-remove-hook + session 'remove-user-hook + #'rudel-hooks--session-remove-user) + + ;; Stop watching all documents in the session. + (dolist (document documents) + (rudel-hooks--session-remove-document session document)) + + ;; Stop watching session for added/removed documents. + (object-remove-hook + session 'add-document-hook + #'rudel-hooks--session-add-document) + (object-remove-hook + session 'remove-document-hook + #'rudel-hooks--session-remove-document)) + + ;; Run the hook. + (run-hook-with-args 'rudel-session-end-hook session) + ) + +(defun rudel-hooks--session-add-user (session user) + "Watch USER for changes and run `rudel-session-add-user-hook'." + ;; Watch USER. + (object-add-hook user 'change-hook #'rudel-hooks--user-change) + + ;; Run the hook. + (run-hook-with-args 'rudel-session-add-user-hook session user)) + +(defun rudel-hooks--session-remove-user (session user) + "Stop watching USER and run `rudel-session-remove-user-hook'" + ;; Stop watching USER. + (object-remove-hook user 'change-hook #'rudel-hooks--user-change) + + ;; Run the hook. + (run-hook-with-args 'rudel-session-remove-user-hook + session user)) + +(defun rudel-hooks--session-add-document (session document) + "Watch DOCUMENT and run `rudel-session-add-document-hook'." + ;; Watch document for attach/detach. + (object-add-hook document 'attach-hook + #'rudel-hooks--document-attach) + (object-add-hook document 'detach-hook + #'rudel-hooks--document-detach) + + ;; Run the hook. + (run-hook-with-args 'rudel-session-add-document-hook + session document) + ) + +(defun rudel-hooks--session-remove-document (session document) + "Stop watching DOCUMENT and run `rudel-session-remove-document-hook'." + ;; Stop watching DOCUMENT for attach/detach. + (object-remove-hook + document 'attach-hook #'rudel-hooks--document-attach) + (object-remove-hook + document 'detach-hook #'rudel-hooks--document-detach) + + ;; Run the hook. + (run-hook-with-args 'rudel-session-remove-document-hook + session document) + ) + + +(defun rudel-hooks--user-change (user) + "Run `rudel-user-change-hook' with argument USER." + (run-hook-with-args 'rudel-user-change-hook user)) + + +(defun rudel-hooks--document-attach (document buffer) + "Run `rudel-document-attach-hook' with arguments DOCUMENT and BUFFER." + (run-hook-with-args 'rudel-document-attach-hook + document buffer)) + +(defun rudel-hooks--document-detach (document buffer) + "Run `rudel-document-detach-hook' with arguments DOCUMENT and BUFFER." + (run-hook-with-args 'rudel-document-detach-hook + document buffer)) + + +;;; Initialization +;; + +(defun rudel-hooks--install-handlers () + "Install handlers for session start/end." + ;; Install handlers for already started sessions. + (when (boundp 'rudel-current-session) + (mapc + #'rudel-hooks--session-start + (when rudel-current-session + (list rudel-current-session)))) + + ;; Watch for session start/end. + (add-hook 'rudel-session-start-hook + #'rudel-hooks--session-start) + ) + +(defun rudel-hooks--uninstall-handlers () + "Uninstall handlers for session start/end." + ;; Stop watching session start/end. + (remove-hook 'rudel-session-start-hook + #'rudel-hooks--session-start) + + ;; Uninstall handlers for already started sessions. + (when (boundp 'rudel-current-session) + (mapc + #'rudel-hooks--session-end + (when rudel-current-session + (list rudel-current-session)))) + ) + +(rudel-hooks--install-handlers) + +(provide 'rudel-hooks) +;;; rudel-hooks.el ends here diff --git a/emacs.d/lisp/rudel/.svn/text-base/rudel-icons.el.svn-base b/emacs.d/lisp/rudel/.svn/text-base/rudel-icons.el.svn-base new file mode 100644 index 0000000..9bb35b5 --- /dev/null +++ b/emacs.d/lisp/rudel/.svn/text-base/rudel-icons.el.svn-base @@ -0,0 +1,90 @@ +;;; rudel-icons.el --- Icons used by Rudel +;; +;; Copyright (C) 2008, 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: rudel, icons +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; This file loads all icons used in Rudel. + + +;;; History: +;; +;; 0.1 - Initial revision. + + +;;; Code: +;; + +(require 'image) + + +;;; Image constants +;; + +(defconst rudel-icons-image-formats '(svg png) + "Image formats to try (in that order) when loading Rudel icons.") + +(defconst rudel-icons-directory + (file-name-as-directory + (concat (file-name-directory + (locate-library "rudel-icons.el")) + "icons")) + "Directory that holds Rudel icon files.") + + +;;; Helper macro +;; + +(defmacro rudel-defimage (name &optional docstring) + "Load image from Rudel icon directory and define image named NAME. +Optional argument DOCSTRING is the documentation string to +associate with the image." + (declare (doc-string 2)) + (let ((icon (intern (format "rudel-icon-%s" name))) + (specs (mapcar + (lambda (type) + `(:type ,type + :ascent center + :mask heuristic + :file ,(concat rudel-icons-directory + name "." (symbol-name type)))) + rudel-icons-image-formats))) + `(defimage ,icon + (,@specs) + ,(or docstring + (format "%s icon." (capitalize name))))) + ) + + +;;; Image definitions +;; + +(rudel-defimage "person") +(rudel-defimage "document") +(rudel-defimage "connected") +(rudel-defimage "disconnected") +(rudel-defimage "plaintext") +(rudel-defimage "encrypted") + +(provide 'rudel-icons) +;;; rudel-icons.el ends here diff --git a/emacs.d/lisp/rudel/.svn/text-base/rudel-interactive.el.svn-base b/emacs.d/lisp/rudel/.svn/text-base/rudel-interactive.el.svn-base new file mode 100644 index 0000000..5dda752 --- /dev/null +++ b/emacs.d/lisp/rudel/.svn/text-base/rudel-interactive.el.svn-base @@ -0,0 +1,181 @@ +;;; rudel-interactive.el --- User interaction functions for Rudel. +;; +;; Copyright (C) 2008, 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Rudel, user, interface, interaction +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; Functions for user interactions commonly used in Rudel components. + + +;;; History: +;; +;; 0.1 - Initial revision. + + +;;; Code: +;; + +(require 'rudel-compat) ;; for `read-color' replacement + + +;;; Function for reading Rudel objects from the user. +;; + +(defun rudel-read-backend (backends &optional prompt return) + "Read a backend name from BACKENDS and return that name or the actual backend depending on RETURN. +If RETURN is 'object, return the backend object which is of the +form (NAME . CLASS-OR-OBJECT); Otherwise return the name as +string." + (unless prompt + (setq prompt "Backend: ")) + (let* ((backend-names (mapcar (lambda (cell) + (symbol-name (car cell))) + backends)) + (backend-name (completing-read prompt backend-names nil t))) + (cond + ((eq return 'object) + (assoc (intern backend-name) backends)) + (t backend-name))) + ) + +(defun rudel-read-session (sessions &optional prompt return) + "Read a session name from SESSIONS and return that name or the session info depending on RETURN. +If PROMPT is non-nil use as prompt string. +If RETURN is 'object, return the session object; Otherwise return +the name as string." + (unless prompt + (setq prompt "Session: ")) + ;; For presentation and identification of sessions, use the :name + ;; property. + (flet ((to-string (session) + (if (rudel-backend-cons-p session) + (symbol-name (car session)) + (plist-get session :name)))) + ;; Read a session by name, then return that name or the + ;; corresponding session info. + (let ((session-name (completing-read prompt + (mapcar #'to-string sessions) + nil t))) + (cond + ((eq return 'object) + (find session-name sessions + :key #'to-string :test #'string=)) + (t session-name)))) + ) + +(defun rudel-read-user-name () + "Read a username. +The default is taken from `rudel-default-username'." + (read-string "Username: " rudel-default-username)) + +(defun rudel-read-user-color () + "Read a color." + (read-color "Color: " t)) + +(defun rudel-read-user (&optional users prompt return) + "Read a user name from USERS and return that name or the actual user depending on RETURN. +If USERS is nil, use the user list of `rudel-current-session'. +If RETURN. is 'object, return the user object; Otherwise return +the name as string." + ;; If no user list is provided, the user list of the current session + ;; is used. + (unless users + (if rudel-current-session + (setq users (oref rudel-current-session :users)) + (error "No user list and no active Rudel session"))) + (unless prompt + (setq prompt "User: ")) + ;; Construct a list of user name, read a name with completion and + ;; return a user name of object. + (let* ((user-names (mapcar 'object-name-string users)) + (user-name (completing-read prompt user-names nil t))) + (cond + ((eq return 'object) + (find user-name users + :test 'string= :key 'object-name-string)) + (t user-name))) + ) + +(defun rudel-read-document (&optional documents prompt return) + "Read a document name from DOCUMENTS and return that name or the actual document depending on RETURN. +If RETURN. is 'object, return the backend object; Otherwise +return the name as string." + (unless documents + (if rudel-current-session + (setq documents (oref rudel-current-session :documents)) + (error "No document list and no active Rudel session"))) + (unless documents + (error "No documents")) ; TODO error is a bit harsh + (unless prompt + (setq prompt "Document: ")) + + ;; Construct list of names, read one name and return that name or + ;; the named object. + (let* ((document-names (mapcar #'rudel-unique-name documents)) + (document-name (completing-read prompt document-names nil t))) + (cond + ((eq return 'object) + (find document-name documents + :test #'string= :key #'rudel-unique-name)) + (t document-name))) + ) + + +;;; Buffer allocation functions +;; + +(defun rudel-allocate-buffer-clear-existing (name) + "When the requested buffer NAME exists, clear its contents and use it." + (let ((buffer (get-buffer name))) + (if buffer + (progn + ;; Ask the user whether it is OK to erase the contents of + ;; the buffer. + (unless (yes-or-no-p (format + "Buffer `%s' already exists; Erase contents? " + name)) + (error "Buffer `%s' already exists" name)) ;; TODO throw or signal; not error + ;; When the buffer is attached to a different document, ask + ;; whether it is OK to detach the buffer. + (let ((document (rudel-buffer-document buffer))) + (unless (or (not document) + (yes-or-no-p (format + "Buffer `%s' is attached to the document `%s'; Detach? " + name + (rudel-unique-name document)))) + (error "Buffer `%s' already attached to a document" name))) + ;; Delete buffer contents; maybe detach buffer first. + (when (rudel-buffer-has-document-p buffer) + (rudel-unpublish-buffer buffer)) + (with-current-buffer buffer + (erase-buffer))) + (setq buffer (get-buffer-create name))) + buffer) + ) + +(defun rudel-allocate-buffer-make-unique (name) + "When the requested buffer NAME exists, create another buffer." + (get-buffer-create (generate-new-buffer-name name))) + +(provide 'rudel-interactive) +;;; rudel-interactive.el ends here diff --git a/emacs.d/lisp/rudel/.svn/text-base/rudel-loaddefs.el.svn-base b/emacs.d/lisp/rudel/.svn/text-base/rudel-loaddefs.el.svn-base new file mode 100644 index 0000000..9de8708 --- /dev/null +++ b/emacs.d/lisp/rudel/.svn/text-base/rudel-loaddefs.el.svn-base @@ -0,0 +1,23 @@ +;;; rudel-loaddefs.el + +(autoload 'rudel-join-session "rudel" "Start a collaborative Rudel session" t) +(autoload 'rudel-host-session "rudel" "Host a collaborative Rudel session" t) +(autoload 'rudel-speedbar "rudel-speedbar" + "Show connected users and documents for the Rudel session in speedbar" t) +(autoload 'global-rudel-minor-mode "rudel-mode" + "Bindings for rudel session-level commands" t) + +(global-set-key (kbd "C-c c j") 'rudel-join-session) + +(setq rudel-dir (file-name-directory (or (buffer-file-name) load-file-name))) + +(dolist (dir '("." "jupiter" "obby" "zeroconf")) + (add-to-list 'load-path (concat rudel-dir "/" dir))) + +(eval-after-load 'rudel + '(progn (global-rudel-minor-mode) + (require 'rudel-obby) + (when (require 'zeroconf nil t) + (require 'rudel-zeroconf)))) + +(provide 'rudel-loaddefs) \ No newline at end of file diff --git a/emacs.d/lisp/rudel/.svn/text-base/rudel-mode.el.svn-base b/emacs.d/lisp/rudel/.svn/text-base/rudel-mode.el.svn-base new file mode 100644 index 0000000..182188a --- /dev/null +++ b/emacs.d/lisp/rudel/.svn/text-base/rudel-mode.el.svn-base @@ -0,0 +1,621 @@ +;;; rudel-mode.el --- Global and buffer-local Rudel minor modes +;; +;; Copyright (C) 2008, 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: rudel, mode +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; This file contains the following global and buffer-local Rudel +;; minor modes: +;; +;; + `rudel-header-subscriptions-minor-mode': Display subscribed users +;; and their respective status in the header line +;; + `rudel-mode-line-publish-state-minor-mode': Display publication +;; state of buffers in their mode lines +;; + `global-rudel-minor-mode': Installs a keymap and a Rudel menu + + +;;; History: +;; +;; 0.4 - Display buffer publication state in mode line. +;; +;; 0.3 - Display subscriptions in header line. +;; +;; 0.2 - Use define-minor-mode. +;; +;; 0.1 - Initial revision. + + +;;; Code: +;; + +(require 'easy-mmode) +(require 'easymenu) + +(require 'rudel) +(require 'rudel-hooks) + + +;;; Customization Options +;; + +(defcustom rudel-header-subscriptions-use-images t + "Use images when displaying subscribed users in header-line." + :group 'rudel + :type 'boolean + :set (lambda (symbol value) + (set-default symbol value) + (when (featurep 'rudel-mode) + (rudel-header-subscriptions--options-changed)))) + +(defcustom rudel-header-subscriptions-separator " " + "String used to separate indicator strings of subscribed users." + :group 'rudel + :type 'string + :set (lambda (symbol value) + (set-default symbol value) + (when (featurep 'rudel-mode) + (rudel-header-subscriptions--options-changed)))) + +(defcustom rudel-mode-line-publish-state-unpublished-string "-" + "String used to indicate not published state in the mode line." + :group 'rudel + :type 'string + :set (lambda (symbol value) + (set-default symbol value) + (when (featurep 'rudel-mode) + (rudel-mode-line-publish-state--options-changed)))) + +(defcustom rudel-mode-line-publish-state-published-string "P" + "String used to indicate published state in the mode line." + :group 'rudel + :type 'string + :set (lambda (symbol value) + (set-default symbol value) + (when (featurep 'rudel-mode) + (rudel-mode-line-publish-state--options-changed)))) + +(dolist (v '(rudel-header-subscriptions-use-images + rudel-header-subscriptions-separator + rudel-mode-line-publish-state-unpublished-string + rudel-mode-line-publish-state-published-string)) + (put v 'save-local-variable t)) + + +;;; Header line subscriptions helper functions +;; + +(defun rudel-header-subscriptions--make-format (document) + "Return a Lisp object usable as `header-line-format' generated from DOCUMENT." + (with-slots (subscribed) document + (mapconcat + (lambda (user) + (rudel-display-string + user rudel-header-subscriptions-use-images)) + subscribed rudel-header-subscriptions-separator))) + +(defun rudel-header-subscriptions--update-from-document (document) + "Update header-line of the buffer attached to DOCUMENT." + (with-slots (buffer) document + (when buffer + (with-current-buffer buffer + (setq header-line-format + (rudel-header-subscriptions--make-format document)) + (force-mode-line-update))))) + +(defun rudel-header-subscriptions--update-from-buffer () + "Update header-line of the current buffer from associated document." + (setq header-line-format + (when (rudel-buffer-document) + (rudel-header-subscriptions--make-format + (rudel-buffer-document)))) + (force-mode-line-update)) + +(defun rudel-header-subscriptions--options-changed () + "Update headers in buffers that have header subscriptions mode enabled." + (dolist (buffer (buffer-list)) + (with-current-buffer buffer + (when rudel-header-subscriptions-minor-mode + (rudel-header-subscriptions--update-from-buffer))))) + + +;;; Header line indication of users' status and activities +;; + +(defun rudel-header-subscriptions--user-change (document user) + "Update header line after USER changed." + ;; Update the header line to reflect the changes to USER. + (rudel-header-subscriptions--update-from-document document)) + +(defun rudel-header-subscriptions--add-user (document user) + "Start monitoring USER and update header line." + ;; Monitor USER. + (lexical-let ((document document)) + (object-add-hook user 'change-hook + (lambda (user) + (rudel-header-subscriptions--user-change + document user)))) + + ;; Update the header line once to get the user added. + (rudel-header-subscriptions--update-from-document document) + ) + +(defun rudel-header-subscriptions--remove-user (document user) + "Stop monitoring USER and update header line." + ;; TODO Stop monitoring USER. + ;; (object-remove-hook user 'change-hook + + ;; Update the header line once to get the user removed. + (rudel-header-subscriptions--update-from-document document) + ) + +;;;###autoload +(define-minor-mode rudel-header-subscriptions-minor-mode + "Toggle Rudel header subscriptions minor mode. + +This mode displays users subscribed to the document associated +with the buffer in the header-line. Depending on the kind of +session, additional information like connection status, +encryption or activity indication may be displayed with each +user. + +If ARG is null, toggle Rudel header subscriptions mode. +If ARG is a number greater than zero, turn on Rudel header +subscriptions mode; otherwise, turn it off." + :init-value nil + :group 'rudel + (cond + ;; Emacs session is not interactive + (noninteractive + (setq rudel-header-subscriptions-minor-mode nil)) + + ;; Mode is being enabled and the buffer has an attached document. + ((and rudel-header-subscriptions-minor-mode + (rudel-buffer-document)) + (let ((document (rudel-buffer-document))) + + ;; Monitor all users that already are subscribed to the + ;; document. + (with-slots (subscribed) document + (dolist (user subscribed) + (rudel-header-subscriptions--add-user document user))) + + ;; Monitor future (un)subscribe events. + (object-add-hook document 'subscribe-hook + #'rudel-header-subscriptions--add-user) + (object-add-hook document 'unsubscribe-hook + #'rudel-header-subscriptions--remove-user)) + + ;; Update header line. + (rudel-header-subscriptions--update-from-buffer)) + + ;; Mode is being disabled, but the buffer has an attached document. + ((and (not rudel-header-subscriptions-minor-mode) + (rudel-buffer-document)) + (let ((document (rudel-buffer-document))) + + ;; Stop monitoring all users that are subscribed to the + ;; document. + (with-slots (subscribed) document + (dolist (user subscribed) + (rudel-header-subscriptions--remove-user document user))) + + ;; Stop monitoring (un)subscribe events. + (object-remove-hook document 'subscribe-hook + #'rudel-header-subscriptions--add-user) + (object-remove-hook document 'unsubscribe-hook + #'rudel-header-subscriptions--remove-user)) + + ;; Reset header line to default format. + (setq header-line-format default-header-line-format) + (force-mode-line-update)) ;; TODO remove all handlers + + ;; No buffer document + (t + ;; Ensure the mode is disabled. + (setq rudel-header-subscriptions-minor-mode nil) + + ;; Reset header line to default format. + (setq header-line-format default-header-line-format) + (force-mode-line-update))) + ) + + +;;; Global header subscriptions mode +;; + +;; Tracking stuff for the global mode + +(defun rudel-header-subscriptions--attach (document buffer) + "Activate header subscriptions mode for BUFFER." + (with-current-buffer buffer + (rudel-header-subscriptions-minor-mode 1))) + +(defun rudel-header-subscriptions--detach (document buffer) + "Deactivate header subscriptions mode for BUFFER." + (with-current-buffer buffer + (rudel-header-subscriptions-minor-mode 0))) + +(defun rudel-header-subscriptions--add-document (session document) + "Watch DOCUMENT for attach/detach events." + ;; When document is attached to a buffer, turn the mode on. + (with-slots (buffer) document + (when buffer + (rudel-header-subscriptions--attach document buffer))) + + ;; Watch document for attaching and detaching. + (object-add-hook + document 'attach-hook #'rudel-header-subscriptions--attach) + (object-add-hook + document 'detach-hook #'rudel-header-subscriptions--detach)) + +(defun rudel-header-subscriptions--remove-document (session document) + "Stop watching DOCUMENT for attach/detach events." + ;; When document is attached to a buffer, turn the mode off. + (with-slots (buffer) document + (when buffer + (rudel-header-subscriptions--detach document buffer))) + + ;; Stop watching document for attaching and detaching. + (object-remove-hook + document 'attach-hook #'rudel-header-subscriptions--attach) + (object-remove-hook + document 'detach-hook #'rudel-header-subscriptions--detach)) + +(defun rudel-header-subscriptions--session-start (session) + "Watch SESSION documents and watch for added/removed documents." + ;; Watch all documents in the session. + (with-slots (documents) session + (dolist (document documents) + (rudel-header-subscriptions--add-document session document))) + + ;; Watch session for added/removed documents. + (object-add-hook + session 'add-document-hook + #'rudel-header-subscriptions--add-document) + (object-add-hook + session 'remove-document-hook + #'rudel-header-subscriptions--remove-document) + ) + +(defun rudel-header-subscriptions--session-end (session) + "Stop watching SESSION for added/removed documents." + ;; Stop watching all documents in the session. + (with-slots (documents) session + (dolist (document documents) + (rudel-header-subscriptions--remove-document session document))) + + ;; Stop watching session for added/removed documents. + (object-remove-hook + session 'add-document-hook + #'rudel-header-subscriptions--add-document) + (object-remove-hook + session 'remove-document-hook + #'rudel-header-subscriptions--remove-document) + ) + +;;;###autoload +(define-globalized-minor-mode global-rudel-header-subscriptions-mode + rudel-header-subscriptions-minor-mode + rudel-header-subscriptions-minor-mode + :group 'rudel) + +(defadvice global-rudel-header-subscriptions-mode + (around track-subscriptions activate) + "Start/stop tracking subscriptions when the mode is (de)activated." + (let ((value ad-do-it)) + (if value + + ;; Add handlers to session start and end hooks and run the + ;; start handler on already started sessions. + (progn + + ;; Go through all existing sessions. + (mapc #'rudel-header-subscriptions--session-start + (when rudel-current-session + (list rudel-current-session))) + + ;; Watch for new/ended sessions. + (add-hook 'rudel-session-start-hook + #'rudel-header-subscriptions--session-start) + (add-hook 'rudel-session-end-hook + #'rudel-header-subscriptions--session-end)) + + ;; Remove handlers from session start and end hooks and run the + ;; end handler on active sessions. + (mapc #'rudel-header-subscriptions--session-end + (when rudel-current-session + (list rudel-current-session))) + + (remove-hook 'rudel-session-start-hook + #'rudel-header-subscriptions--session-start) + (remove-hook 'rudel-session-end-hook + #'rudel-header-subscriptions--session-end))) + ) + + +;;; Mode line indication of buffer state +;; + +(defvar rudel-mode-line-publish-state-string + (propertize + "-" + 'mouse-face 'mode-line-highlight + 'help-echo "Buffer is not published") + "Contains a mode line fragment indicating the publication state +of the buffer.") +(make-variable-buffer-local 'rudel-mode-line-publish-state-string) +(put 'rudel-mode-line-publish-state-string 'risky-local-variable t) + +(defun rudel-mode-line-publish-state--add-indicator-to-mode-line () + "Add Rudel publish state indicator to mode line." + (let* ((new-format (copy-list mode-line-format)) + (format-rest (nthcdr + (position 'mode-line-modified mode-line-format) + new-format)) + (format-rest-cdr (cdr format-rest))) + (setcdr format-rest (cons 'rudel-mode-line-publish-state-string + format-rest-cdr)) + (setq mode-line-format new-format)) + (force-mode-line-update)) + +(defun rudel-mode-line-publish-state--remove-indicator-from-mode-line () + "Remove Rudel publish state indicator from mode line." + (let ((format-rest (nthcdr + (position 'mode-line-remote mode-line-format) + mode-line-format))) + ;; Only change the mode line if our indicator is present. + (when (eq (second format-rest) 'rudel-mode-line-publish-state-string) + (setcdr format-rest (cddr format-rest)) + (force-mode-line-update)))) + +(defun rudel-mode-line-publish-state--update-string () + "Update variable `rudel-mode-line-publish-state-string'." + ;; Update `rudel-mode-line-publish-state-string' with appropriate + ;; propertized indicator string. + (setq rudel-mode-line-publish-state-string + (cond + ((rudel-buffer-document) + (propertize + rudel-mode-line-publish-state-published-string + 'mouse-face 'mode-line-highlight + 'help-echo "Buffer is published")) + (t + (propertize + rudel-mode-line-publish-state-unpublished-string + 'mouse-face 'mode-line-highlight + 'help-echo "Buffer is not published")))) + + ;; Update the mode line. + (force-mode-line-update) + ) + +(defun rudel-mode-line-publish-state--document-attach (document buffer) + "Handle attaching of DOCUMENT to BUFFER. +When `rudel-mode-line-publish-state-minor-mode' is enabled in +BUFFER, update the state string." + ;; Only act when BUFFER has the minor mode enabled. + (with-current-buffer buffer + (when rudel-mode-line-publish-state-minor-mode + ;; Update the mode line. + (rudel-mode-line-publish-state--update-string) + + ;; Watch for detaching of DOCUMENT from BUFFER. + (object-add-hook + document 'detach-hook + #'rudel-mode-line-publish-state--document-detach))) + ) + +(defun rudel-mode-line-publish-state--document-detach (document buffer) + "Handle detaching of DOCUMENT from BUFFER." + ;; Update the mode line of BUFFER. + (with-current-buffer buffer + (rudel-mode-line-publish-state--update-string)) + + ;; Stop watching for detaching of DOCUMENT from BUFFER. It (or a + ;; different document) has to attach again first, before the next + ;; detaching can occur. + (object-remove-hook + document 'detach-hook + #'rudel-mode-line-publish-state--document-detach) + ) + +(defun rudel-mode-line-publish-state--options-changed () + "Update mode lines in buffers that have mode line publish state mode enabled." + (dolist (buffer (buffer-list)) + (with-current-buffer buffer + (when rudel-mode-line-publish-state-minor-mode + (rudel-mode-line-publish-state--update-string))))) + +;;;###autoload +(define-minor-mode rudel-mode-line-publish-state-minor-mode + "Toggle Rudel mode line publish state minor mode. + +This mode displays an indicator of the buffer's state with +respect to an associated Rudel document in the mode line. If the +buffer has an attached document, the string \"P\" is displayed +after the remote file indicator. Otherwise, the string \"-\" is +displayed. + +If ARG is null, toggle Rudel mode line publish state minor mode. +If ARG is a number greater than zero, turn on Rudel minor mode +line publish state mode; otherwise, turn it off." + :init-value nil + :group 'rudel + (cond + ;; Emacs session is not interactive + (noninteractive + (setq rudel-mode-line-publish-state-minor-mode nil)) + + ;; Mode is enabled + (rudel-mode-line-publish-state-minor-mode + ;; Extend and update the mode line, no matter whether the buffer + ;; has a document or not. + (rudel-mode-line-publish-state--add-indicator-to-mode-line) + (rudel-mode-line-publish-state--update-string) + + ;; Watch document, if available or attach events, when a document + ;; is not available. + (let ((document (rudel-buffer-document))) + (if document + ;; Handle detaching of the document from the buffer. + (object-add-hook + document 'detach-hook + #'rudel-mode-line-publish-state--document-detach) + ;; Handle attaching of documents to buffers. We use the global + ;; hook here, installing the handler twice is prevented by + ;; `add-hook'. + (add-hook 'rudel-document-attach-hook + #'rudel-mode-line-publish-state--document-attach)))) + + ;; Mode is disabled + (t + ;; Maybe stop watching for the document detaching from the buffer. + (let ((document (rudel-buffer-document))) + (when document + (object-remove-hook + document 'detach-hook + #'rudel-mode-line-publish-state--document-detach))) + + ;; Remove the indicator from the mode line. + (rudel-mode-line-publish-state--remove-indicator-from-mode-line))) + ) + + +;;; Global mode line publish state mode +;; + +;;;###autoload +(define-globalized-minor-mode global-rudel-mode-line-publish-state-mode + rudel-mode-line-publish-state-minor-mode + rudel-mode-line-publish-state-minor-mode + :group 'rudel) + + +;;; Global Rudel mode, menu and keymap +;; + +(defvar rudel-minor-keymap + (let ((map (make-sparse-keymap)) + (sub-map (make-sparse-keymap))) + ;; Define sub keymap + (define-key sub-map "j" #'rudel-join-session) + (define-key sub-map "h" #'rudel-host-session) + (define-key sub-map "e" #'rudel-end-session) + + (define-key sub-map "c" #'rudel-change-color) + + (define-key sub-map "p" #'rudel-publish-buffer) + (define-key sub-map "u" #'rudel-unpublish-buffer) + (define-key sub-map "s" #'rudel-subscribe) + + ;; Bind the sub keymap into map + (define-key map "\C-cc" sub-map) + map) + "Keymap used in Rudel minor mode.") + +(when rudel-minor-keymap + (easy-menu-define + rudel-minor-menu rudel-minor-keymap "Rudel Minor Mode Menu" + '("Rudel" + [ "Join Session" rudel-join-session + (not rudel-current-session) ] + [ "Leave Session" rudel-end-session + rudel-current-session ] + "---" + [ "Host a Session" rudel-host-session + t ] + "---" + [ "Change Color" rudel-change-color + (and rudel-current-session + (rudel-capable-of-p + (oref rudel-current-session :backend) + 'change-color)) ] ; TODO bulky + "---" + [ "Publish current Buffer" rudel-publish-buffer + (and rudel-current-session + (not (rudel-buffer-has-document-p))) ] + [ "Unpublish current Buffer" rudel-unpublish-buffer + (rudel-buffer-has-document-p) ] + [ "Subscribe to Document" rudel-subscribe + rudel-current-session ] + "---" + [ "Rudel Overview" rudel-speedbar + t ] + "---" + ( "Options" + [ "Highlight Contributions in Authors' Colors" + (lambda () + (interactive) + (setq rudel-overlay-author-display + (not rudel-overlay-author-display)) + (rudel-overlay-options-changed)) + :style toggle + :selected rudel-overlay-author-display ] + ( "Show subscribed Users" + [ "In this Buffer" + rudel-header-subscriptions-minor-mode + :style toggle + :selected rudel-header-subscriptions-minor-mode ] + [ "Globally" + global-rudel-header-subscriptions-mode + :style toggle + :selected global-rudel-header-subscriptions-mode ] ) + ( "Show Status in mode line" + [ "In this Buffer" + rudel-mode-line-publish-state-minor-mode + :style toggle + :selected rudel-mode-line-publish-state-minor-mode ] + [ "Globally" + global-rudel-mode-line-publish-state-mode + :style toggle + :selected global-rudel-mode-line-publish-state-mode ] ) ) ) + ) + ) + +;;;###autoload +(define-minor-mode global-rudel-minor-mode + "Toggle global Rudel minor mode (No modeline indicator). + +If ARG is null, toggle global Rudel mode. +If ARG is a number greater than zero, turn on global Rudel mode; +otherwise, turn it off." + :init-value nil + :keymap rudel-minor-keymap + :global t + :group 'rudel + (cond + ;; Emacs session is not interactive + (noninteractive + (setq global-rudel-minor-mode nil)) + + ;; Mode is enabled + (global-rudel-minor-mode + ) + + ;; Mode is disabled + (t + )) + ) + +(provide 'rudel-mode) +;;; rudel-mode.el ends here diff --git a/emacs.d/lisp/rudel/.svn/text-base/rudel-operations.el.svn-base b/emacs.d/lisp/rudel/.svn/text-base/rudel-operations.el.svn-base new file mode 100644 index 0000000..3637303 --- /dev/null +++ b/emacs.d/lisp/rudel/.svn/text-base/rudel-operations.el.svn-base @@ -0,0 +1,133 @@ +;;; rudel-operations.el --- Rudel domain operations +;; +;; Copyright (C) 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Rudel, operations +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; This file contains classes representing operations like insertions +;; and deletions, that are import for Rudel's domain of collaborative +;; editing. + + +;;; History: +;; +;; 0.1 - Initial revision + + +;;; Code: +;; + +(require 'eieio) + + +;;; Class rudel-operation +;; + +(defclass rudel-operation () + () + "Abstract base class for operations." + :abstract t) + +(defgeneric rudel-apply ((this rudel-operation) object) + "Apply the change represented by THIS to OBJECT.") + + +;;; Class rudel-insert-op +;; + +;; TODO should be rudel-insert but there is a method of the same name +(defclass rudel-insert-op (rudel-operation) + ((from :initarg :from + :type (or null (integer 0)) + :documentation + "Start of the region affected by this operation or +nil. nil means end of buffer") + (data :initarg :data + :type string + :documentation + "The inserted string.")) + "Objects of this class represent insertion operations.") + +(defmethod rudel-apply ((this rudel-insert-op) object) + "Apply THIS to OBJECT by inserting the associated data." + (with-slots (from data) this + (rudel-insert object from data))) + +(defmethod slot-missing ((this rudel-insert-op) + slot-name operation &optional new-value) + "Simulate read-only slots :length and :to." + (cond + ;; Slot :length + ((and (or (eq slot-name :length) + (eq slot-name 'length)) + (eq operation 'oref)) + (with-slots (data) this + (length data))) + ;; Slot :to + ((and (or (eq slot-name :to) + (eq slot-name 'to)) + (eq operation 'oref)) + (with-slots (from length) this + (+ from length))) + ;; Call next method + (t (call-next-method))) + ) + + +;;; Class rudel-delete-op +;; + +;; TODO should be rudel-delete but there is a method of the same name +(defclass rudel-delete-op (rudel-operation) + ((from :initarg :from + :type (integer 0) + :documentation + "Start of the region affected by this operation.") + (to :initarg :to + :type (integer 0) + :documentation + "End of the region affected by this operation.")) + "Objects of this class represent deletion operations.") + +(defmethod rudel-apply ((this rudel-delete-op) object) + "Apply THIS to OBJECT by deleting the associated region." + (with-slots (from length) this + (rudel-delete object from length))) + +(defmethod slot-missing ((this rudel-delete-op) + slot-name operation &optional new-value) + "Simulate slot :length" + (cond + ;; Slot :length + ((or (eq slot-name :length) + (eq slot-name 'length)) + (with-slots (from to) this + (if (eq operation 'oref) + (- to from) + (setq to (+ from new-value))))) + ;; Call next method + (t (call-next-method))) + ) + +(provide 'rudel-operations) +;;; rudel-operations.el ends here diff --git a/emacs.d/lisp/rudel/.svn/text-base/rudel-operators.el.svn-base b/emacs.d/lisp/rudel/.svn/text-base/rudel-operators.el.svn-base new file mode 100644 index 0000000..e51982e --- /dev/null +++ b/emacs.d/lisp/rudel/.svn/text-base/rudel-operators.el.svn-base @@ -0,0 +1,172 @@ +;;; rudel-operators.el --- Sets of modification operators for Rudel objects +;; +;; Copyright (C) 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Rudel, operators +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; Collections of operations on specific objects are collected into +;; classes. Current there are +;; +;; - rudel-document-operators: realize operations on document objects +;; +;; - rudel-connection-operators: realize operations on connection +;; objects +;; +;; - rudel-overlay-operators: realize operations by altering the +;; overlays of buffer objects +;; +;; - rudel-hook-operators: realize operations by calling hooks + + +;;; History: +;; +;; 0.1 - Initial revision. + + +;;; Code: +;; + +(require 'eieio) + +(require 'rudel-overlay) + + +;;; Class rudel-document-operators +;; + +(defclass rudel-document-operators () + ((document :initarg :document + :type rudel-document-child + :documentation + "Document to which modification operators are +applied.")) + "Provides operation methods which modify an associated document.") + +(defmethod rudel-insert ((this rudel-document-operators) position data) + "Insert DATA at POSITION into the document attached to THIS." + (with-slots (document) this + (rudel-insert document position data))) + +(defmethod rudel-delete ((this rudel-document-operators) position length) + "Delete a region of LENGTH characters at POSITION from the document attached to THIS." + (with-slots (document) this + (rudel-delete document position length))) + + +;;; Class rudel-connection-operators +;; + +(defclass rudel-connection-operators () + ((connection :initarg :connection + :type rudel-connection-child + :documentation + "Connection on which the operations are +performed.") + (document :initarg :document + :type rudel-document-child + :documentation + "Document object to which operations refer.")) + "Provides operation methods which affect an associated +connection.") + +(defmethod rudel-insert ((this rudel-connection-operators) position data) + "Notify the connection associated to THIS of the insertion of DATA at POSITION." + (with-slots (connection document) this + (rudel-local-insert connection document position data))) + +(defmethod rudel-delete ((this rudel-connection-operators) position length) + "Notify the connection associated to THIS of a deletion of LENGTH at POSITION." + (with-slots (connection document) this + (rudel-local-delete connection document position length))) + + +;;; Class rudel-overlay-operators +;; + +(defclass rudel-overlay-operators () + ((document :initarg :document + :type rudel-document-child + :documentation + "The document to the overlays of which the +operations are applied") + (user :initarg :user + :type rudel-user-child + :documentation + "The user object associated to operations.")) + "Provides operation methods which affect the overlays of a +buffer.") + +(defmethod rudel-insert ((this rudel-overlay-operators) position data) + "Update the overlays associated to THIS to incorporate an insertion of DATA at POSITION." + (with-slots (document user) this + (with-slots (buffer) document + + ;; Since we inserted something, (point-max) is at least the + ;; length of the insertion + 1. So we can safely subtract the + ;; length of the insertion and 1. + (unless position + (with-current-buffer buffer + (setq position (- (point-max) (length data) 1)))) + + + (rudel-update-author-overlay-after-insert + buffer (+ position 1) (length data) user))) + ) + +(defmethod rudel-delete ((this rudel-overlay-operators) position length) + "Update the overlays associated to THIS to incorporate a deletion of LENGTH at POSITION." + (with-slots (document user) this + (with-slots (buffer) document + (rudel-update-author-overlay-after-delete + buffer (+ position 1) length user)))) + + +;;; Class rudel-hook-operators +;; + +(defclass rudel-hook-operators () + ((document :initarg :document + :type rudel-document-child + :documentation + "The document object to which operations refer.") + (user :initarg :user + :type rudel-user-child + :documentation + "The user object associated to operations.")) + "Provides operation methods which cause corresponding hooks to +be called.") + +(defmethod rudel-insert ((this rudel-hook-operators) position data) + "Call insert hook associated to THIS with POSITION and DATA." + (with-slots (document user) this + (with-slots (buffer) document + (run-hook-with-args 'rudel-insert-hook buffer user position data)))) + +(defmethod rudel-delete ((this rudel-hook-operators) position length) + "Call delete hook associated to THIS with POSITION and LENGTH." + (with-slots (document user) this + (with-slots (buffer) document + (run-hook-with-args 'rudel-delete-hook buffer user position length)))) + +(provide 'rudel-operators) +;;; rudel-operators.el ends here diff --git a/emacs.d/lisp/rudel/.svn/text-base/rudel-overlay.el.svn-base b/emacs.d/lisp/rudel/.svn/text-base/rudel-overlay.el.svn-base new file mode 100644 index 0000000..093afdf --- /dev/null +++ b/emacs.d/lisp/rudel/.svn/text-base/rudel-overlay.el.svn-base @@ -0,0 +1,275 @@ +;;; rudel-overlay.el --- Overlay functions for Rudel +;; +;; Copyright (C) 2008, 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: rudel, overlay +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; + + +;;; History: +;; +;; 0.1 - Initial revision. + + +;;; Code: +;; + +(require 'custom) + +(eval-when-compile + (require 'cl)) + + +;;; Rudel overlay faces +;; + +(defcustom rudel-overlay-author-display t + "Indicate authorship by setting text color to user color." + :group 'rudel + :type 'boolean + :set (lambda (symbol value) + (set-default symbol value) + (when (featurep 'rudel-overlay) + (rudel-overlay-options-changed)))) + +(put 'rudel-overlay-author-display 'safe-local-variable t) + +(defface rudel-author-overlay-face + '((default (:background "black"))) + "*Face used to highlight contributions according to their authors. +Attributes involving color are not applied literally. Instead the +color is replaced with the color associated with the respective +author." + :group 'rudel) + + +;;; General overlay functions +;; + +(defun rudel-overlay-p (overlay) + "Non-nil if OVERLAY is a Rudel overlay." + (overlay-get overlay :rudel)) + +(defun rudel-overlay-length (overlay) + "Distance between end and start of OVERLAY." + (- (overlay-end overlay) (overlay-start overlay))) + +(defun rudel-overlay-user (overlay) + "User object associated to OVERLAY." + (overlay-get overlay :user)) + +(defun rudel-overlays (&optional predicate) + "Return a list of Rudel-related overlays or overlays satisfying PREDICATE. +If PREDICATE is non-nil returned overlays satisfy PREDICATES; +Otherwise all Rudel-related overlays are returned." + (unless predicate + (setq predicate #'rudel-overlay-p)) + + (let* ((overlay-lists (overlay-lists)) + (overlays (append (car overlay-lists) + (cdr overlay-lists)))) + (remove-if-not predicate overlays)) + ) + +(defun rudel-overlays-at (position &optional predicate) + "Return a list of Rudel-related overlays at POSITION. +If PREDICATE is non-nil returned overlays satisfy PREDICATES; +Otherwise all Rudel-related overlays are returned." + (unless predicate + (setq predicate #'rudel-overlay-p)) + (remove-if-not predicate (overlays-at position))) + +(defun rudel-overlays-in (start end &optional predicate) + "Return a list of Rudel-related overlays in the range START to END. +If PREDICATE is non-nil returned overlays satisfy PREDICATES; +Otherwise all Rudel-related overlays are returned." + (unless predicate + (setq predicate #'rudel-overlay-p)) + (remove-if-not predicate (overlays-in start end))) + +(defun rudel-overlays-remove-all () + "Remove all Rudel overlays from the current buffer." + (mapc #'delete-overlay (rudel-overlays))) + + +;;; Author overlay +;; + +(defun rudel-author-overlay-p (overlay) + "Predicate for author overlays." + (eq (overlay-get overlay :rudel) 'author)) + +(defun rudel-author-overlays () + "Return the list of author overlays in the current buffer." + (rudel-overlays #'rudel-author-overlay-p)) + +(defun rudel-author-overlay-at (position &optional author) + "" + (let ((overlays (rudel-overlays-at + position #'rudel-author-overlay-p))) + ;; There can only be one rudel overlay at any given position + (when overlays + (when (or (not author) + (eq (rudel-overlay-user (car overlays)) author)) + (car overlays)))) + ) + +(defun rudel-author-overlays-in (start end &optional author) + "" + (rudel-overlays-in + start end + (lambda (overlay) + (and (rudel-overlay-p overlay) + (or (not author) + (eq (rudel-overlay-user overlay) author))))) + ) + +(defun rudel-make-author-overlay (buffer from to author) + "Make and return an overlay for the range FROM to TO in BUFFER suitable for contributions by AUTHOR. +AUTHOR has to be an object of type rudel-user-child." + (let ((overlay (make-overlay from to buffer t))) + (rudel-overlay-author-set-properties overlay author) + overlay)) + +(defun rudel-overlay-author-set-properties (overlay author) + "Set properties of OVERLAY according to slots of AUTHOR. +AUTHOR has to be an object of type rudel-user-child." + (with-slots ((name :object-name) color) author + (overlay-put overlay :rudel 'author) + (overlay-put overlay :user author) + (overlay-put overlay 'face (when rudel-overlay-author-display + (rudel-overlay-make-face + (rudel-overlay-make-face-symbol + 'author name) + 'rudel-author-overlay-face + color))) + (overlay-put overlay 'help-echo (when rudel-overlay-author-display + (format "Written by %s" name)))) + ) + +(defun rudel-overlay-author-update (overlay) + "Update properties of OVERLAY from its attached user object." + (let ((author (rudel-overlay-user overlay))) + (rudel-overlay-author-set-properties overlay author))) + + +;;; Update functions for author overlays +;; + +(defun rudel-update-author-overlay-after-insert (buffer position length author) + "Update author overlays in BUFFER to incorporate an insertion of length LENGTH at POSITION by AUTHOR. +POSITION refers to an Emacs buffer position. +AUTHOR has to be an object of type rudel-author-child." + (when author + (with-current-buffer buffer + (let* ((end (+ position length)) + (before (when (> position 1) + (rudel-author-overlay-at (- position 1) author))) + (at (rudel-author-overlay-at position)) + (after (when (< end (point-max)) + (rudel-author-overlay-at (+ end 1) author)))) + (cond + ;; If there is an overlay, we have to split it unless the + ;; author is AUTHOR or we are on its boundary. + (at + (unless (eq (rudel-overlay-user at) author) + (let* ((on-start (= (overlay-start at) position)) + (on-end (= (- (overlay-end at) 1) position)) + (before (unless on-start + (if on-end at (copy-overlay at)))) + (after (unless on-end at))) + (when before + (move-overlay before (overlay-start before) position)) + (when after + (move-overlay after end (overlay-end after))) + (rudel-make-author-overlay buffer position end author)))) + ;; There is no overlay under the insert, but there are + ;; overlays of the same author immediately before and after + ;; the insert. We merge these two into one large overlay + ;; including the insert. + ((and before after) + (let ((end (overlay-end after))) + (delete-overlay after) + (move-overlay before (overlay-start before) end))) + ;; If there is an overlay of the same author before the + ;; insert, we extend it. + (before + (move-overlay before (overlay-start before) end)) + ;; If there is an overlay of the same author after the + ;; insert, we extend it. + (after + (move-overlay after position (overlay-end after))) + ;; If there are no overlays at all, we create a suitable one. + (t + (rudel-make-author-overlay buffer position end author)))))) + ) + +(defun rudel-update-author-overlay-after-delete (buffer position length author) + "Update author overlays in BUFFER to incorporate a deletion of length LENGTH at POSITION by AUTHOR. +POSITION refers to an Emacs buffer position. +AUTHOR has to be an object of type rudel-author-child." + (with-current-buffer buffer + (mapc + (lambda (overlay) + (when (zerop (rudel-overlay-length overlay)) + (delete-overlay overlay))) + (rudel-author-overlays-in position position))) + ) + + +;;; Miscellaneous functions +;; + +(defun rudel-overlay-make-face-symbol (category name) + "Allocate a symbol for a face for CATEGORY and NAME." + (intern (format "rudel-%s-overlay-%s-face" + (if (stringp category) + category + (symbol-name category)) + name))) + +(defun rudel-overlay-make-face (face template color) + "Copy TEMPLATE to FACE and replace color attributes with COLOR. +TEMPLATE has to be a face. FACE can be nil or a face. In the +latter case, FACE is returned unmodified." + (unless (facep face) + (make-face face) + (copy-face template face) + (rudel-overlay-set-face-attributes face color)) + face) + +(defun rudel-overlay-set-face-attributes (face color) + "Set color-related attributes of FACE with respect to COLOR." + (when (facep face) + (dolist (property '(:foreground :background :underline :overline)) + (unless (eq (face-attribute face property) 'unspecified) + (set-face-attribute face nil property color))))) + +(defun rudel-overlay-options-changed () + "Update Rudel overlays after a change of customization options." + (dolist (buffer (buffer-list)) + (with-current-buffer buffer + (mapc #'rudel-overlay-author-update (rudel-overlays))))) + +(provide 'rudel-overlay) +;;; rudel-overlay.el ends here diff --git a/emacs.d/lisp/rudel/.svn/text-base/rudel-protocol.el.svn-base b/emacs.d/lisp/rudel/.svn/text-base/rudel-protocol.el.svn-base new file mode 100644 index 0000000..c60718c --- /dev/null +++ b/emacs.d/lisp/rudel/.svn/text-base/rudel-protocol.el.svn-base @@ -0,0 +1,83 @@ +;;; rudel-protocol.el --- Interface implemented by Rudel protocol backends +;; +;; Copyright (C) 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Rudel, backend, protocol +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; This file contains the interface for Rudel protocol backends. This +;; interface consists of the following functions: +;; +;; + joining sessions +;; + `rudel-ask-join-info' +;; + `rudel-join' +;; + hosting sessions +;; + `rudel-ask-host-info' +;; + `rudel-host' +;; + factory functions +;; + `rudel-make-document' + + +;;; History: +;; +;; 0.1 - Initial revision + + +;;; Code: +;; + +(require 'eieio) + +(require 'rudel-backend) + + +;;; Class rudel-protocol-backend +;; + +(defclass rudel-protocol-backend (rudel-backend) + () + "Interface implemented by protocol backends." + :abstract t) + +(defgeneric rudel-ask-join-info ((this rudel-protocol-backend)) + "Retrieve information for joining a session from user. +Return a property list that contains the collected information.") + +(defgeneric rudel-join ((this rudel-protocol-backend) info) + "Create a new connection according to the data in the property list INFO. +Implementations can rely on the fact that the property :session +contains the `rudel-session' object to which the new connection +will be associated.") + +(defgeneric rudel-ask-host-info ((this rudel-protocol-backend)) + "Retrieve information for hosting a session from user. +Return a property list that contains the collected information.") + +(defgeneric rudel-host ((this rudel-protocol-backend) info) + "Create a new session according to the property list INFO.") + +(defgeneric rudel-make-document ((this rudel-protocol-backend) + name session) + "Create a new document object named NAME for SESSION.") + +(provide 'rudel-protocol) +;;; rudel-protocol.el ends here diff --git a/emacs.d/lisp/rudel/.svn/text-base/rudel-session-initiation.el.svn-base b/emacs.d/lisp/rudel/.svn/text-base/rudel-session-initiation.el.svn-base new file mode 100644 index 0000000..395ab07 --- /dev/null +++ b/emacs.d/lisp/rudel/.svn/text-base/rudel-session-initiation.el.svn-base @@ -0,0 +1,352 @@ +;;; rudel-session-initiation.el --- Session discovery and advertising functions +;; +;; Copyright (C) 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Rudel, session, initiation, service, discovery, advertising +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; Interfaces for session initiation and discovery. +;; +;; The central interface is +;; `rudel-session-initiation-backend'. Backends implementing this +;; interface can provide methods to discover sessions, to advertise +;; sessions, or both. +;; +;; The client programming interface consists of a priority which is +;; one of: +;; +;; + `primary' +;; + `fallback' +;; +;; and the following functions: +;; +;; + `rudel-session-initiation-discover' +;; + `rudel-session-initiation-advertise' +;; + `rudel-session-initiation-withdraw' + + +;;; History: +;; +;; 0.1 - Initial revision + + +;;; Code: +;; + +(eval-when-compile + (require 'cl)) + +(require 'eieio) + +(require 'rudel-backend) + + +;;; Customization options +;; + +(defcustom rudel-configured-sessions nil + "List of configured sessions. + +Each session is described as a plist (a list of keys and values +see Info node `(elisp)Property Lists'). Keys are specified using +keywords and look like this: :host, :username, :color. Values are +mostly strings, but symbols and numbers are possible as well. + +The following keys are required for any session: + +* :name (string) +* :backend (string or symbol) + +Other keys are optional and depend on the selected +backend. Required keys for which no value is specified will be +prompted for when selecting the session. The values of the :name +properties have to be distinct for all configured sessions. + +Additional keys required by most backends: + +* :host (string) +* :port (number) +* :username (string) +* :color (string) + +Here is a complete example of customized values for the obby +backend: + +* :name \"sonian\" +* :backend obby +* :host \"sobby\" +* :port 6522 +* :encryption t +* :username \"phil\" +* :color \"white\" +* :global-password \"\" (this means \"no password\") +* :user-password \"\" + +The programmatic equivalent looks like this: + +(add-to-list + 'rudel-configured-sessions + (list :name \"myserver\" + :backend 'obby + :host \"my.sobby-server.net\" + :username user-login-name + ;; Use M-x list-colors-display to see color choices. + :color \"white\" + :encryption t + :port 6522 + ;; empty string means no password + :global-password \"\" + :user-password \"\"))" + :group 'rudel + :type '(repeat :tag "Connections" + (plist :tag "Connection" + :options ((:name string) + (:backend symbol) + (:username string) + (:color color)))) + ) + + +;;; Variables and constants +;; + +(defvar rudel-session-discovered-hook nil + "This hook is run when collaboration sessions are discovered.") + +(defvar rudel-session-vanished-hook nil + "This hook is run when previously discovered collaboration +session disappear.") + + +;;; Class rudel-session-initiation-backend +;; + +(defclass rudel-session-initiation-backend (rudel-backend) + ((priority :initarg :priority + :type symbol + :accessor rudel-priority + :documentation + "Priority of the session initiation method +implemented by this backend. Has to be either 'primary or +'fallback")) + "Interface implemented by session initiation backends." + :abstract t) + +(defgeneric rudel-discover ((this rudel-session-initiation-backend)) + "Return a list of discovered sessions. +Each list element is a connect info property list. See +`rudel-join-session' for a description of the format of this +list. + +The presence of an implementation of this generic function should +be indicated by the presence of the 'discover' capability.") + +(defgeneric rudel-advertise ((this rudel-session-initiation-backend) info) + "Advertise session described by INFO. +INFO is a connect info property list. See `rudel-host-session' +for a description of the format of this list. + +The presence of an implementation of this generic function should +be indicated by the presence of the 'advertise' capability.") + + +;;; Client programming interface functions. +;; + +(defun rudel-session-initiation-suitable-backends (capability) + "Return primary and fallback backends that have CAPABILITY. +The returned list is of the form (PRIMARY FALLBACK), where +PRIMARY and FALLBACK are lists of backends of the respective +priority." + (let* (;; Select all backends, which can discover sessions + (suitable-backends (rudel-backend-suitable-backends + 'session-initiation + (lambda (backend) + (rudel-capable-of-p backend capability)))) + ;; Select primary backends + (primary-backends (remove* + 'fallback suitable-backends + :key (lambda (backend) + (rudel-priority (cdr backend))))) + ;; Select fallback backends + (fallback-backends (remove* + 'primary suitable-backends + :key (lambda (backend) + (rudel-priority (cdr backend)))))) + (list primary-backends fallback-backends)) + ) + +(defun rudel-session-initiation-discover (&optional backend-name) + "Return a list of session using BACKEND-NAME when non-nil. +BACKEND-NAME is a symbol. When it is non-nil, only the specified +backend is used to discover session. + +The returned list is of the form (INFO-1 ... INFO-N FALLBACK-1 +... FALLBACK-M) where INFO-I are connect info property lists (see +`rudel-join-session') and FALLBACK-I are conses of the form (NAME +. CLASS-OR-OBJECT) that specify fallback backends." + (multiple-value-bind (primary-backends fallback-backends) + (rudel-session-initiation-suitable-backends 'discover) + ;; Retrieve session list from primary backend and fall back to + ;; fallback backends if the list is empty. + (if backend-name + (let ((backend (find backend-name fallback-backends :key #'car))) + (rudel-discover (cdr backend))) + (let ((primary-results + (remove-if #'null + (apply #'append + (mapcar #'rudel-discover + (mapcar #'cdr primary-backends)))))) + (append primary-results fallback-backends)))) + ) + +(defun rudel-session-initiation-advertise (info) + "Advertise the session described by INFO. +INFO is a connect info property list. See `rudel-host-session' +for a description of the format of this list. + +Primary backends are tried first. If none succeeds, fallback +backends are tried. + +The result is non-nil if at least one backend was able to +advertise the session." + (multiple-value-bind (primary-backends fallback-backends) + (rudel-session-initiation-suitable-backends 'advertise) + (or ;; Try to advertise the session using primary backends. + (some (mapcar (lambda (backend) + (rudel-advertise backend info)) + (mapcar #'cdr primary-backends))) + ;; When the primary backends fail, try to advertise the + ;; session using fallback backends + (some (mapcar (lambda (backend) + (rudel-advertise backend info)) + (mapcar #'cdr fallback-backends))))) + ) + + +;;; Class rudel-ask-protocol-backend +;; + +(defconst rudel-ask-protocol-version '(0 1) + "Version of the ask-protocol backend for Rudel.") + +;;;###autoload +(defclass rudel-ask-protocol-backend (rudel-session-initiation-backend) + ((capabilities :initform (discover)) + (priority :initform fallback)) + "This fallback backend can \"discover\" sessions by letting the +user select a suitable backend and asking for connect information +required by the chosen backend.") + +(defmethod initialize-instance ((this rudel-ask-protocol-backend) + &rest slots) + "Set backend version." + (when (next-method-p) + (call-next-method)) + + (oset this :version rudel-ask-protocol-version)) + +(defmethod rudel-discover ((this rudel-ask-protocol-backend)) + "\"Discover\" sessions by asking the user about the backend to use and the connect info." + (let ((backend (rudel-backend-choose + 'protocol + (lambda (backend) + (rudel-capable-of-p backend 'join))))) + (list (append (list :name "asked" + :backend backend) + (rudel-ask-connect-info (cdr backend))))) + ) + +;;;###autoload +(rudel-add-backend (rudel-backend-get-factory 'session-initiation) + 'ask-protocol 'rudel-ask-protocol-backend) + + +;;; Class rudel-configured-sessions-backend +;; + +(defconst rudel-configured-sessions-version '(0 1) + "Version of the configured-sessions backend for Rudel.") + +;;;###autoload +(defclass rudel-configured-sessions-backend + (rudel-session-initiation-backend) + ((capabilities :initform (discover)) + (priority :initform primary)) + "This fallback backend can \"discover\" sessions the user has +configured using customization.") + +(defmethod initialize-instance ((this rudel-configured-sessions-backend) + &rest slots) + "Set backend version." + (when (next-method-p) + (call-next-method)) + + (oset this :version rudel-configured-sessions-version)) + +(defmethod rudel-discover ((this rudel-configured-sessions-backend)) + "\"Discover\" sessions the has configured." + ;; Iterate over all configured sessions in order to make + ;; adjustments. + (mapcar #'rudel-session-initiation-adjust-info + rudel-configured-sessions)) + +;;;###autoload +(rudel-add-backend (rudel-backend-get-factory 'session-initiation) + 'configured-sessions 'rudel-configured-sessions-backend) + + +;;; Miscellaneous functions +;; + +(defun rudel-session-initiation-adjust-info (info) + "Resolve arguments that need resolving in INFO." + ;; Start with a new, empty property list. + (let ((adjusted-info) + (key (car info)) + (value (cadr info)) + (rest info)) + ;; Iterate over all properties in INFO. + (while rest + (setq rest (cddr rest)) + (cond + ;; Resolve backend arguments. + ((eq key :backend) + (let ((backend (rudel-backend-get 'protocol + (if (stringp value) + (intern value) + value)))) + (push backend adjusted-info) + (push key adjusted-info))) + ;; Keep other arguments unmodified. + (t + (push value adjusted-info) + (push key adjusted-info))) + ;; Advance to next key value pair. + (setq key (car rest) + value (cadr rest))) + ;; Return the transformed session information. + adjusted-info) + ) + +(provide 'rudel-session-initiation) +;;; rudel-session-initiation.el ends here diff --git a/emacs.d/lisp/rudel/.svn/text-base/rudel-speedbar.el.svn-base b/emacs.d/lisp/rudel/.svn/text-base/rudel-speedbar.el.svn-base new file mode 100644 index 0000000..8c2caa8 --- /dev/null +++ b/emacs.d/lisp/rudel/.svn/text-base/rudel-speedbar.el.svn-base @@ -0,0 +1,214 @@ +;;; rudel-speedbar.el --- Speedbar rendering of Rudel objects +;; +;; Copyright (C) 2008, 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: rudel, collaboration, speedbar +;; X-RCS: $Id: rudel-speedbar.el,v 1.32 2008/10/10 21:47:28 zappo Exp $ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + +;;; Commentary: +;; +;; This implements rendering of Rudel objects in speedbar. + + +;;; History: +;; +;; 0.1 - Initial revision. + + +;;; Code: +;; + +(require 'speedbar) +(require 'eieio-speedbar) + + +;;; Class rudel-user methods +;; + +(defmethod eieio-speedbar-description ((this rudel-user)) + "Provide a speedbar description for OBJ." + (format "User %s" (object-name-string this))) + +(defmethod eieio-speedbar-object-buttonname ((this rudel-user)) + "Return a string to use as a speedbar button for OBJECT." + (format "%s" (object-name-string this))) + + +;;; Class rudel-document methods +;; + +(defmethod eieio-speedbar-description ((this rudel-document)) + "Construct a description for from the name of document object THIS." + (format "Document %s" (object-name-string this))) + +(defmethod eieio-speedbar-object-buttonname ((this rudel-document)) + "Return a string to use as a speedbar button for OBJECT." + (rudel-unique-name this)) + +;;; Speedbar support mode +;; +(defun rudel-speedbar-make-map () + "Make the generic object based speedbar keymap." + (speedbar-make-specialized-keymap)) + +(defvar rudel-speedbar-key-map + (rudel-speedbar-make-map) + "A Generic object based speedbar display keymap.") + +(defvar rudel-speedbar-menu + '([ "Compile" rudel-speedbar-compile-line t]) + "Menu part in easymenu format used in speedbar while browsing objects.") + +(defun rudel-speedbar-toplevel-buttons (dir) + "Return a list of objects to display in speedbar. +Argument DIR is the directory from which to derive the list of objects." + (when rudel-current-session + (with-slots (users documents) rudel-current-session + (append users documents)))) + +(eieio-speedbar-create 'rudel-speedbar-make-map + 'rudel-speedbar-key-map + 'rudel-speedbar-menu + "Collaboration Session" + 'rudel-speedbar-toplevel-buttons) + +;;;###autoload +(defun rudel-speedbar () + "Show connected users and available documents of Rudel session in speedbar." + (interactive) + (speedbar-frame-mode 1) + (speedbar-change-initial-expansion-list "Collaboration Session") + (speedbar-get-focus)) + +;;; Speedbar Project Methods +;; +;; (defun rudel-find-nearest-file-line () +;; "Go backwards until we find a file." +;; (save-excursion +;; (beginning-of-line) +;; (looking-at "^\\([0-9]+\\):") +;; (let ((depth (string-to-number (match-string 1)))) +;; (while (not (re-search-forward "[]] [^ ]" +;; (save-excursion (end-of-line) +;; (point)) +;; t)) +;; (re-search-backward (format "^%d:" (1- depth))) +;; (setq depth (1- depth))) +;; (speedbar-line-token)))) +;; +;; (defmethod eieio-speedbar-derive-line-path ((obj rudel-session) &optional depth) +;; "Return the path to OBJ. +;; Optional DEPTH is the depth we start at." +;; "session" ;(file-name-directory (oref obj file)) +;; ) +;; +;; (defmethod eieio-speedbar-derive-line-path ((obj rudel-session) &optional depth) +;; "Return the path to OBJ. +;; Optional DEPTH is the depth we start at." +;; (let ((proj (rudel-target-parent obj))) +;; ;; Check the type of line we are currently on. +;; ;; If we are on a child, we need a file name too. +;; (save-excursion +;; (let ((lt (speedbar-line-token))) +;; (if (or (eieio-object-p lt) (stringp lt)) +;; (eieio-speedbar-derive-line-path proj) +;; ;; a child element is a token. Do some work to get a filename too. +;; (concat (eieio-speedbar-derive-line-path proj) +;; (rudel-find-nearest-file-line))))))) +;; +;; (defmethod eieio-speedbar-description ((obj rudel-session)) +;; "Provide a speedbar description for OBJ." +;; "bla") ;(rudel-description obj)) +;; +;; +;; (defmethod eieio-speedbar-object-buttonname ((object rudel-project)) +;; "Return a string to use as a speedbar button for OBJECT." +;; (if (rudel-parent-project object) +;; (rudel-name object) +;; (concat (rudel-name object) " " (oref object version)))) +;; +;; +;; (defmethod eieio-speedbar-object-children ((this rudel-project)) +;; "Return the list of speedbar display children for THIS." +;; (condition-case nil +;; (with-slots (subproj targets) this +;; (append subproj targets)) +;; (error nil))) +;; +;; +;; ;;; Generic file management for TARGETS +;; ;; +;; (defun rudel-file-find (text token indent) +;; "Find the file TEXT at path TOKEN. +;; INDENT is the current indentation level." +;; (speedbar-find-file-in-frame +;; (expand-file-name token (speedbar-line-directory indent))) +;; (speedbar-maybee-jump-to-attached-frame)) +;; +;; (defun rudel-create-tag-buttons (filename indent) +;; "Create the tag buttons associated with FILENAME at INDENT." +;; (let* ((lst (speedbar-fetch-dynamic-tags filename))) +;; ;; if no list, then remove expando button +;; (if (not lst) +;; (speedbar-change-expand-button-char ??) +;; (speedbar-with-writable +;; ;; We must do 1- because indent was already incremented. +;; (speedbar-insert-generic-list (1- indent) +;; lst +;; 'rudel-tag-expand +;; 'rudel-tag-find))))) +;; +;; (defun rudel-tag-expand (text token indent) +;; "Expand a tag sublist. Imenu will return sub-lists of specialized tag types. +;; Etags does not support this feature. TEXT will be the button +;; string. TOKEN will be the list, and INDENT is the current indentation +;; level." +;; (cond ((string-match "+" text) ;we have to expand this file +;; (speedbar-change-expand-button-char ?-) +;; (speedbar-with-writable +;; (save-excursion +;; (end-of-line) (forward-char 1) +;; (speedbar-insert-generic-list indent token +;; 'rudel-tag-expand +;; 'rudel-tag-find)))) +;; ((string-match "-" text) ;we have to contract this node +;; (speedbar-change-expand-button-char ?+) +;; (speedbar-delete-subblock indent)) +;; (t (error "Ooops... not sure what to do"))) +;; (speedbar-center-buffer-smartly)) +;; +;; (defun rudel-tag-find (text token indent) +;; "For the tag TEXT in a file TOKEN, goto that position. +;; INDENT is the current indentation level." +;; (let ((file (rudel-find-nearest-file-line))) +;; (speedbar-find-file-in-frame file) +;; (save-excursion (speedbar-stealthy-updates)) +;; ;; Reset the timer with a new timeout when cliking a file +;; ;; in case the user was navigating directories, we can cancel +;; ;; that other timer. +;; ; (speedbar-set-timer speedbar-update-speed) +;; (goto-char token) +;; (run-hooks 'speedbar-visiting-tag-hook) +;; ;;(recenter) +;; (speedbar-maybee-jump-to-attached-frame) +;; )) + +(provide 'rudel-speedbar) + +;;; rudel-speedbar.el ends here diff --git a/emacs.d/lisp/rudel/.svn/text-base/rudel-state-machine.el.svn-base b/emacs.d/lisp/rudel/.svn/text-base/rudel-state-machine.el.svn-base new file mode 100644 index 0000000..d96bcc7 --- /dev/null +++ b/emacs.d/lisp/rudel/.svn/text-base/rudel-state-machine.el.svn-base @@ -0,0 +1,331 @@ +;;; rudel-state-machine.el --- A simple state machine for Rudel +;; +;; Copyright (C) 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Rudel, FSM +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; This is a simple implementation of a finite state machine +;; (FSM). The is modeled by rudel-state-machine class, objects of +;; which contain state objects of classes derived from +;; rudel-state. There are no explicit transition rules, since states +;; specify their successors. + + +;;; History: +;; +;; 0.1 - Initial revision + + +;;; Code: +;; + +(eval-when-compile + (require 'cl)) + +(require 'eieio) + +(require 'rudel-errors) +(require 'rudel-compat) ;; for pulsing progress reporter + + +;;; Errors related to the state machine +;; + +;; rudel-state-error + +(intern "rudel-state-error") + +(put 'rudel-state-error 'error-conditions + '(error + rudel-error rudel-state-error)) + +(put 'rudel-state-error 'error-message + "Invalid state or state transition") + +;; rudel-invalid-successor-state + +(intern "rudel-invalid-successor-state") + +(put 'rudel-invalid-successor-state 'error-conditions + '(error + rudel-error rudel-state-error rudel-invalid-successor-state)) + +(put 'rudel-invalid-successor-state 'error-message + "Invalid successor state in state transition") + +;; rudel-entered-error-state + +(intern "rudel-entered-error-state") + +(put 'rudel-entered-error-state 'error-conditions + '(error + rudel-error rudel-state-error rudel-entered-error-state)) + +(put 'rudel-entered-error-state 'error-message + "Transition to error state") + +;; rudel-no-start-state + +(intern "rudel-no-start-state") + +(put 'rudel-no-start-state 'error-conditions + '(error + rudel-error rudel-state-error rudel-no-start-state)) + +(put 'rudel-no-start-state 'error-message + "No start state specified for state machine") + + +;;; Class rudel-state +;; + +(defclass rudel-state () + () + "A state that can be used in state machines." + :abstract t) + +(defgeneric rudel-accept ((this rudel-state) &rest arguments) + "Executed when the machine receives an event while in state THIS.") + +(defgeneric rudel-enter ((this rudel-state) &rest arguments) + "Executed when the machine switches to state THIS.") + +(defgeneric rudel-leave ((this rudel-state)) + "Executed when the machine leaves state THIS.") + + +;;; Class rudel-state-machine +;; + +(defclass rudel-state-machine () + ((states :initarg :states + :type list ;; alist + :initform nil + :documentation + "A list (NAME . STATE) conses where NAME is a symbol +and STATE is an object of a class derived from rudel-state.") + (state :initarg :state + :type rudel-state-child + :documentation + "The current state of the machine.")) + "A finite state machine.") + +(defmethod initialize-instance :after ((this rudel-state-machine) + &rest slots) + "Set current state of THIS to a proper initial value. +If a start state is specified in the arguments to the +constructor, that state is used. If there is no such state, the +list of states is search for a state named start. If that fails +as well, the first state in the state list is used." + (with-slots (states) this + ;; Find a suitable start state and switch to it. + (let ((start (or (plist-get slots :start) + (car (assoc 'start states)) + (when (length states) + (car (nth 0 states)))))) + (unless start + (signal 'rudel-no-start-state nil)) + ;; Make start state the current state and call send an enter + ;; message. + (let ((start (cdr (assoc start states)))) + (oset this :state start) + (rudel--switch-to-return-value + this start (rudel-enter start))))) + ) + +(defmethod rudel-find-state ((this rudel-state-machine) name) + "Return state object for symbol NAME." + (with-slots (states) this + (cdr (assoc name states)))) + +(defmethod rudel-register-state ((this rudel-state-machine) name state) + "Register STATE and its NAME with THIS state machine." + (object-add-to-list this :states (cons name state) t)) + +(defmethod rudel-register-states ((this rudel-state-machine) states) + "Register STATES with THIS state machine. +STATES is a list of cons cells whose car is a symbol - the name +of the state - and whose cdr is a class." + (dolist (symbol-and-state states) + (destructuring-bind (name . class) symbol-and-state + (rudel-register-state + this name (make-instance class (symbol-name name))))) + ) + +(defmethod rudel-current-state ((this rudel-state-machine) &optional object) + "Return name and, optionally, state object of the current state of THIS. +If OBJECT is non-nil, (NAME . OBJECT) is returned. Otherwise, +just NAME." + (with-slots (states state) this + (let ((state-symbol (car (find state states :key #'cdr :test #'eq)))) + (if object + (cons state-symbol state) + state-symbol))) + ) + +(defmethod rudel-accept ((this rudel-state-machine) &rest arguments) + "Process an event described by ARGUMENTS." + (with-slots (state) this + ;; Let the current state decide which state is next. + (let ((next (apply #'rudel-accept state arguments))) + (cond + ;; If NEXT is nil, a symbol or a state object, we switch states + ;; without passing any data. + ((or (null next) (symbolp next) (rudel-state-child-p next)) + (rudel-switch this next)) + + ;; If NEXT is a list, it contains the symbol of the successor + ;; state and additional data. + ((listp next) + (apply #'rudel-switch this next)) + + ;; Other types cannot be processed. + (t + (signal 'wrong-type-argument (list (type-of next))))))) + ) + +(defmethod rudel-switch ((this rudel-state-machine) next + &rest arguments) + "Leave current state and switch to state NEXT. +ARGUMENTS are passed to the `rudel-enter' method of the successor +state." + (with-slots (states state) this + (cond + ;; When NEXT is a state object, use it. + ((rudel-state-child-p next)) + + ;; When NEXT is nil, stay in the current state. + ((null next) + (setq next state)) + + ;; When NEXT is a symbol (but not nil), look up the corresponding + ;; state. Signal an error, if there is none. + ((symbolp next) + (let ((next-state (assoc next states))) + (unless next-state + (signal 'rudel-invalid-successor-state + (list next '<- state))) + (setq next (cdr next-state)))) + + ;; Other types cannot be processed. + (t + (signal 'wrong-type-argument (list (type-of next))))) + + ;; Unless the successor state is equal to the current state, leave + ;; the current state and switch to the successor. + (if (and (eq state next) + (null arguments)) + ;; Return state + state + ;; Notify (old) current state. + (rudel-leave state) + ;; Update current state. + (setq state next) + ;; Notify (new) current state. Using the call's value as next + ;; state is a bit dangerous since a long sequence of immediate + ;; state switches could exhaust the stack. + (rudel--switch-to-return-value + this state (apply #'rudel-enter state arguments)))) + ) + +(defmethod rudel--switch-to-return-value ((this rudel-state-machine) + state next) + "Switch from STATE to the next state indicated by NEXT. +STATE is the current state. +NEXT can nil, a list or a `rudel-state' object." + (cond + ;; Remain in current state. + ((null next) + state) + ;; NEXT contains next state and arguments to pass to it when + ;; switching. + ((listp next) + (apply #'rudel-switch this next)) + ;; Otherwise NEXT is a `rudel-state' object. + (t + (rudel-switch this next))) + ) + +(defmethod object-print ((this rudel-state-machine) &rest strings) + "Add current state to the string representation of THIS." + (if (slot-boundp this 'state) + (with-slots (state) this + (apply #'call-next-method + this + (format " state: %s" + (object-name-string state)) + strings)) + (call-next-method this " state: #start")) + ) + + +;;; Miscellaneous functions +;; + +(defun rudel-state-wait (machine success-states + &optional error-states callback) + "Repeatedly call CALLBACK until MACHINE is in a state in SUCCESS-STATES or ERROR-STATES. +MACHINE should be of type rudel-state-machine-child or at least +have a method `rudel-get-state'. + +SUCCESS-STATES and ERROR-STATES are lists which contain the +names (as symbols) of success and error states respectively. +This function does not return when MACHINE enters states not in +SUCCESS-STATES or ERROR-STATES. As a result, a deadlock can occur +when MACHINE deadlocks or cycles through states not in either +list infinitely. + +When non-nil, CALLBACK has to be a function that accepts one +argument of the form (SYMBOL . STATE) where SYMBOL is the name +symbol of the current state and STATE is the state object." + ;; Wait until MACHINE enter a state in SUCCESS-STATES or + ;; ERROR-STATES. + (let ((result + (catch 'state-wait + (while t + ;; Retrieve current state. + (destructuring-bind (symbol . state) + (rudel-current-state machine t) + + ;; Check against success and error states. + (when (memq symbol success-states) + (throw 'state-wait (cons 'success (cons symbol state)))) + (when (memq symbol error-states) + (throw 'state-wait (cons 'error (cons symbol state)))) + + ;; Update progress indicator and sleep. + (when callback + (funcall callback (cons symbol state))) + (sleep-for 0.05)))))) + (when callback + (funcall callback t)) + + ;; If MACHINE ended up in an error state, signal + (unless (eq (car result) 'success) + (signal 'rudel-entered-error-state (cdr result))) + ;; Return state + (cdr result)) + ) + +(provide 'rudel-state-machine) +;;; rudel-state-machine.el ends here diff --git a/emacs.d/lisp/rudel/.svn/text-base/rudel-tls.el.svn-base b/emacs.d/lisp/rudel/.svn/text-base/rudel-tls.el.svn-base new file mode 100644 index 0000000..a770851 --- /dev/null +++ b/emacs.d/lisp/rudel/.svn/text-base/rudel-tls.el.svn-base @@ -0,0 +1,201 @@ +;;; rudel-tls.el --- Start TLS protocol. +;; +;; Copyright (C) 2008, 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Rudel, TLS, encryption, starttls, gnutls +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 rudel. If not, see . + + +;;; Commentary: +;; +;; This contains a simple implementation of the so calls Start TLS +;; protocol, which means enabling TLS encryption for an existing +;; network connection. + + +;;; History: +;; +;; 0.1 - Initial revision. + + +;;; Code: +;; + +(require 'format-spec) + +(require 'rudel) +(require 'rudel-util) + + +;;; Customization +;; + +(defcustom rudel-tls-client-program + "gnutls-cli" + "The gnutls client program to use for encrypted connections." + :group 'rudel + :type 'file) + +(defcustom rudel-tls-client-arguments + "--starttls --kx ANON_DH --port %p %h" + "Arguments passed to the gnutls client program." + :group 'rudel + :type 'string) + + +;;; TLS functions +;; + +(defun rudel-tls-make-process (&rest args) + "Make a network process with keyword arguments ARGS. +This function works similar to `make-network-process'. Supported +keyword arguments are :name (ignore), :host, :port, :filter +and :sentinel. The returned process object is suitable for +start-TLS. Once the enclosing protocol indicates TLS encryption +should start, `rudel-tls-start-tls' can be called to enabled TLS +for the network connection." + (let* ((host (plist-get args :host)) ;; TODO clumsy + (port (plist-get args :service)) + (filter (plist-get args :filter)) + (sentinel (plist-get args :sentinel)) + ;; Compile the command to start the TLS binary. + (arguments (format-spec rudel-tls-client-arguments + (format-spec-make + ?h host + ?p (number-to-string port)))) + ;; Start the TLS program. + (process (apply #'start-process + (format "*tls-%s*" host) nil + rudel-tls-client-program + (split-string arguments " ")))) + + ;; Store filter function and attach proxy filter to handle TLS + ;; handshake. + (when filter + (rudel-set-process-object process filter :old-filter)) + (set-process-filter process #'rudel-tls-wait-init) + + ;; Attach sentinel function. + (when sentinel + (set-process-sentinel process sentinel)) + + ;; Mark the process as supporting TLS encryption + (rudel-set-process-object process t :supports-tls) + + process) + ) + +(defun rudel-tls-start-tls (process) + "Enable TLS encryption for the network connection PROCESS. +This only works if PROCESS has been created by +`rudel-tls-make-process'." + ;; Save current filter function. + (rudel-set-process-object + process (process-filter process) :old-filter) + ;; Install TLS handshake filter function and signal program to start + ;; TLS handshake. + (message "tls-start-tls: switching to \"handshake\" filter") + (set-process-filter process #'rudel-tls-wait-handshake) + (signal-process process 'sigalrm) + ) + +(defun rudel-tls-wait-init (process data) + "Is installed as process filter on PROCESS until gnutls is done printing messages." + ;; Retrieve complete lines. + (let ((buffer (rudel-process-object process :buffer))) + (rudel-assemble-line-fragments data buffer) + (rudel-set-process-object process buffer :buffer)) + + (let ((client-data) + (old-filter (rudel-process-object process :old-filter)) + (client-mode)) + + ;; Assemble lines that were not generated by gnutls. It is very + ;; brittle to wait for last line of gnutls output like, but it + ;; cannot be helped. + (rudel-loop-lines data line + (if client-mode + (setq client-data (concat client-data line "\n")) + (when (string-match-p "- Simple Client Mode.*" line) + (setq client-mode t)))) + + ;; When there are any lines not generated by gnutls, + ;; initialization is over. Process the data and install the old + ;; filter function. + (when client-data + (funcall old-filter process client-data)) + (when client-mode + (message "tls-wait-init: switching back to old filter") + (set-process-filter process old-filter))) + ) + +(defun rudel-tls-wait-handshake (process data) + "Is installed as process filter on PROCESS while gnutls is doing the TLS handshake." + ;; Retrieve complete lines. + (let ((buffer (rudel-process-object process :buffer))) + (rudel-assemble-line-fragments data buffer) + (rudel-set-process-object process buffer :buffer)) + + (let ((client-data) + (old-filter (rudel-process-object process :old-filter)) + (client-mode)) + + ;; Assemble lines that were not generated by gnutls. It is very + ;; brittle to wait for last line of gnutls output like, but it + ;; cannot be helped. + (rudel-loop-lines data line + (if client-mode + (setq client-data (concat client-data line "\n")) + (when (string-match-p "- Compression.*" line) + (setq client-mode t)))) + + ;; When there are any lines not generated by gnutls, handshake is + ;; over. Process the data and install `established' filter + ;; function. + (when client-data + (funcall old-filter process client-data)) + (when client-mode + (message "tls-wait-handshake: switching to \"established\" filter") + (set-process-filter process #'rudel-tls-established) + (rudel-set-process-object process t :encryption))) + ) + +(defun rudel-tls-established (process data) + "Is installed as process filter on PROCESS after gnutls has established TLS encryption." + ;; Retrieve complete lines. + (let ((buffer (rudel-process-object process :buffer))) + (rudel-assemble-line-fragments data buffer) + (rudel-set-process-object process buffer :buffer)) + + (let ((client-data) + (old-filter (rudel-process-object process :old-filter))) + + ;; Assemble lines that were not generated by gnutls. + (rudel-loop-lines data line + (unless (string-match-p "- Peer has closed the GNUTLS connection" line) + (setq client-data (concat client-data line "\n")))) + + ;; When there are any lines not generated by gnutls, pass those to + ;; the old filter function. + (when client-data + (funcall old-filter process client-data))) + ) + +(provide 'rudel-tls) +;;; rudel-tls.el ends here diff --git a/emacs.d/lisp/rudel/.svn/text-base/rudel-transport.el.svn-base b/emacs.d/lisp/rudel/.svn/text-base/rudel-transport.el.svn-base new file mode 100644 index 0000000..8f0b21d --- /dev/null +++ b/emacs.d/lisp/rudel/.svn/text-base/rudel-transport.el.svn-base @@ -0,0 +1,72 @@ +;;; rudel-transport.el --- Rudel transport interface and backend +;; +;; Copyright (C) 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Rudel, backend, transport +;; X-RCS: $Id:$ +;; +;; This program 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. +;; +;; This program 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 this program. If not, see . + + +;;; Commentary: +;; +;; This file contains the interface for Rudel transport backends. + + +;;; History: +;; +;; 0.1 - Initial revision + + +;;; Code: +;; + +(require 'eieio) + +(require 'rudel-backend) + + +;;; Class rudel-transport +;; + +(defclass rudel-transport () + () + "Interface for transport objects.") + +(defgeneric rudel-transport-send ((this rudel-transport) data) + "Send DATA through THIS transport object.") + +(defgeneric rudel-transport-set-handler ((this rudel-transport) + handler) + "Install HANDLER as dispatcher for messages received by THIS.") + + +;;; Class rudel-transport-backend +;; + +(defclass rudel-transport-backend (rudel-backend) + () + "Interface implemented by transport backends." + :abstract t) + +(defgeneric rudel-make-connection ((this rudel-transport-backend) info) + "Create a transport object according to INFO.") + +(defgeneric rudel-wait-for-connections ((this rudel-transport-backend) + info) + "Create a transport object according to INFO.") + +(provide 'rudel-transport) +;;; rudel-transport.el ends here diff --git a/emacs.d/lisp/rudel/.svn/text-base/rudel-util.el.svn-base b/emacs.d/lisp/rudel/.svn/text-base/rudel-util.el.svn-base new file mode 100644 index 0000000..12a967c --- /dev/null +++ b/emacs.d/lisp/rudel/.svn/text-base/rudel-util.el.svn-base @@ -0,0 +1,261 @@ +;;; rudel-util.el --- Miscellaneous functions for Rudel +;; +;; Copyright (C) 2008, 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: rudel, miscellaneous, util +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; This file contains miscellaneous functions for Rudel. + + +;;; History: +;; +;; 0.1 - Initial revision. + + +;;; Code: +;; + +(eval-when-compile + (require 'cl)) + +(require 'eieio) + +(require 'rudel-errors) + + +;;; Errors +;; + +;; rudel-dispatch-error + +(intern "rudel-dispatch-error") + +(put 'rudel-dispatch-error 'error-conditions + '(error + rudel-error rudel-dispatch-error)) + +(put 'rudel-dispatch-error 'error-message + "Could not dispatch message to handler") + + +;;; Class rudel-hook-object +;; + +(defclass rudel-hook-object () + () + "Mixin for classes that offer one or more hooks for each of +their objects. + +This idiom is usually called something like signal/slot or +event/subscription, but for Emacs, the notion of hooks seems more +appropriate." + :abstract t) + +(defmethod object-add-hook ((this rudel-hook-object) + hook function &optional append) + "Add FUNCTION to HOOK for THIS. +If APPEND is non-nil FUNCTION becomes the last element in the +list of hooks." + (let ((value (slot-value this hook))) + (unless (member function value) + (set-slot-value this hook + (if append (append value (list function)) + (cons function value))))) + ) + +(defmethod object-remove-hook ((this rudel-hook-object) + hook function) + "Remove FUNCTION from HOOK for THIS." + (set-slot-value this hook + (remove function (slot-value this hook)))) + +(defmethod object-run-hook-with-args ((this rudel-hook-object) + hook &rest arguments) + "Run HOOK of THIS with arguments ARGUMENTS." + (let ((hook (slot-value this hook))) + (apply #'run-hook-with-args 'hook this arguments))) + + +;;; Class rudel-socket-owner +;; + +(defclass rudel-socket-owner () + ((socket :initarg :socket + :type process + :documentation + "The process object representing the socket through +which the communication happens.")) + "Class rudel-socket-owner ") + +(defmethod initialize-instance :after ((this rudel-socket-owner) + &rest slots) + "Attach THIS to as process object of our socket." + ;; Attach to our socket. + (with-slots (socket) this + (rudel-set-process-object socket this)) + ) + +(defmethod rudel-disconnect ((this rudel-socket-owner)) + "Disconnect the network connection owned by THIS." + (with-slots (socket) this + (delete-process socket))) + +(defmethod rudel-state-change ((this rudel-socket-owner) state message) + "Called when the state of THIS changes to STATE. +MESSAGE is the message emitted when the state transition +occurred." + (with-slots (socket) this + (case state + ;; Nothing to do here. + (run + nil) + + ;; Dispatch events which indicate the termination of the + ;; connection to `rudel-close'. + ((closed failed exit) + (rudel-close this)))) + ) + +(defmethod rudel-close ((this rudel-socket-owner)) + "Called when the connection associated to THIS is closed.") + + +;;; Networking helper functions and macros +;; + +(defun rudel-process-object (process &optional key) + "Return the object attached to PROCESS using identifier KEY." + (unless key + (setq key :object)) + (get (intern (process-name process)) key)) + +(defun rudel-set-process-object (process object &optional key) + "Set object attached to PROCESS using identifier KEY to OBJECT." + (unless key + (setq key :object)) + (put (intern (process-name process)) key object)) + +(defun rudel-filter-dispatch (process data) + "Call `rudel-receive' method of object attached to PROCESS with DATA." + (let ((object (rudel-process-object process))) + (rudel-receive object data))) + +(defun rudel-sentinel-dispatch (process message) + "Call `rudel-state-change' method of the object attached to PROCESS with state and MESSAGE." + (let ((object (rudel-process-object process)) + (state (process-status process))) + (rudel-state-change object state message))) + + +;;; Fragmentation and assembling functions. +;; + +(defmacro rudel-assemble-line-fragments (data storage) + "Find an return complete lines in DATA, store excess data in STORAGE. +If STORAGE is non-nil when calling, consider content as leftover +data from last and concatenate with DATA before processing." + (declare (debug (form form))) + (let ((index (make-symbol "index"))) + `(progn + ;; If there are stored fragments, append them to the new data. + (when ,storage + (setq ,data (concat ,storage ,data)) + (setq ,storage nil)) + ;; Try to find a line break in the augmented data. + (let ((,index (position ?\n ,data :from-end t))) + (unless (and ,index (eq ,index (- (length ,data) 1))) + (setq ,storage (if ,index + (substring ,data (+ ,index 1)) + ,data)) + (setq ,data (when ,index + (substring ,data 0 (+ ,index 1)))))) + ,data)) + ) + +(defmacro rudel-loop-lines (data var &rest forms) + "Execute FROMS with VAR subsequently bound to all lines in DATA." + (declare (indent 2) + (debug (form symbolp &rest form))) + (let ((lines (make-symbol "lines"))) + `(when ,data + (let ((,lines (split-string ,data "\n" t))) + (dolist (,var ,lines) + (progn ,@forms))))) + ) + +(defmacro rudel-loop-chunks (data var size &rest forms) + "Execute FROMS in a loop with VAR bound to chunks of DATA of SIZE. +Unless (zerop (mod (length data) size) 0) the final chunk is +truncated. The expression SIZE is evaluated in each loop unless +it is a number." + (declare (indent 3) + (debug (form symbolp numberp &rest form))) + ;; If we got a constant number as SIZE, we can check right away. + (when (and (numberp size) (<= size 0)) + (error "Size should be positive")) + + (let ((rest (make-symbol "rest")) + (amount (make-symbol "amount")) + ;; If SIZE has to be evaluated, we have to check at runtime. + (check (unless (numberp size) + `((when (<= ,size 0) + (error "Size should be positive")))))) + `(progn + ,@check + (let ((,rest ,data) + (,var) + (,amount)) + (while (not (string= ,rest "")) + (setq ,amount (min (length ,rest) ,size) + ,var (substring ,rest 0 ,amount) + ,rest (substring ,rest ,amount)) + (progn ,@forms))))) + ) + + +;;; Miscellaneous functions +;; + +(defun rudel-dispatch (object prefix name arguments) + "Call method (concat PREFIX NAME) of OBJECT with ARGUMENTS. +If no such method can be found, the condition +rudel-dispatch-error is signalled." + ;; Construct a matching symbol. + (let* ((method (intern-soft (concat prefix name)))) + ;; If we found a suitable method, run it; Otherwise signal. + (unless method + (signal 'rudel-dispatch-error 'method-symbol-unbound)) + (condition-case error + ;; Try to call METHOD. This can still fail when METHOD is not + ;; defined for the class of OBJECT. + (apply method object arguments) + ;; Only handle a condition 'no-method-definition' that refers to + ;; METHOD, otherwise continue unwinding. + (no-method-definition + (if (eq method (cadr error)) + (signal 'rudel-dispatch-error 'no-method-for-object) + (signal (car error) (cdr error)))))) + ) + +(provide 'rudel-util) +;;; rudel-util.el ends here diff --git a/emacs.d/lisp/rudel/.svn/text-base/rudel.el.svn-base b/emacs.d/lisp/rudel/.svn/text-base/rudel.el.svn-base new file mode 100644 index 0000000..88603fa --- /dev/null +++ b/emacs.d/lisp/rudel/.svn/text-base/rudel.el.svn-base @@ -0,0 +1,1012 @@ +;;; rudel.el --- A collaborative editing framework for Emacs +;; +;; Copyright (C) 2008, 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: rudel, collaboration +;; URL: http://rudel.sourceforge.net/ +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 rudel. If not, see . + + +;;; Commentary: +;; +;; Rudel is a framework for collaborative editing in Emacs. Its +;; architecture allows communication with arbitrary collaborative +;; editors. + + +;;; History: +;; +;; 0.1 - Initial revision. + + +;;; Code: +;; + +(eval-when-compile + (require 'cl)) + +(require 'eieio) +(require 'eieio-base) + +(require 'eieio-speedbar) ;; TODO required for now + +(require 'rudel-util) +(require 'rudel-backend) +(require 'rudel-session-initiation) +(require 'rudel-operations) +(require 'rudel-operators) +(require 'rudel-overlay) +(require 'rudel-hooks) +(require 'rudel-interactive) ;; for `rudel-read-backend', + ;; `rudel-read-document', + ;; `rudel-read-session' +(require 'rudel-icons) +(require 'rudel-compat) ;; for `read-color' replacement + + +;;; Global variables +;; + +(defconst rudel-version '(0 3) + "Version of the Rudel framework.") + +(defvar rudel-current-session nil + "Global object representing the current Rudel session. +nil if there is no active session.") + +(defvar rudel-buffer-document nil + "Buffer-local variable which holds the Rudel document associated with the buffer.") +(make-variable-buffer-local 'rudel-buffer-document) +(put 'rudel-buffer-document 'permanent-local t) + +(defvar rudel-buffer-change-workaround-data nil + "Buffer-local variable which holds change data that could not be accessed otherwise. +It would be nice to find another way to do this.") +(make-variable-buffer-local 'rudel-buffer-change-workaround-data) +(put 'rudel-buffer-change-workaround-data 'permanent-local t) + + +;;; Customization +;; + +(defgroup rudel nil + "Rudel collaborative editing framework." + :group 'applications) + +(defcustom rudel-allocate-buffer-function + 'rudel-allocate-buffer-clear-existing + "A function used to find or create buffers to associate to documents. +The function is called with the document name as the sole +argument and has to return a buffer object which will be attached +to the document in question." + :group 'rudel + :type '(choice (const :tag "Clear content of existing buffer" + rudel-allocate-buffer-clear-existing ) + (const :tag "Create a new uniquely named buffer" + rudel-allocate-buffer-make-unique ) + (function :tag "Other function")) + :require 'rudel-interactive) + +(defcustom rudel-default-username (user-login-name) + "*" + :group 'rudel + :type '(string)) + + +;;; Class rudel-session +;; + +(defclass rudel-session (rudel-hook-object) + ((backend :initarg :backend + :type rudel-backend-child + :documentation + "The backend used by this session.") + (users :initarg :users + :type list + :initform nil + :documentation + "The list of users participating in this +session.") + (documents :initarg :documents + :type list + :initform nil + :documentation + "This list of documents available in +this session.") + ;; Hooks + (end-hook :initarg :end-hook + :type list + :initform nil + :documentation + "") + (add-user-hook :initarg :add-user-hook + :type list + :initform nil + :documentation + "This hook is run when a user gets added +to the session. +The arguments are the session and the user object.") + (remove-user-hook :initarg :remove-user-hook + :type list + :initform nil + :documentation + "This hook is run when a user gets +removed from the session. +The arguments are the session and the user object.") + (add-document-hook :initarg :add-document-hook + :type list + :initform nil + :documentation + "This hook is run when a document gets +added to the session. +The arguments are the session and the document object.") + (remove-document-hook :initarg :remove-document-hook + :type list + :initform nil + :documentation + "This hook is run when a document gets +removed from the session. +The arguments are the session and the document object.")) + "This class serves as a base class for rudel-client-session and +rudel-server-session. Consequently, it consists of slots common +to client and server sessions." + :abstract t) + +(defmethod rudel-end ((this rudel-session)) + "Terminate THIS session performing all necessary cleanup." + ;; Run the hook. + (object-run-hook-with-args this 'end-hook)) + +(defmethod rudel-add-user ((this rudel-session) user) + "Add USER to the user list of THIS session. + +Runs object hook (see `rudel-hook-object') `add-user-hook' with +arguments THIS and USER." + ;; Add USER to list. + (object-add-to-list this :users user) + + ;; Run the hook. + (object-run-hook-with-args this 'add-user-hook user)) + +(defmethod rudel-remove-user ((this rudel-session) user) + "Remove USER from the user list of THIS session. + +Runs object hook (see `rudel-hook-object') `remove-user-hook' +with arguments THIS and USER." + ;; Remove USER from list. + (object-remove-from-list this :users user) + + ;; Run the hook. + (object-run-hook-with-args this 'remove-user-hook user)) + +(defmethod rudel-find-user ((this rudel-session) + which &optional test key) + "Find user WHICH in the user list. +WHICH is compared to the result of KEY using TEST." + (unless test + (setq test #'string=)) + (unless key + (setq key #'object-name-string)) + (with-slots (users) this + (find which users :key key :test test)) + ) + +(defmethod rudel-add-document ((this rudel-session) document) + "" + (unless (slot-boundp document :session) + (oset document :session this)) + + ;; Add DOCUMENT to the list of documents in THIS session. + (object-add-to-list this :documents document) + + ;; Run the hook. + (object-run-hook-with-args this 'add-document-hook document)) + +(defmethod rudel-remove-document ((this rudel-session) document) + "Remove DOCUMENT from THIS session, detaching it if necessary." + ;; Detach document from its buffer when necessary. + (when (rudel-attached-p document) + (rudel-detach-from-buffer document)) + + ;; Remove DOCUMENT from the list of documents in THIS session. + (object-remove-from-list this :documents document) + + ;; Run the hook. + (object-run-hook-with-args this 'remove-document-hook document)) + +(defmethod rudel-find-document ((this rudel-session) + which &optional test key) + "Find document WHICH in the document list. +WHICH is compared to the result of KEY using TEST." + (unless test + (setq test #'string=)) + (unless key + (setq key #'object-name-string)) + (with-slots (documents) this + (find which documents :key key :test test)) + ) + +(defmethod rudel-unsubscribed-documents ((this rudel-session)) + "" + (unless (slot-boundp this :self) + (error "Cannot find unsubscribed documents unless slot self is bound")) + (with-slots (documents self) this + (remove-if + (lambda (document) + (with-slots (subscribed) document + (memq self subscribed))) + documents)) + ) + + +;;; Class rudel-client-session +;; +(defclass rudel-client-session (rudel-session) + ((connection :initarg :connection + :type (or null rudel-connection-child) + :initform nil + :documentation + "The connection used for communication by this +session.") + (self :initarg :self + :type rudel-user-child + :documentation + "Points into USERS to the user object representing +the local user")) + "Objects represent a collaborative editing session from a +client perspective.") + +(defmethod rudel-end ((this rudel-client-session)) + ;; Clean everything up + (with-slots (connection users documents) this + ;; Detach all documents from their buffers + (mapc #'rudel-detach-from-buffer documents) + + ;; Terminate the connection + (when connection + (condition-case nil + (rudel-disconnect connection) + (error nil)))) + + ;; + (when (next-method-p) + (call-next-method)) + ) + + +;;; Class rudel-server-session +;; + +(defclass rudel-server-session (rudel-session) + () + "Class rudel-server-session " + :abstract t) + + +;;; Class rudel-connection +;; + +(defclass rudel-connection () + ((session :initarg :session + :type rudel-session-child + :documentation + "")) + "This abstract class defines the interface implementations of +client protocols have to obey." + :abstract t) + +(defgeneric rudel-disconnect ((this rudel-connection)) + "Close the connection.") + +(defgeneric rudel-change-color- ((this rudel-connection) color) ;; TODO name + "") + +(defgeneric rudel-publish ((this rudel-connection) document) + "") + +(defgeneric rudel-subscribe-to ((this rudel-connection) document) + "") + +(defgeneric rudel-unsubscribe-from ((this rudel-connection) document) ; TODO name should be rudel-unsubscribe + "") + +(defgeneric rudel-local-insert ((this rudel-connection)) + "") + +(defgeneric rudel-local-delete ((this rudel-connection)) + "") + +(defgeneric rudel-remote-insert ((this rudel-connection)) + "") + +(defgeneric rudel-remote-delete ((this rudel-connection)) + "") + + +;;; Class rudel-user +;; + +(defclass rudel-user (eieio-named + eieio-speedbar-file-button + rudel-hook-object) + ((color :initarg :color + :accessor rudel-color + :documentation + "Color used to indicate ownership or authorship +by the user. Examples includes text written by the user or the +user name itself.") + (change-hook :initarg :change-hook + :type list + :initform nil + :documentation + "This hook is run when this user object +changes.")) + "Objects of this class represent users participating in +collaborative editing session. Note that a participating user +does not have to be connected to the session at any given time." + :abstract t) + +(defmethod rudel-display-string ((this rudel-user) + &optional use-images align) + "Return a textual representation of THIS for user interface stuff." + (with-slots ((name :object-name) color) this + (propertize + (concat + (when use-images + (propertize "*" 'display rudel-icon-person)) + name) + 'face (list :background color))) + ) + + +;;; Class rudel-document +;; + +(defclass rudel-document (eieio-named + eieio-speedbar-file-button + rudel-hook-object) + ((session :initarg :session + :type rudel-session + :documentation + "") + (buffer :initarg :buffer + :type (or null buffer) + :initform nil + :documentation + "") + (subscribed :initarg :subscribed + :type list + :initform nil + :documentation + "") + ;; Hooks + (subscribe-hook :initarg :subscribe-hook + :type list + :initform nil + :documentation + "This hook is run when a user subscribes to +this document object.") + (unsubscribe-hook :initarg :unsubscribe-hook + :type list + :initform nil + :documentation + "This hook is run when a user unsubscribes +from this document object.") + (attach-hook :initarg :attach-hook + :type list + :initform nil + :documentation + "This hook is run when a buffer is attached +to this document object.") + (detach-hook :initarg :detach-hook + :type list + :initform nil + :documentation + "This hook is run when the attached buffer +is detached from this document object.")) + "This class represents a document, which participants of a +collaborative editing session can subscribe to." + :abstract t) + +(defmethod rudel-unique-name ((this rudel-document)) + "Returns a suggested name for the buffer attached to THIS document." + (object-name-string this)) + +(defmethod rudel-suggested-buffer-name ((this rudel-document)) + "Returns a suggested name for the buffer attached to THIS document." + (rudel-unique-name this)) + +(defmethod rudel-attached-p ((this rudel-document)) + (with-slots (buffer) this + buffer)) + +(defmethod rudel-attach-to-buffer ((this rudel-document) buffer) + "Attach THIS document to BUFFER" + (with-slots ((doc-buffer :buffer)) this + ;; Set buffer slot of THIS to BUFFER and associated THIS with + ;; BUFFER. + (setq doc-buffer buffer) + (rudel-set-buffer-document this buffer) + + (with-current-buffer doc-buffer + ;; Add the handler function for buffer changes to the buffer's + ;; change hook. + (add-hook 'after-change-functions + #'rudel-handle-buffer-change + nil t) + + ;; Store change data before the change a done. This is necessary + ;; because the number of bytes (not characters) cannot otherwise + ;; be recovered after a deletion. + (add-hook 'before-change-functions + #'rudel-buffer-change-workaround + nil t) + + ;; Add a handler to the kill-buffer hook to unsubscribe from the + ;; document when the buffer gets killed. + (add-hook 'kill-buffer-hook + #'rudel-unpublish-buffer + nil t) + + ;; + (add-hook 'change-major-mode-hook + #'rudel-handle-major-mode-change + nil t)) + + ;; Run the hook. + (object-run-hook-with-args this 'attach-hook doc-buffer)) + ) + +(defmethod rudel-detach-from-buffer ((this rudel-document)) + "Detach document THIS from its buffer. +Do nothing, if THIS is not attached to any buffer." + (with-slots (buffer) this + (let ((buffer-save buffer)) + + ;; Only try to detach from BUFFER, if it is non-nil. BUFFER can + ;; be nil, if the user did not subscribe to the document, or + ;; unsubscribed after subscribing. + (when buffer + + (with-current-buffer buffer + ;; Remove our handler function from the kill-buffer hook. + (remove-hook 'kill-buffer-hook + #'rudel-unpublish-buffer + t) + + ;; Remove our handler function from the after-change hook. + (remove-hook 'after-change-functions + #'rudel-handle-buffer-change + t) + + ;; Remove our handler function from the before-change hook. + (remove-hook 'before-change-functions + #'rudel-buffer-change-workaround + t) + + ;; Remove all overlays. + (rudel-overlays-remove-all) + + ;; Remove the major mode change handler. + (remove-hook 'change-major-mode-hook + #'rudel-handle-major-mode-change + t)) + + ;; Unset buffer slot of THIS and delete association of THIS with + ;; BUFFER. + (rudel-set-buffer-document nil buffer) + (setq buffer nil)) + + ;; Run the hook. + (object-run-hook-with-args this 'detach-hook buffer-save))) + ) + +(defmethod rudel-add-user ((this rudel-document) user) + "Add USER to the list of subscribed users of THIS. + +Runs object hook (see `rudel-hook-object') `subscribe-hook' with +arguments THIS and USER." + ;; Add USER to list. + (object-add-to-list this :subscribed user) + + ;; Run the hook. + (object-run-hook-with-args this 'subscribe-hook user)) + +(defmethod rudel-remove-user ((this rudel-document) user) + "Remove USER from the list of subscribed users of THIS. + +Runs object hook (see `rudel-hook-object') `unsubscribe-hook' +with arguments THIS and USER." + ;; Remove USER from list. + (object-remove-from-list document :subscribed user) + + ;; Run the hook. + (object-run-hook-with-args this 'unsubscribe-hook user)) + +(defmethod rudel-insert ((this rudel-document) position data) + "Insert DATA at POSITION into the buffer attached to THIS. +When POSITION is nil `point-max' is used to determine the +insertion position. +Modification hooks are disabled during the insertion." + (with-slots (buffer) this + (save-excursion + (set-buffer buffer) + + (unless position + (setq position (- (point-max) 1))) + + (let ((inhibit-modification-hooks t)) + (goto-char (+ position 1)) + (insert data)))) + ) + +(defmethod rudel-delete ((this rudel-document) position length) + "Delete a region of LENGTH character at POSITION from the buffer attached to THIS. +Modification hooks are disabled during the insertion." + (with-slots (buffer) this + (save-excursion + (set-buffer buffer) + + (let ((inhibit-modification-hooks t)) + (delete-region (+ position 1) (+ position length 1))))) + ) + +(defmethod rudel-local-operation ((this rudel-document) operation) + "Apply the local operation OPERATION to THIS." + (with-slots (session buffer) this + (with-slots (connection (user :self)) session + (dolist (operators (list + + ;; Update overlays + (rudel-overlay-operators + "overlay-operators" + :document this + :user user) + + ;; Notify connection + (rudel-connection-operators + "connection-operators" + :connection connection + :document this))) + + ;; Apply the operation using each set of operators + (rudel-apply operation operators)))) + ) + +(defmethod rudel-remote-operation ((this rudel-document) user operation) + "Apply the remote operation OPERATION performed by USER to THIS." + (dolist (operators (append + + ;; Update buffer contents + (list (rudel-document-operators + "document-operators" + :document this)) + + ;; Update overlays + (when user + (list (rudel-overlay-operators + "overlay-operators" + :document this + :user user))))) + + ;; Apply the operation using each set of operators + (rudel-apply operation operators)) + ) + +(defmethod rudel-chunks ((this rudel-document)) + "Return a list of text chunks of the associated buffer. +Each element in the chunk is a list structured like this (START +END AUTHOR). START and END are numbers, AUTHOR is of type (or +null rudel-user-child)." + (with-slots (buffer) this + ;; Extract buffer string and a list of chunks partitioning the + ;; string according to the respective author (or nil). + (with-current-buffer buffer + (let ((string (buffer-string)) ;; TODO no-properties? + (overlay-chunks (mapcar + (lambda (overlay) + (list (- (overlay-start overlay) 1) + (- (overlay-end overlay) 1) + (rudel-overlay-user overlay))) + (sort* (rudel-author-overlays) + '< :key 'overlay-start))) + (last) + (augmented-chunks)) + + ;; Iterate through the list of chunks to find gaps between + ;; chunks (also before the first) and insert entries with + ;; author nil accordingly. + (dolist (chunk overlay-chunks) + (when (or (and (not last) + (> (nth 0 chunk) 0)) + (and last + (/= (nth 1 last) + (nth 0 chunk)))) + (push (list (if last (nth 1 last) 0) + (nth 0 chunk) + nil) + augmented-chunks)) + (push chunk augmented-chunks) + (setq last chunk)) + + ;; If there is text after the last chunk, create another one + ;; with author nil. If there were no chunks at all, this chunk + ;; can also cover the whole buffer string. + (when (or (and (not last) + (/= (length string) 0)) + (and last + (/= (nth 1 last) (length string)))) + (push (list (if last (nth 1 last) 0) + (length string) + nil) + augmented-chunks)) + + ;; Sort chunks according to the start position. + (sort* augmented-chunks '< :key 'car)))) + ) + + +;;; Buffer-related functions +;; + +(defun rudel-buffer-has-document-p (&optional buffer) + "Return non-nil if a document object is attached to BUFFER. +If BUFFER is nil, use the current buffer." + (unless buffer + (setq buffer (current-buffer))) + + (buffer-local-value 'rudel-buffer-document buffer)) + +(defun rudel-buffer-document (&optional buffer) + "Return the document object attached to BUFFER. +If BUFFER is nil, use the current buffer." + (unless buffer + (setq buffer (current-buffer))) + + (buffer-local-value 'rudel-buffer-document buffer)) + +(defun rudel-set-buffer-document (document &optional buffer) + "Associate BUFFER to DOCUMENT. +If DOCUMENT is nil, make it not associated to any buffer. +If BUFFER is nil, use the current buffer." + (unless buffer + (setq buffer (current-buffer))) + + (with-current-buffer buffer + (setq rudel-buffer-document document))) + +(defun rudel-handle-buffer-change (from to length) + "Handle buffer change at range FROM to TO with length LENGTH by relaying them to the document object of the buffer. +See after-change-functions for more information." + (when (rudel-buffer-has-document-p) + (let ((document (rudel-buffer-document)) + (text)) ; TODO with-rudel-buffer-document? + (cond + ;; The change was an insert + ((and (/= from to) + (zerop length)) + (with-slots (buffer) document + (with-current-buffer buffer + (setq text (buffer-substring-no-properties from to))) + (rudel-local-operation document + (rudel-insert-op + "insert" + :from (- from 1) + :data text)))) + + ;; The change was a delete + ((and (= from to) + (not (zerop length))) + (rudel-local-operation document + (rudel-delete-op + "delete" + :from (- from 1) + :length length))) + + ;; The operation was neither an insert nor a delete. This seems + ;; to mean that the region has changed arbitrarily. The only + ;; option we have is sending a delete and corresponding insert + ;; message that emulate the change. + (t + (with-slots (buffer) document + (with-current-buffer buffer + (setq text (buffer-substring-no-properties from to))) + (rudel-local-operation document + (rudel-delete-op + "delete" + :from (- from 1) + :length length)) + (rudel-local-operation document + (rudel-insert-op + "insert" + :from (- from 1) + :data text))))))) + ) + +(defun rudel-buffer-change-workaround (from to) + (when (/= from to) + (setq rudel-buffer-change-workaround-data + (list from to + (buffer-substring-no-properties from to))))) + + +;;; Protection against major mode changes +;; + +(defvar rudel-mode-changed-buffers nil + "List of buffers that may need to be repaired after a major + mode change.") + +(defun rudel-handle-major-mode-change () + "Store the current buffer to repair damage done by major mode change. + +Note: The way this works is inspired by mode-local.el by David +Ponce and Eric M. Ludlam." + ;; Store the buffer for later repair. + (add-to-list 'rudel-mode-changed-buffers (current-buffer)) + + ;; Schedule `rudel-after-major-mode-change' to run after the + ;; command, that caused the major mode change. + (add-hook 'post-command-hook + #'rudel-after-major-mode-change) + ) + +(defun rudel-after-major-mode-change () + "Repair damage done by major mode changes. +As a function in `post-command-hook', this is run after there was +a `major-mode' change. + +Note: The way this works is inspired by mode-local.el by David +Ponce and Eric M. Ludlam." + ;; Remove this function from `post-command-hook'. + (remove-hook 'post-command-hook + #'rudel-after-major-mode-change) + + ;; Repair all buffers affected by the major mode change. + (dolist (buffer rudel-mode-changed-buffers) + (let ((document (buffer-local-value 'rudel-buffer-document + buffer))) + (rudel-attach-to-buffer document buffer))) + ) + + +;;; Interactive functions +;; + +;;;###autoload +(defun rudel-join-session (info) + "Join the collaborative editing session described by INFO. +INFO is a property list that describes the collaborative editing +session in terms of properties like :host, :port +and :encryption. The particular properties and their respective +meanings depend on the used backend. + +When called interactively, all data required to join a session +will be prompted for." + (interactive + ;; Try the discover method of session initiation backends to find + ;; available sessions. + (list + (let ((info) + (session-initiation-backend)) + (while (not info) + (message "Discovering Sessions ...") + (let* ((sessions (rudel-session-initiation-discover + session-initiation-backend)) + (maybe-info (if (= (length sessions) 1) + (car sessions) + (rudel-read-session + sessions "Choose Session: " 'object)))) + (if (rudel-backend-cons-p maybe-info) + (setq session-initiation-backend (car maybe-info)) + (setq info maybe-info)))) + info))) + + ;; First, create the session object. + (let* ((backend (cdr (plist-get info :backend))) + (session (rudel-client-session + (plist-get info :name) + :backend backend)) + (connection)) + ;; Give the backend a chance to collect remaining connect + ;; info. For session initiation methods like Zeroconf, we have the + ;; _connection_ info, but are still missing the username and + ;; stuff. + (setq info (rudel-ask-connect-info backend info)) + + ;; Add the session object to the connect information. + (plist-put info :session session) + + ;; Ask BACKEND to connect using INFO. Do not catch errors since + ;; the error messages are probably the best feedback we can give. + (setq connection (rudel-connect backend info)) + + ;; Set the connection slot of the session object and store it + ;; globally. + (oset session :connection connection) + (setq rudel-current-session session) + + ;; Reset the global session variable when the session ends. + (object-add-hook session 'end-hook + (lambda (session) + (setq rudel-current-session nil))) + + ;; Run the hook. + (run-hook-with-args 'rudel-session-start-hook session)) + ) + +;;;###autoload +(defun rudel-host-session () + "Host a collaborative editing session. +All data required to host a session will be prompted for +interactively." + (interactive) + ;; If necessary, ask the user for the backend we should use. + (let* ((backend (cdr (rudel-backend-choose + 'protocol + (lambda (backend) + (rudel-capable-of-p backend 'host))))) + (info (rudel-ask-host-info backend)) + (server)) + + ;; Try to create the server + (condition-case error-data + (setq server (rudel-host backend info)) + ('error + (error "Could not host session using backend `%s' with %s: %s" + (object-name-string backend) + info + (car error-data)))) + server)) + +;;;###autoload +(defun rudel-end-session () + "End the current collaborative editing session." + (interactive) + (unless rudel-current-session + (error "No active Rudel session")) + + ;; Actually end the session. + (rudel-end rudel-current-session) + ) + +;;;###autoload +(defun rudel-change-color () + "Change the color associated with the local user. +Not all backends support this operation." + (interactive) + ;; Make sure we have a session. + (unless rudel-current-session + (error "No active Rudel session")) + + (with-slots (backend connection self) rudel-current-session + ;; Make sure the backend can change colors. + (unless (rudel-capable-of-p backend 'change-color) + (error "Backend `%s' cannot change colors" + (object-name-string backend))) + + (with-slots ((name :object-name) color) self + ;; Ask the user for a new color. + (setq color (read-color "New Color: " t)) + + ;; Tell the connection to announce the change and change it in + ;; our user object. + (rudel-change-color- connection color) + + ;; Run the change hook. + (object-run-hook-with-args self 'change-hook) + + ;; Update overlay color. + (rudel-overlay-set-face-attributes + (rudel-overlay-make-face-symbol 'author name) + color))) + ) + +;;;###autoload +(defun rudel-subscribe (document) + "Subscribe to DOCUMENT offered by a peer in a collaborative editing session. +When called interactively, DOCUMENT is prompted for interactively." + (interactive + (list (progn + ;; We have to retrieve the document list from an active + ;; session. + (unless rudel-current-session + (error "No active Rudel session")) + ;; Select unsubscribed documents. + (let ((documents (rudel-unsubscribed-documents + rudel-current-session))) + ;; Already subscribed to all documents. This is an error. + (when (null documents) + (error "No unsubscribed documents")) + ;; Read an unsubscribed document. + (rudel-read-document documents nil 'object))))) + + ;; Make sure we have a session. + (unless rudel-current-session + (error "No active Rudel session")) + + ;; Create a new buffer and attach the document to it. + (let* ((name (rudel-suggested-buffer-name document)) + (buffer (funcall rudel-allocate-buffer-function name))) + (rudel-attach-to-buffer document buffer) + + (let ((connection (oref (oref document :session) :connection))) + (rudel-subscribe-to connection document)) + + ;; Show the new buffer. + (set-window-buffer nil buffer)) + ) + +;;;###autoload +(defun rudel-publish-buffer (&optional buffer) + "Make the BUFFER available for subscription to peers in a collaborative editing session. +If BUFFER is nil, the current buffer is used." + (interactive (list nil)) + + ;; Make sure we have a session. + (unless rudel-current-session + (error "No active Rudel session")) + + (unless buffer + (setq buffer (current-buffer))) + + (with-current-buffer buffer + (when (rudel-buffer-has-document-p) + (error "Buffer already published or subscribed"))) ; TODO keep this? + + ;; + (with-slots (backend connection self) rudel-current-session + (let ((document (rudel-make-document backend + (buffer-name buffer) + rudel-current-session))) + (rudel-add-document rudel-current-session document) + + (rudel-attach-to-buffer document buffer) + (object-add-to-list document :subscribed self) + + (rudel-publish connection document))) + ) + +;;;###autoload +(defun rudel-unpublish-buffer (&optional buffer) + "Deny peers access to BUFFER in a collaborative editing session. +If BUFFER is nil, the current is used." + (interactive) + + ;; Make sure we have a session. + (unless rudel-current-session + (error "No active Rudel session")) + + (unless buffer + (setq buffer (current-buffer))) + + (with-current-buffer buffer + (unless (rudel-buffer-has-document-p) + (error "Buffer is not published"))) + + ;; + (with-slots (connection) rudel-current-session + (let ((document (rudel-buffer-document buffer))) + (rudel-detach-from-buffer document) + + (rudel-unsubscribe-from connection document))) + ) + +(provide 'rudel) +;;; rudel.el ends here diff --git a/emacs.d/lisp/rudel/ChangeLog b/emacs.d/lisp/rudel/ChangeLog new file mode 100644 index 0000000..03c4fab --- /dev/null +++ b/emacs.d/lisp/rudel/ChangeLog @@ -0,0 +1,2403 @@ +2009-10-22 Jan Moringen + + * TODO (Future): added BEEP and SubEthaEdit tasks + +2009-10-15 Jan Moringen + + * rudel.el (rudel-version): bumped to 0.3 + * Project.ede (project rudel): bumped version to 0.3 + + * TODO: whitespace fixes + (Milestone rudel-0.4): new; focus on telepathy transport + (Milestone rudel-0.3): added infinote, document trees + +2009-10-13 Jan Moringen + + * TODO: new file; TODO items for future development and releases + +2009-10-12 Phil Hagelberg + + * rudel-mode.el + (rudel-mode-line-publish-state--add-indicator-to-mode-line): + Replace reference to mode-line indicator that was not present in + Emacs 22. + + * rudel-compat.el: add rudel-get-coding-system wrapper function. + * rudel-obby-util.el (with-parsed-args): Replace call to + Emacs23-specific coding-system-from-name function with + rudel-get-coding-system. + + rudel-compat.el: Add string-match-p if not present. (< Emacs 23) + + * rudel-mode.el, rudel-overlay.el: Move use of :safe flag from + defcustom to a separate put. Required for Emacs 22 compatibility. + + * rudel-loaddefs.el: Only require rudel-zeroconf if zeroconf is + available. + +2009-10-12 Jan Moringen + + * jupiter/jupiter-delete.el (jupiter-delete::jupiter-transform): + fixed error message + +2009-10-08 Jan Moringen + + * jupiter/jupiter.el (header): downcased keywords + (object-print): new method; add revisions and log length to string + * jupiter/jupiter-nop.el (header): downcased keywords; cosmetic + representation changes to "commentary" section + * jupiter/jupiter-insert.el (header): downcased keywords; cosmetic + changes to "commentary" section + (object-print): new method; add start, end, length and data to + string representation + * jupiter/jupiter-delete.el (header): downcased keywords; cosmetic + changes to "commentary" section + (jupiter-transform): cosmetic changes + (object-print): new method; add position and length to string + representation + * jupiter/jupiter-compound.el (header): downcased keywords; + cosmetic changes to "commentary" section + (object-print): new method; add number of children to string + representation + +2009-10-07 Jan Moringen + + * doc/card.tex (Session Initiation): mention configured sessions; + explain Zeroconf-advertised session in more detail + +2009-10-06 Jan Moringen + + * doc/card.tex (Joining a Session ...): added global and user + passwords + + * rudel.el (rudel-version): use list representation instead of + float + (rudel-allocate-buffer-function): added documentation string + + * INSTALL (COMPILING): fixed typo + +2009-10-06 Phil Hagelberg + + * README (JOINING): Mention `rudel-configured-sessions' + customization. + + * README (INTRODUCTION): emphasize obby backend being the only + supported one so far + (JOINING): update example session with passwords. + + * rudel-loaddefs.el: Add autoloads for rudel-host-session and + rudel-speedbar + +2009-10-05 Phil Hagelberg + + * README: Mention Eshell issue and license. + +2009-10-03 Jan Moringen + + * rudel-compat.el (make-pulsing-progress-reporter): removed; + `make-progress-reporter' is sufficient + (progress-reporter-pulse): store index in the car of the reporter; + set last update time of the reporter + * obby/rudel-obby.el (rudel-connect): use `make-progress-reporter' + instead of `make-pulsing-progress-reporter' + * obby/rudel-obby-client.el + (rudel-obby-connection::rudel-subscribe-to): cosmetic changes in + status messages + + * ChangeLog: updated + +2009-10-02 Phil Hagelberg + + * INSTALL: Mention CEDET's inclusion in Emacs + + * rudel-state-machine.el (rudel-state-wait): update progress + reporter usage to match rudel-compat.el + + * rudel-compat.el: Use updated progress reporters + (progress-reporter-update): may be used by pulsing reporters too + (make-progress-reporter): nil max value returns pulsing reporter + (progress-reporter-force-update): may be used by pulsing reporters + too + (progress-reporter-pulsing-p): added + (progress-reporter-pulse): removed message change option + +2009-10-02 Jan Moringen + + * rudel-state-machine.el (rudel-state-wait): accept callback + function instead of message; adjusted documentation string + * obby/rudel-obby.el (rudel-obby-backend::rudel-connect): when + calling `rudel-state-wait', provide a callback; the callback + controls a progress reporter + * obby/rudel-obby-client.el + (rudel-obby-connection::rudel-subscribe-to): when calling + `rudel-state-wait', provide a callback; the callback controls a + progress reporter + + * rudel-compat.el (progress-reporter-pulse): store index as float + + * obby/rudel-obby-client.el + (rudel-obby-client-state-idle::rudel-obby/obby_document_create): + cosmetic changes of printed messages + (rudel-obby-client-state-idle::rudel-obby/obby_document_remove): + cosmetic changes of printed messages + + * rudel.el (rudel-session::rudel-remove-document): when necessary, + detach document first; added documentation string + (rudel-document::rudel-attached-p): new method; return non-nil + when document is attached to a buffer + + * obby/rudel-obby-client.el + (rudel-obby-client-state-idle::rudel-obby/net6_client_part): + promoted warning severity to :warning + (rudel-obby-client-state-idle::rudel-obby/obby_document_remove): + fixed lookup of document; added warning message in case it is not + found + (rudel-obby-client-state-idle::rudel-obby/obby_document/rename): + promoted warning severity to :warning + (rudel-obby-connection::rudel-unpublish): new method; ask server + to remove a document + +2009-10-01 Jan Moringen + + * rudel-state-machine.el (rudel-state-wait): fixed progress range + [0, 100] -> [0, 1]; fixed reference to reporter object + * obby/rudel-obby.el (rudel-obby-send): removed remnants of calls + to `working-*' functions; call `progress-reporter-pulse' just + before `progress-reporter-done' + +2009-09-30 Jan Moringen + + * rudel-compat.el: only define pulsing progress reporter when + Emacs does not have one itself + (header): downcased keywords + (make-pulsing-progress-reporter): renamed + `make-progress-reporter-pulse' -> + `make-pulsing-progress-reporter'; cosmetic changes; explanatory + comment + (progress-reporter-pulse): added documentation string + Suggested by Phil Hagelberg + + * rudel-compat.el (progress-pulse-values): new variable; indicator + strings used by pulsing progress reporter + (make-progress-reporter-pulse): new function; creates pulsing + progress reporter + (progress-reporter-pulse): new function; updates pulsing progress + reporter + Suggested by Phil Hagelberg + +2009-09-29 Jan Moringen + + * obby/rudel-obby-server.el + (rudel-obby-server-state-before-join::rudel-obby/net6_client_login): + accept two additional arguments: global-password and user-password + + * rudel-session-initiation.el (rudel-configured-sessions): + improved documentation string + +2009-09-29 Jan Moringen + + * INSTALL (REQUIREMENTS): wording + (INSTALLING): mention other things that cause rudel to be + autoloaded; minor cosmetic changes + (COMPILING): fixed filename compile.el -> rudel-compile.el + + * rudel-compile.el (header): added copyright and meta data + (code): let-bind rudel-dir; call `byte-recompile-directory' just + once since it operates recursively + +2009-09-28 Jan Moringen + + * obby/rudel-obby-server.el (header): better documentation + (require rudel-state-machine): now required for state machine + style handling of client connections + (require rudel-obby-state): now required; provides base class for + states + (rudel-obby-server-state-new): new class; client connection state + new + (rudel-obby-server-state-encryption-negotiate): new class; client connection state + for negotiating encryption + (rudel-obby-server-state-before-join): new class; client connection state + for waiting for login request + (rudel-obby-server-state-new): new class; client connection state + entered after session setup and joining is complete + (rudel-obby-server-connection-states): new variable; list of + states and their symbolic names + (rudel-obby-client): now derived from rudel-state-machine + + * obby/rudel-obby.el (rudel-obby-backend::rudel-ask-connect-info): + check for :global-password and :user-password correctly; do not + put them into the list when they are "" + (rudel-obby-backend::rudel-connect): comment + + * rudel-session-initiation.el + (rudel-configured-sessions-backend::rudel-discover): let + `rudel-session-initiation-adjust-info' do the heavy lifting + (rudel-session-initiation-adjust-info): new function; adjust + arguments that need adjusting in a session information property + list + + * rudel-session-initiation.el (rudel-configured-sessions): more + precise specification of the customization type + +2009-09-27 Phil Hagelberg + + * compile.el: renamed compile.el -> rudel-compile.el + + * INSTALL: Mention new install process using compile.el and + rudel-loaddefs.el. + + * rudel-loaddefs.el: Autoload rudel as one unit instead of + piece-by-piece. Remove eieio dependency from autoloads. + + * compile.el: Perform compilation from Elisp + + * Makefile: Remove error-prone CEDET-autogenerated build scripts. + +2009-09-27 Jan Moringen + + * rudel-session-initiation.el + (rudel-configured-sessions-backend::rudel-discover): fixed a bug + that dropped the last option in each configured session + + * rudel-session-initiation.el (rudel-configured-sessions): new + customization option; contains a list of session information lists + (rudel-ask-protocol-backend::initialize-instance): maybe call next + method + (rudel-configured-sessions-version): new constant; version of the + configured-sessions backend + (rudel-configured-sessions-backend): new class; + configured-sessions backend + (rudel-configured-sessions-backend::initialize-instance): new + method; set version slot + (rudel-configured-sessions-backend::rudel-discover): new method; + "discover" configured sessions + (autoload rudel-add-backend): register + rudel-configured-sessions-backend as a protocol backend + + * rudel-chat.el (rudel-chat-buffer-name): new constant; name chat + log buffer + (rudel-chat-handle-buffer): raise buffer when logging a chat + message + + * rudel-debug.el (header): fixed meta-data and license + (rudel-debug-sent-data-face): added documentation string + (rudel-debug-received-data-face): added documentation string + (rudel-debug-received-processed-data-face): added documentation + string + (rudel-debug-state-face): added documentation string + (rudel-debug-special-face): added documentation string + (rudel-suspend-session-socket): added documentation string + (rudel-resume-session-socket): added documentation string + + * obby/rudel-obby-util.el (rudel-obby-dispatch): use + `display-warning' instead of `warn' + * obby/rudel-obby-state.el (rudel-obby-state::rudel-accept): use + `display-warning' instead of `warn' + (rudel-obby-document-handler::rudel-obby/obby_document): use + `display-warning' instead of `warn' + * obby/rudel-obby-client.el + (rudel-obby-client-state-idle::rudel-obby/net6_client_part): use + `display-warning' instead of `warn' + (rudel-obby-client-state-idle::rudel-obby/obby_document/record): + use `display-warning' instead of `warn' + * rudel-backend.el (rudel-backend-factory::rudel-load-backends): + use `display-warning' instead of `warn' + + * obby/rudel-obby-client.el (require rudel-chat): used when + handling chat messages + (rudel-obby-client-state-idle::rudel-obby/obby_message): new + method; handles obby 'message' messages by dispatching to + `rudel-chat-dispatch-message' + * rudel-chat.el (whole file): new file; handling of incoming chat + messages + * Project.ede (target rudel): added rudel-chat.el + * Makefile (target rudel_LISP): added rudel-chat.el + +2009-09-26 Jan Moringen + + * obby/rudel-obby-state.el (rudel-obby-server-connection-state): + new class; base class for server connection states + (rudel-obby-server-connection-state::rudel-broadcast): new method; + broadcast message to a set of receivers + +2009-09-25 Jan Moringen + + * obby/rudel-obby.el (rudel-obby-backend::rudel-connect): new + error state they-finalized; handle join-failed error specially + * obby/rudel-obby-client.el (rudel-obby-client-state-join-failed): + improved comments + (rudel-obby-client-state-they-finalized): new state class; used to + indicate that the connection was closed by the peer + (rudel-obby-client-connection-states): added they-finalized + (rudel-obby-connection::rudel-close): switch state machine to + they-finalized + (rudel-obby-connection::rudel-subscribe-to): new error state + they-finalized + + * rudel.el (rudel-client-session::connection): allow nil value + (rudel-client-session::rudel-end): only try to disconnect the + connection if it is non-nil; ignore errors during disconnect + + * obby/rudel-obby-client.el + (rudel-obby-client-state-encryption-start::rudel-obby/net6_encryption_begin): + do not require rudel-tls; do not try to start TLS encryption if + the connection does not support it + * rudel-tls.el (rudel-tls-make-process): mark process as + supporting TLS encryption + + * obby/rudel-obby-state.el (rudel-obby/net6_ping): return nil to + prevent erratic behavior of the state machine + + * rudel-interactive.el (rudel-allocate-buffer-clear-existing): + added missing whitespace in prompt string + +2009-09-24 Jan Moringen + + * rudel-tls.el (rudel-tls-start-tls): changed displayed message + (rudel-tls-wait-init): ignore all lines until "- Simple Client + Mode.*" is received; then switch back old filter + (rudel-tls-wait-handshake): ignore all lines until "- + Compression.*" is received; then switch to established filter + (rudel-tls-established): do not ignore any lines other than + "- Peer has closed the GNUTLS connection" + + * obby/rudel-obby.el (rudel-ask-connect-info): ask for global and + user passwords + * obby/rudel-obby-client.el + (rudel-obby-client-state-joining::rudel-enter): transmit global + and user passwords when available + + * obby/rudel-obby-errors.el + (rudel-obby-error-wrong-global-password): fixed error code + (rudel-obby-error-wrong-user-password): fixed error code + (rudel-obby-error-protocol-version-mismatch): fixed error code + (rudel-obby-error-not-encrypted): fixed error code + * obby/rudel-obby-client.el + (rudel-obby-client-state-joining::rudel-obby/net6_login_failed): + recognize wrong global/user password error codes + + * obby/rudel-obby-debug.el (whole file): new file; debugging + functions for the obby backend + * rudel-debug.el (whole file): new file; debugging functions + + * obby/rudel-obby-errors.el + (rudel-obby-error-wrong-global-password): new constant; error code + for wrong global password + (rudel-obby-error-wrong-user-password): new constant; error code + for wrong user password + (rudel-obby-error-protocol-version-mismatch): new constant; error + code for protocol version mismatch + (rudel-obby-error-not-encrypted): new constant; error code for not + encrypted + + * Project.ede (target rudel): added rudel-transport.el + * Makefile (target rudel_LISP): added rudel-transport.el + * rudel-transport.el (whole file): new file; interface for + transport backends + +2009-09-20 Jan Moringen + + * obby/rudel-obby-server.el + (rudel-obby-client::rudel-remote-operation): added byte -> char + conversion before the operation is applied to the server-side + document; updated comments + + * obby/rudel-obby-server.el (rudel-obby-client::rudel-broadcast): + cosmetic changes + (rudel-obby-client::rudel-obby/net6_client_login): cosmetic + changes; improved comments + (rudel-obby-client::rudel-obby/obby_document): changed + documentation string; cosmetic changes + (rudel-obby-client::rudel-obby/obby_document/record): added a + comment + (rudel-obby-client::rudel-remote-operation): improved comments + + * obby/rudel-obby-server.el (header): added header comment + (rudel-obby-client::rudel-obby/obby_document_create): changed + documentation comment + (rudel-obby-client::rudel-obby/obby_document/subscribe): changed + documentation comment; cosmetic changes + (rudel-obby-client::rudel-obby/obby_document/unsubscribe): + cosmetic changes + (rudel-obby-server::initialize-instance): do not run :after; call + next method + (rudel-obby-server::rudel-broadcast): signal wrong-type-argument + instead of just error; cosmetic changes + (rudel-obby-server::rudel-check-username-and-color): changed + comments + (rudel-obby-server::object-print): new method; generate string + representation with number of clients + +2009-09-19 Jan Moringen + + * obby/rudel-obby-server.el + (rudel-obby-client::rudel-obby/obby_document/record/ins): + construct the operation and use `rudel-remote-operation' + (rudel-obby-client::rudel-obby/obby_document/record/del): + construct the operation and use `rudel-remote-operation' + (rudel-obby-client::rudel-remote-operation): new method; transform + and apply an operation object to the server-side document; send + operation to all other clients + (rudel-obby-server::rudel-broadcast): cosmetic changes + + * obby/rudel-obby-server.el + (rudel-obby-client::rudel-obby/obby_document/record/del): swapped + local and remote revisions in the operation name to be consistent + with record/ins; does not affect behavior + +2009-09-12 Jan Moringen + + * rudel-mode.el (rudel-header-subscriptions--update-from-document): + force mode line update + (rudel-header-subscriptions--update-from-buffer): force mode line + update + (rudel-header-subscriptions-minor-mode): force mode line update + + * rudel-mode.el + (rudel-mode-line-publish-state-unpublished-string): new + customization option; string used to indicate that a buffer is not + published + (rudel-mode-line-publish-state-published-string): new + customization option; string used to indicate that a buffer is + published + (rudel-mode-line-publish-state--update-string): use + `rudel-mode-line-publish-state-unpublished-string' and + `rudel-mode-line-publish-state-unpublished-string' + + * rudel.el (rudel-session::end-hook): new slot; stores handlers of + the end hook + (rudel-session::rudel-end): run end hook + (rudel-session::rudel-join-session): install handler on session + end hook to set `rudel-current-session' to nil + (rudel-session::rudel-end-session): no need to run + rudel-session-end-hook or reset `rudel-current-session' + * rudel-hooks.el (rudel-hooks--session-start): add handler for the + end hook of the session + (rudel-hooks--session-end): remove the handler from end hook of + the session; run the rudel-session-end hook + (rudel-hooks--install-handlers): do install handler for + rudel-session-end hook; this is now done by installing the in the + session object + (rudel-hooks--uninstall-handlers): no need to remove + rudel-session-end hook + + * rudel-util.el (rudel-socket-owner::rudel-state-change): cover + more states + + * obby/rudel-obby-client.el + (rudel-obby-client-state-idle::rudel-obby/obby_document_remove): + implemented, was stub; untested though + +2009-09-10 Jan Moringen + + * obby/rudel-obby-client.el + (rudel-obby-client-state-idle::rudel-obby/net6_client_join): check + whether we have a user object for the specified user id; modify + the existing object if there is one + +2009-09-09 Jan Moringen + + * zeroconf/Makefile (whole file): regenerated + * wave/Makefile (whole file): regenerated + * Makefile (target all): build target doc + (target doc): new target; build documentation + (target tags): build target tags in doc directory + (target dist): build target dist in doc directory + +2009-09-06 Jan Moringen + + * telepathy/rudel-telepathy.el (header): fixes + (require rudel-backend): required, since we define a backend + (require rudel-transport): it is a transport backend + (class rudel-telepathy-backend): derived from + rudel-transport-backend + (rudel-telepathy-backend::initialize-instance): new method; set + version slot + (autoloading): upgraded to new backend registration style + + * INSTALL (REQUIREMENTS): mention Avahi + +2009-09-05 Jan Moringen + + * rudel.el (require rudel-util): required for `rudel-hook-object' + + * rudel-util.el (property svn:executable): removed property + * rudel-overlay.el (property svn:executable): removed property + + * doc/Project.ede (whole file): new file; project file for the + documentation directory + * doc/Makefile (whole file): new file; generated Makefile for the + documentation directory + * doc/card.tex (whole file): new file; reference card for Rudel; + source + * doc/card.pdf (whole file): new file; reference card for Rudel; + PDF + +2009-09-04 Jan Moringen + + * rudel-interactive.el (rudel-read-document): added comment + (rudel-allocate-buffer-clear-existing): handle the case in which + case a buffer with the desired name exists and is attached to a + different document; added some comments + + * rudel-mode.el (header): list all provided modes; bump version + (require rudel-hooks): required for global hooks + (rudel-mode-line-publish-state-string): new variable; buffer + local, holds publish state string for buffer + (rudel-mode-line-publish-state--add-indicator-to-mode-line): new + function; add publish state indicator to mode line + (rudel-mode-line-publish-state--remove-indicator-from-mode-line): + new function; remove publish state indicator from mode line + (rudel-mode-line-publish-state--update-string): new function; + update publish state indicator according to buffer state + (rudel-mode-line-publish-state--document-attach): new function; + handle document attaching to buffer + (rudel-mode-line-publish-state--document-detach): new function; + handle document detaching from buffer + (rudel-mode-line-publish-state-minor-mode): new minor mode; + displays publish state of buffer in mode line + (global-rudel-mode-line-publish-state-mode): new minor mode; + globalization of `rudel-mode-line-publish-state-minor-mode' + (rudel-minor-keymap): menu entries for buffer local and global + mode line publish state mode + + * rudel.el (require rudel-hooks): required or hook variables + (rudel-session-start-hook): moved to rudel-hooks.el + (rudel-session-end-hook): moved to rudel-hooks.el + * rudel-hooks.el (whole file): new file; contains hook variables + and mapping from object hooks to global hooks + * Project.ede (target rudel): added file rudel-hooks.el + * Makefile (target rudel_LISP): added file rudel-hooks.el + +2009-09-03 Jan Moringen + + * rudel-mode.el (rudel-header-subscriptions-minor-mode): improved + documentation string + +2009-08-30 Jan Moringen + + * zeroconf/Makefile (whole file): new file; generated Makefile for + the zeroconf subproject + + * wave/rudel-wave.el (whole file): new file; main class of the + wave backend + * wave/Project.ede (whole file): new file; project file for the + wave subproject + * wave/Makefile (whole file): new file; generated Makefile for the + wave subproject + * Project.ede (target autoloads): added wave directory + * Makefile (LOADDIRS): added wave and zeroconf directories + (VERSION): bumped to 0.2 + (target all): added wave and zeroconf + (tags): descend into zeroconf and wave directories + (dist): descend into zeroconf and wave directories + +2009-08-28 Jan Moringen + + * rudel.el (rudel-change-color): run the change hook of the self + user + + * rudel-overlay.el (rudel-overlay-set-face-attributes): check the + face actually exists + +2009-08-27 Jan Moringen + + * rudel-mode.el (rudel-header-subscriptions-use-images): new + variable; controls whether images are used when formatting user + names + (rudel-header-subscriptions-separator): new variable; separator + used when formatting user names + (rudel-header-subscriptions--make-format): new function; make a + format object for header line + (rudel-header-subscriptions--update-from-document): new function; + update header line from document + (rudel-header-subscriptions--update-from-buffer): new function; + update header line from buffer + (rudel-header-subscriptions--options-changed): new function; + update header line in all buffers that have + rudel-header-subscriptions-minor-mode enabled after customization + option change + (rudel-header-subscriptions--user-change): new function; update + header line after a user object change + (rudel-header-subscriptions--add-user): new function; watch newly + subscribed user and update header line + (rudel-header-subscriptions--remove-user): new function; stop + watching user and update header line + (minor mode rudel-header-subscriptions-minor-mode): new minor + mode; display subscribed users in buffer's header line + (rudel-header-subscriptions--attach): new function; enable header + subscription minor mode when attaching + (rudel-header-subscriptions--detach): new function; disable header + subscription minor mode when detaching + (rudel-header-subscriptions--add-document): new function; monitor + attaching/detaching of new document + (rudel-header-subscriptions--remove-document): new function; stop + monitoring attaching/detaching of new document + (rudel-header-subscriptions--session-start): new function; watch + documents being added/removed to/from the session + (rudel-header-subscriptions--session-end): new function; stop + watching documents being added/removed to/from the session + (minor mode global-rudel-header-subscriptions-mode): global minor + mode that controls `rudel-header-subscriptions-minor-mode' in + buffers + (advice global-rudel-header-subscriptions-mode): controls + adding/removing watches for added/removed documents when the + global mode is enabled/disabled + (rudel-minor-keymap): Added entries for + `rudel-header-subscriptions-minor-mode' and + `global-rudel-header-subscriptions-mode' + +2009-08-26 Jan Moringen + + * . (property svn:ignore): added patterns + * jupiter (property svn:ignore): added patterns + * obby (property svn:ignore): added patterns + + * rudel.el (rudel-session::add-user-hook): new slot; add user hook + function list + (rudel-session::remove-user-hook): new slot; remove user hook + function list + (rudel-session::add-document-hook): updated documentation string + (rudel-session::remove-document-hook): updated documentation + string + (rudel-session::rudel-add-user): run add user hook + (rudel-session::rudel-remove-user): run remove user hook + + * rudel.el (rudel-session-start-hook): new variable; session start + hook function list + (rudel-session-end-hook): new variable; session end hook function + list + (rudel-join-session): run session start hook + (rudel-end-session): run session end hook + + * obby/rudel-obby-server.el + (rudel-obby-client::rudel-obby/obby_document_create): call + `generate-new-buffer-name' on complete buffer name; not just name + part + + * obby/rudel-obby-server.el + (rudel-obby-client::rudel-obby/obby_document/subscribe): send + number of bytes instead of number of characters + + * obby/rudel-obby-client.el + (rudel-obby-client-state-document-synching::remaining-bytes): + fixed initarg num-bytes -> remaining-bytes + + * rudel.el (rudel-session::add-document-hook): new slot; run when + a document gets added to the session + (rudel-session::remove-document-hook): new slot; run when a + document gets removed from the session + (rudel-session::rudel-add-document): run add document hook + (rudel-session::rudel-remove-document): run remove document hook + (rudel-document::unsubscribe-hook): fixed initarg subscribe-hook + -> unsubscribe-hook + +2009-08-25 Jan Moringen + + * www/index.html (Download): link to http://bazaar-vcs.org; + improved wording + + * www/index.html (Download): changed link to source; add browse + source link for bzr + + * INSTALL (REQUIREMENTS): precise CEDET release version + + * obby/rudel-obby-server.el + (rudel-obby-client::rudel-obby/obby_user_colour): run change hook + after setting slot + (rudel-obby-server::rudel-remove-client): run change hook after + setting slot + * obby/rudel-obby-client.el + (rudel-obby-client-state-idle::rudel-obby/net6_client_part): run + change hook after setting slots + (rudel-obby-client-state-idle::rudel-obby/obby_user_colour): run + change hook after setting slot + * rudel.el (class rudel-user): derive from `rudel-hook-object' + (rudel-user::change-hook): new slot; stores change hook functions + + * obby/rudel-obby-server.el + (rudel-obby-client::rudel-obby/obby_user_colour): cosmetic changes + (rudel-obby-client::rudel-obby/obby_document/subscribe): use + `rudel-add-user' + (rudel-obby-client::rudel-obby/obby_document/unsubscribe): use + `rudel-remove-user' + (rudel-obby-server::rudel-check-username-and-color): whitespace + fixes + +2009-08-23 Jan Moringen + + * rudel.el (rudel-document::attach-hook): new slot; attach hook + function list + (rudel-document::detach-hook): new slot; detach hook function list + (rudel-document::rudel-attach-to-buffer): run hook `attach-hook' + (rudel-document::rudel-detach-from-buffer): run hook `detach-hook' + (rudel-document::rudel-add-user): improved documentation string + (rudel-document::rudel-remove-user): improved documentation string + + * obby/rudel-obby.el + (rudel-obby-user::eieio-speedbar-object-buttonname): fixed typo + +2009-08-21 Jan Moringen + + * obby/rudel-obby-client.el + (rudel-obby-client-state-idle::rudel-obby/obby_document/subscribe): + call `rudel-add-user' + (rudel-obby-client-state-idle::rudel-obby/obby_document/unsubscribe): + call `rudel-remove-user' + * rudel.el (class rudel-document): mixin rudel-hook-object + (rudel-document::subscribe-hook): new slot; subscribe-hook + function list + (rudel-document::unsubscribe-hook): new slot; unsubscribe-hook + function list + (rudel-document::rudel-add-user): new method; add user to list of + subscribed users and run subscribe-hook + (rudel-document::rudel-remove-user): new method; remove user from + list of subscribed users and run unsubscribe-hook + + * obby/rudel-obby.el (header): cosmetic changes + (include rudel-icons): `rudel-display-string' uses icons + (rudel-obby-user::eieio-speedbar-object-buttonname): use + `rudel-display-string' + (rudel-obby-user::rudel-display-string): new method; textual + representation of user object + (rudel-obby-parse-message): cosmetic changes + * rudel.el (include rudel-icons): `rudel-display-string' uses + icons + (rudel-user::rudel-display-string): new method; textual + representation of user object + + * rudel-util.el (rudel-hook-object): new class; abstract mixin for + classes that offer hooks + (rudel-hook-object::object-add-hook): new method; add function to + hook list + (rudel-hook-object::object-remove-hook): new method; remove + function from hook list + (rudel-hook-object::object-run-hook-with-args): new method; run + hook functions + +2009-08-20 Jan Moringen + + * icons/plaintext.svg (new file): plaintext icon + * icons/person.svg (new file): person icon + * icons/encrypted.svg (new file): encrypted icon + * icons/document.svg (new file): document icon + * icons/disconnected.svg (new file): disconnected icon + * icons/connected.svg (new file): connected icon + * rudel-icons.el (new file): loading icons + * Project.ede (target rudel): added rudel-icons.el + +2009-08-19 Jan Moringen + + * rudel.el (rudel-join-session): renamed local variable backend to + session-initiation-backend + +2009-08-17 Jan Moringen + + * obby/rudel-obby-state.el (rudel-obby-state::rudel-accept): fixed + format of warning message + + * rudel-state-machine.el (require working): needed by + `rudel-state-wait' + + * obby/rudel-obby-client.el + (rudel-obby-client-state-idle::rudel-obby/obby_user_colour): set + face attributes + * rudel.el (rudel-change-color): set face attributes + * rudel-overlay.el (rudel-overlay-make-face): use + `rudel-overlay-set-face-attributes' + (rudel-overlay-set-face-attributes): new function; set face + attributes + + * rudel-overlay.el (rudel-overlay-author-set-properties): call + `rudel-overlay-make-face-symbol' + (rudel-overlay-make-face-symbol): new function; return face symbol + +2009-08-16 Jan Moringen + + * rudel-overlay.el (rudel-overlay-author-display): option that + controls display of author overlays + (rudel-make-author-overlay): call + `rudel-overlay-author-set-properties' to set the overlay + properties + (rudel-overlay-author-set-properties): new function; set overlay + properties; respects rudel-overlay-author-display + (rudel-overlay-author-update): new function; update overlay + properties based on associated user object + (rudel-overlay-options-changed): new function; call + `rudel-overlay-author-update' on all Rudel overlays in all buffers + * rudel-mode.el (header): cosmetic changes + (rudel-minor-menu): added menu entry to toggle display of author + overlays + + * rudel-overlay.el (rudel-make-author-overlay): use `intern' + instead of `make-symbol' when allocating the face name; this way, + faces can actually be created lazily + (rudel-overlay-make-face): call `make-face' for the new face + + * obby/rudel-obby-client.el + (rudel-obby-client-state-document-synching::object-print): fixed + slot name remaining-bytes + + * rudel.el (rudel-host-session): the backend object is the cdr of + the result of `rudel-backend-choose' + +2009-08-15 Jan Moringen + + * telepathy/rudel-telepathy.el (header): added file comment + * obby/rudel-obby-state.el (header): fixed email address + (whole file): whitespace fixes + * jupiter/jupiter.el (header): cosmetic changes + (whole file): whitespace fixes + * rudel-util.el (header): cosmetic changes + * rudel-mode.el (header): cosmetic changes + * rudel-errors.el (header): fixed type + (whole file): whitespace fixes + + * obby/rudel-obby.el (rudel-ask-connect-info): added optional + argument info; do not ask for things that are already specified in + info + (autoload): register obby service with the Zeroconf backend + * zeroconf/rudel-zeroconf.el (new file): Zeroconf session + initiation for Rudel + * zeroconf/Project.ede (new file): subproject zeroconf + * rudel.el (rudel-join-session): call `rudel-ask-info' to augment + connect info + * Project.ede (target autoloads): added directory zeroconf + * INSTALL (INSTALLING): mention zeroconf subdirectory + (COMPILING): mention zeroconf target + + * rudel-backend.el (rudel-backend-factory::rudel-get-backend): + return backend as a cons + (rudel-backend-get): new function; convenience function for + getting backends + + * obby/rudel-obby.el (header): extended commentary and history + (rudel-obby-version): bumped to 0.2 + (rudel-obby-backend::initialize-instance): set :version slot in + constructor instead of using obsolete lambda expression in + initform + +2009-08-14 Jan Moringen + + * Project.ede (project rudel): bumped version to 0.2; added + mailing list and path to web files + + * rudel-backend.el (rudel-backend-factory): do not initialize + backends with lambda expression + (rudel-backend-factory::initialize-instance): new method; + initialize backends + (rudel-backend-cons-p): use `object-p' instead of `eieio-object-p' + (rudel-backend-dump): changed format slightly + +2009-08-13 Jan Moringen + + * rudel.el (include rudel-session-initiation): required for + `rudel-join-session' + (rudel-join-session): mostly rewritten; moved user interaction to + `interactive' form + * rudel-session-initiation.el (new file): session initiation + backend interface and high-level programming interface + * Project.ede (target rudel): added rudel-session-initiation.el + * Makefile (rudel_LISP): added rudel-session-initiation.el + + * rudel-interactive.el (rudel-read-session): discriminate sessions + vs. session generating objects using `rudel-backend-cons-p' + + * rudel-backend.el (rudel-backend-cons-p): new function; checks + whether a cons consists of a backend name and object + +2009-08-12 Jan Moringen + + * rudel-interactive.el (rudel-read-session): new function; read + session by name and return it + + * rudel-interactive.el (rudel-read-backend): argument backends is + no longer optional; always return a cons of backend name and + object; updated documentation string + (whole file): whitespace fixes + * rudel-backend.el (rudel-backend-choose): always return a cons of + backend name and object; updated documentation string + +2009-08-11 Jan Moringen + + * rudel.el (file header): added project URL + (whole file): improved some comments + +2009-08-10 Jan Moringen + + * rudel.el (include eieio-base): needed for eieio-named + +2009-08-04 Jan Moringen + + * rudel-state-machine.el (header section commentary): updated + (rudel-state-machine::initialize-instance): use + `rudel--switch-to-return-value' to allow immediate switch to + another state + (rudel-state-machine::rudel-switch): use + `rudel--switch-to-return-value' to switch to successor state + (rudel-state-machine::rudel--switch-to-return-value): new function + switch to successor state for different kinds of specifications of + the successor state + +2009-08-03 Jan Moringen + + * obby/rudel-obby.el (require rudel-backend): now necessary + (require rudel-protocol): now necessary + (class rudel-obby-backend): now derived from `rudel-backend'; + autoloaded + (autoloading): use `rudel-add-backend' + * rudel.el (require rudel-backend): backends have their own file + (class rudel-backend): moved to rudel-backend.el + (rudel-load-backends): moved to rudel-backend.el + (rudel-suitable-backends): moved to rudel-backend.el + (rudel-choose-backend): moved to rudel-backend.el + (rudel-join-session): use `rudel-backend-choose' + (rudel-host-session): use `rudel-backend-choose' + + * rudel-protocol.el (new file): interface for Rudel protocol + backends + * Project.ede (target rudel): added rudel-protocol.el + * Makefile (rudel_LISP): added rudel-protocol.el + + * rudel-backend.el (rudel-backend-factory::rudel-get-backend): + improved documentation string + (rudel-backend-factory::rudel-suitable-backends): improved + documentation string + (rudel-backend-suitable-backends): improved documentation string + (rudel-backend-choose): When only one backend matches, do not + check interactivity using `interactive-p', that does not work; + call `sit-for' correctly + +2009-08-02 Jan Moringen + + * jupiter/Makefile (whole file): regenerated; reflects CEDET + changes + * rudel-backend.el (new file): generic backend management, query + and loading functions + * Project.ede (target rudel): added rudel-backend.el to sources + * Makefile (rudel_LISP): added rudel-backend.el + + * obby/rudel-obby-server.el + (rudel-obby-client::rudel-obby/net6_client_login): use slot + :object-name instead of calling object-name-string + (rudel-obby-server::rudel-add-context): use slot :object-name + instead of calling object-name-string + * obby/rudel-obby-client.el + (rudel-obby-client-state-idle::rudel-obby/obby_document/rename): + use slot :object-name instead of calling object-name-string + (rudel-obby-connection::rudel-add-context): use slot :object-name + instead of calling object-name-string + (rudel-obby-connection::rudel-publish): use slot :object-name + instead of calling object-name-string + * rudel.el (class rudel-user): added base class eieio-named for + virtual slot :object-name; made abstract + (class rudel-document): added base class eieio-named for virtual + slot :object-name + * rudel-overlay.el (rudel-make-author-overlay): use slot + :object-name instead of calling object-name-string + + * rudel-state-machine.el + (rudel-state-machine::initialize-instance): use &rest slots + instead of just slots + +2009-07-15 Jan Moringen + + * rudel.el (rudel-document::rudel-detach-from-buffer): call + `rudel-overlays-remove-all'; remove `rudel-unpublish-buffer' hook + early + * rudel-overlay.el (rudel-overlays-remove-all): new function; + remove all overlays from the current buffer + (whole file): cosmetic changes; typo fixes; whitespace fixes + + * obby/rudel-obby.el (rudel-obby-document::rudel-unique-name): + Check `next-method-p' before calling the next method + * obby/rudel-obby-client.el + (rudel-obby-connection::initialize-instance): Check + `next-method-p' before calling the next method + (rudel-obby-connection::rudel-register-state): Check + `next-method-p' before calling the next method + (rudel-obby-connection::rudel-disconnect): Check + `next-method-p' before calling the next method + * rudel.el (rudel-client-session::rudel-end): Check + `next-method-p' before calling the next method + +2009-07-13 Jan Moringen + + * obby/rudel-obby.el (rudel-obby-backend::rudel-connect): only + start the network process after everything is ready; wait for the + connection state machine to reach a success or error state + (rudel-obby-backend::rudel-host): cosmetic changes + (class rudel-obby-user): cosmetic changes + * rudel.el (rudel-join-session): reversed order of creation for + session and connection; do not catch errors to give error messages + a chance + + * jupiter/jupiter-insert.el (jupiter-insert::jupiter-transform): + cosmetic changes + (whole file): whitespace fixes + + * rudel.el (rudel-session::rudel-find-user): added documentation + string; cosmetic changes + (rudel-session::rudel-find-document): added documentation string; + cosmetic changes + (whole file): whitespace fixes + +2009-07-12 Jan Moringen + + * rudel-speedbar.el (eieio-speedbar-object-buttonname): use + `rudel-unique-name' instead of `object-name-string' + + * obby/rudel-obby-state.el (rudel-enter): return nil + + * obby/rudel-obby-client.el + (rudel-obby-client-state-joining::rudel-enter): return nil + (rudel-obby-client-state-join-failed::rudel-enter): return nil + (rudel-obby-client-state-session-synching::rudel-enter): return + nil + (rudel-obby-client-state-session-synching::object-print): new + method; add number of remaining items to string representation + (rudel-obby-client-state-subscribing::rudel-enter): nil + (rudel-obby-client-state-document-synching::rudel-enter): nil + (rudel-obby-client-state-document-synching::object-print): new + method; add number of remaining bytes to string representation + +2009-07-11 Jan Moringen + + * obby/rudel-obby-util.el (with-parsed-arguments): added debug + declaration + (whole file): whitespace fixes + * rudel-util.el (rudel-assemble-line-fragments): added debug + declaration + (rudel-loop-lines): added debug declaration + (rudel-loop-chunks): fixed documentation string; added debug + declaration + + * rudel-state-machine.el (rudel-no-start-state): new error symbol + (rudel-state-machine::initialize-instance): try hard to find a + suitable start sate; call `rudel-switch' instead of just + `rudel-enter' + (rudel-state-machine::rudel-switch): always return the new current + state; accept successor state from `rudel-enter' + (rudel-state-machine::object-print): new method; add current state + of state machine to string representation + (rudel-state-machine::rudel-state-wait): whitespace fixes + +2009-07-07 Jan Moringen + + * obby/rudel-obby.el (class rudel-obby-backend): better + documentation string + (rudel-obby-backend::rudel-ask-connect-info): added documentation + string + (rudel-obby-backend::rudel-connect): added documentation string + (rudel-obby-backend::rudel-ask-host-info): added documentation + string + (rudel-obby-backend::rudel-host): added documentation string + (rudel-obby-backend::rudel-make-document): added documentation + string + (rudel-obby-send): cosmetic changes + (whole file): whitespace fixes + +2009-07-05 Jan Moringen + + * INSTALL (REQUIREMENTS): added Emacs itself and GNUTls + + * rudel-tls.el (rudel-tls-start-tls): added a message + (rudel-tls-wait-handshake): switch to filter + `rudel-tls-established' instead of restoring the original filter + (rudel-tls-established): new function; filters GNUTls messages in + encrypted connections + (whole file): whitespace fixes + +2009-07-04 Jan Moringen + + * README (INTRODUCTION): extended a bit + (JOINING A SESSION): added prompt/input example and an explanation + of encryption issues in the obby backend + (KNOWN BUGS): new section; no known bugs yet, though + +2009-06-17 Jan Moringen + + * obby/rudel-obby-client.el (require rudel-state-machine): the + connection now is a state machine + (require rudel-obby-errors): used when analyzing login failures + (require rudel-obby-state): useful base classes for states + (rudel-obby-client-state-new): new class; initial state of new + connections + (rudel-obby-client-state-encryption-negotiate): new class; + first encryption state + (rudel-obby-client-state-encryption-start): new class; second + encryption state + (rudel-obby-client-state-joining): new class + (rudel-obby-client-state-join-failed): new class; entered after + failed login attempt + (rudel-obby-client-state idle): new class; default state of + established connections + (rudel-obby-client-state-session-synching): new class; + synchronizing session state to client + (rudel-obby-client-state-subscribing): new class; first state of + document subscription + (rudel-obby-client-state-document-synching): new class; + synchronizing document state to client + (rudel-obby-client-connection-states): new variable; alist of + name symbols and associated state classes + (rudel-obby-connection::initialize-instance): register states + (rudel-obby-connection::rudel-register-state): new method; set + connection slot of state to its connection + (rudel-obby-connection::rudel-add-context): cleanup + (rudel-obby-connection::rudel-message): dispatch message using + `rudel-accept' + (rudel-obby-connection::rudel-subscribe-to): initiate subscription + by switching to state 'subscribing' + + * obby/rudel-obby-state.el (rudel-obby-document-handler): new + class; mixin class that provides handling of obby 'document' + messages + + * rudel-state-machine.el + (rudel-state-machine::initialize-instance): find start state in + slots and switch into it + (while-file): whitespace fixes + +2009-06-15 Jan Moringen + + * www/index.html (section download): fixed link to download area + (whole file): whitespace cleanup + + * obby/rudel-obby-state.el (new file): finite state machine states + for the rudel backend + * obby/Project.ede (target rudel/obby): added rudel-obby-state.el + * obby/Makefile (target obby_LISP): added rudel-obby-state.el + + * rudel-util.el (require rudel-errors): required for dispatch + errors + (symbol rudel-dispatch-error): new condition symbol for dispatch + errors + (rudel-dispatch): new function; dispatch to method based on method + name + (whole file): whitespace fixes + +2009-06-14 Jan Moringen + + * rudel-mode.el (global-rudel-minor-mode): removed; the variable + is created by `define-minor-mode' + (minor-mode-alist): managed by `define-minor-mode' + + * rudel-mode.el (require easy-mmode): used to define global rudel + minor mode + (rudel-minor-keymap): cosmetic changes + (global-rudel-minor-mode): use `define-minor-mode' to define the + mode + (whole file): whitespace cleanup + + * rudel-telepathy.el (whole file): moved to + telepathy/rudel-telepathy.el + * telepathy/rudel-telepathy.el (whole file): moved from + rudel-telepathy.el + + * obby/rudel-obby-server.el (whole file): whitespace cleanup + * obby/rudel-obby-client.el (require rudel-obby): removed; + unnecessary + (rudel-obby-connection::initialize-instance): use &rest for `slots' + argument; cosmetic changes + (rudel-obby-connection::rudel-change-color-): use own `rudel-send' + method instead of the socket's + (rudel-obby-connection::rudel-subscribe-to): cosmetic changes + (rudel-obby-connection::rudel-unsubscribe-from): cosmetic changes + (rudel-obby-connection::rudel-local-operation): cosmetic changes + (whole file): whitespace cleanup + +2009-06-13 Jan Moringen + + * rudel-errors.el (new file): error data + * rudel-state-machine.el (new file): a simple finite state machine + implementation + * Project.ede (target rudel): added rudel-errors.el and + rudel-state-machine.el + * Makefile (target rudel_LISP): added rudel-errors.el and + rudel-state-machine.el + +2009-06-12 Jan Moringen + + * obby/rudel-obby-server.el (require rudel-obby-errors): now + required + (rudel-obby-client::rudel-obby/net6_client_login): check username + and color before adding the client + (rudel-obby-server::rudel-check-username-and-color): new method; + make sure username and color are valid and there are no duplicates + * obby/rudel-obby-errors.el (new file): error data for the obby + backend + * obby/Project.ede (rudel/obby): added rudel-obby-errors.el + * obby/Makefile (obby_LISP): added rudel-obby-errors.el + + * rudel.el (rudel-user::rudel-color): added accessor `rudel-color' + + * obby/rudel-obby-server.el (require cl): required + + * obby/rudel-obby-server.el + (rudel-obby-client::rudel-obby/net6_client_login): send suffices + of synchronized documents + +2009-06-11 Jan Moringen + + * obby/rudel-obby-server.el + (rudel-obby-client::rudel-obby/obby_document_create): send + document/rename message to client when the document suffix changes + + * obby/rudel-obby-server.el + (rudel-obby-client::rudel-obby/net6_client_login): use + `with-parsed-arguments' + (rudel-obby-client::rudel-obby/obby_user_colour): use + `with-parsed-arguments' + (rudel-obby-client::rudel-obby/obby_document_create): use + `with-parsed-arguments' + (rudel-obby-client::rudel-obby/obby_document): use + `with-parsed-arguments' + (rudel-obby-client::rudel-obby/obby_document/subscribe): use + `with-parsed-arguments' + (rudel-obby-client::rudel-obby/obby_document/unsubscribe): use + `with-parsed-arguments' + (rudel-obby-client::rudel-obby/obby_document/record): use + `with-parsed-arguments' + (rudel-obby-client::rudel-obby/obby_document/record/ins): use + `with-parsed-arguments' + (rudel-obby-client::rudel-obby/obby_document/record/del): use + `with-parsed-arguments' + + * obby/rudel-obby-server.el (header): fixed version + +2009-06-10 Jan Moringen + + * obby/rudel-obby-client.el + (rudel-obby-connection::rudel-local-operation): fixed wording in + comment + + * www/images/development.png (whole file): changed size 48x48 -> + 64x64 + * www/images/development.svg (whole file): changed size 48x48 -> + 64x64 + * www/images/download.png (whole file): changed size 48x48 -> + 64x64 + * www/images/download.svg (whole file): changed size 48x48 -> + 64x64 + * www/images/info.png (whole file): changed size 48x48 -> 64x64 + * www/images/info.svg (whole file): changed size 48x48 -> 64x64 + + * index.html (section introduction): wording fixes; link to Gobby + (whole file): removed external link class for sourceforge links + (section download): added link to INSTALL file in svn code browser + (section footer): fixed copyright + + * compatibility.html (section footer): fixed copyright + +2009-06-09 Jan Moringen + + * INSTALL (section REQUIREMENTS): removed ERT which is not + currently used + (section INSTALL): some wording and file name fixes + (section COMPILING): precise make command + + * www/index.html (section development): fixed mailing list and + issue tracker links; removed email address + + * www/compatibility.html (head): fixed charset + (section semantic): added missing

tag + + * www/compatibility.html (new file): compatibility information + * www/index.html (new file): start page + * www/style.css (new file): stylesheet + * www/images/development.png (new file): development icon + * www/images/development.svg (new file): development icon + * www/images/download.png (new file): download icon + * www/images/download.svg (new file): download icon + * www/images/email-link.png (new file): icon for email links + * www/images/external-link.png (new file): icon for external links + * www/images/info.png (new file): info icon + * www/images/info.svg (new file): info icon + * www/images/screenshot.png (new file): screenshot for the start + page + + * obby/rudel-obby-serverl.el + (rudel-obby-client::rudel-obby/obby_document_create): find unique + suffix for the new document; send suffix to clients + + * obby/rudel-obby.el (header): fixed license text + * obby/rudel-obby-util.el (header): fixed license text + * obby/rudel-obby-server.el (header): fixed license text + * obby/rudel-obby-client.el (header): fixed license text + * jupiter/jupiter.el (header): fixed license text + * jupiter/jupiter-operation.el (header): fixed license text + * jupiter/jupiter-nop.el (header): fixed license text + * jupiter/jupiter-insert.el (header): fixed license text + * jupiter/jupiter-delete.el (header): fixed license text + * jupiter/jupiter-compound.el (header): fixed license text + * rudel.el (header): fixed license text + * rudel-util.el (header): fixed license text + * rudel-tls.el (header): fixed license text + * rudel-telepathy.e (header): fixed license text + * rudel-speedbar.el (header): fixed license text + * rudel-overlay.el (header): fixed license text + * rudel-operators.el (header): fixed license text + * rudel-operations.el (header): fixed license text + * rudel-mode.el (header): fixed license text + * rudel-interactive.el (header): fixed license text + * rudel-compat.el (header): fixed license text + + * obby/rudel-obby-util.el (require cl): now required + (generic rudel-obby-char->byte): new generic; char positions -> + byte positions + (jupiter-insert::rudel-obby-char->byte): new method; char + positions -> byte positions + (jupiter-delete::rudel-obby-char->byte): new method; char + positions -> byte positions + (jupiter-compound::rudel-obby-char->byte): new method; char + positions -> byte positions + (jupiter-nop::rudel-obby-char->byte): new method; char positions + -> byte positions + (generic rudel-obby-byte->char): new generic; byte positions -> + char positions + (jupiter-insert::rudel-obby-byte->char): new method; byte + positions -> char positions + (jupiter-delete::rudel-obby-byte->char): new method; byte + positions -> char positions + (jupiter-compound::rudel-obby-byte->char): new method; byte + positions -> char positions + (jupiter-nop::rudel-obby-byte->char): new method; byte positions + -> char positions + * obby/rudel-obby-client.el + (rudel-obby-connection::rudel-local-operation): call + `rudel-obby-char->byte' before processing + (rudel-obby-connection::rudel-remote-operation): call + `rudel-obby-byte->char' before processing + * rudel.el (rudel-buffer-change-workaround-data): new variable; + holds change data for later use + (rudel-document::rudel-attach-to-buffer): add + `rudel-buffer-change-workaround' to 'before-change-functions' + (rudel-document::rudel-detach-from-buffer): remove + `rudel-buffer-change-workaround' from 'before-change-functions' + (rudel-buffer-change-workaround): new function; stores change data + for later use + +2009-06-07 Jan Moringen + + * obby/rudel-obby-client.el + (rudel-obby-connection::rudel-obby/net6_client_join): use + `with-parsed-arguments' + (rudel-obby-connection::rudel-obby/net6_client_part): use + `with-parsed-arguments' + (rudel-obby-connection::rudel-obby/obby_welcome): use + `with-parsed-arguments' + (rudel-obby-connection::rudel-obby/obby_sync_init): use + `with-parsed-arguments' + (rudel-obby-connection::rudel-obby/obby_sync_usertable_user): use + `with-parsed-arguments' + (rudel-obby-connection::rudel-obby/obby_user_colour): use + `with-parsed-arguments' + (rudel-obby-connection::rudel-obby/obby_sync_doclist_document): + use `with-parsed-arguments' + (rudel-obby-connection::rudel-obby/obby_document_create): use + `with-parsed-arguments' + (rudel-obby-connection::rudel-obby/obby_document_remove): use + `with-parsed-arguments' + (rudel-obby-connection::rudel-obby/obby_document): use + `with-parsed-arguments'; cleanup + (rudel-obby-connection::rudel-obby/obby_document/rename): use + `with-parsed-arguments' + (rudel-obby-connection::rudel-obby/obby_document/subscribe): use + `with-parsed-arguments' + (rudel-obby-connection::rudel-obby/obby_document/unsubscribe): use + `with-parsed-arguments' + (rudel-obby-connection::rudel-obby/obby_document/sync_chunk): use + `with-parsed-arguments' + (rudel-obby-connection::rudel-obby/obby_document/record): use + `with-parsed-arguments' + (rudel-obby-connection::rudel-obby/obby_document/record/ins): use + `with-parsed-arguments' + (rudel-obby-connection::rudel-obby/obby_document/record/del): use + `with-parsed-arguments' + +2009-06-06 Jan Moringen + + * obby/rudel-obby-client.el + (rudel-obby-connection::rudel-obby/net6_client_part): use `eql', + not `=' when calling `rudel-find-user' since the client id can be + nil + + * obby/rudel-obby-util.el (require jupiter): silence byte compiler + + * obby/rudel-obby-util.el (rudel-obby-dispatch): moved inside file + (with-parsed-arguments): new macro; executed forms with variables + bound to parsed arguments + +2009-06-04 Jan Moringen + + * rudel.el (require rudel-interactive): interactive functions use + `rudel-read-backend' and `rudel-read-document' + + * rudel.el (rudel-buffer-document): mark as permanent local + variable to prevent deletion in the event of a major-mode change + (rudel-document::rudel-attach-to-buffer): add (buffer-locally) + `rudel-handle-major-mode-change' to 'change-major-mode-hook' such + that it can repair damage caused by major-mode changes + (rudel-document::rudel-detach-from-buffer): remove + `rudel-handle-major-mode-change' from 'change-major-mode-hook' + (rudel-mode-changed-buffers) new variable; temporarily stores + buffers that underwent major-mode changes + (rudel-handle-major-mode-change): new function; schedules buffers + for repair after major-mode changes + (rudel-after-major-mode-change): new function; repairs buffer + objects after major-mode changes + +2009-06-03 Jan Moringen + + * rudel.el (rudel-buffer-has-document-p): use `buffer-local-value' + (rudel-buffer-document): use `buffer-local-value' + (rudel-set-buffer-document): added documentation string + +2009-06-02 Jan Moringen + + * rudel.el (rudel-handle-buffer-change): There are three cases + now: insert, delete and arbitrary changes; arbitrary changes + generate a delete and insert operation + + * rudel-mode.el (rudel-minor-keymap): added some comments + (global-rudel-minor-mode): extended documentation string; cleaned + up code; added comments + + * obby/rudel-obby-client.el + (rudel-obby-connection::rudel-obby/net6_client_part): fixed typo + in variable name client-id-numeric + +2009-05-28 Jan Moringen + + * obby/rudel-obby-util.el (header): Fixed version (1.0 -> 0.1) + + * obby/rudel-obby-client.el (header): Fixed version (1.0 -> 0.1) + (rudel-obby-connection::rudel-obby/obby_document/record/split): + introduced temporary variable + +2009-??-?? Jan Moringen + + * rudel.el (rudel-buffer-document): removed; replaced by + rudel-buffer-documents hash-table + (rudel-buffer-documents): new variable; a hash-table, which + associates documents to buffers + (rudel-buffer-has-document-p): + (rudel-buffer-document): + (rudel-set-buffer-document): + +2009-03-16 Jan Moringen + + * obby/rudel-obby-client.el + (rudel-obby-connection::rudel-obby/net6_client_part): do not crash + if the client id cannot be found + + * obby/rudel-obby.el (rudel-obby-backend::rudel-make-document): + specify value 1 for slot suffix + (rudel-obby-document::suffix): new slot; contains the suffix + number of the document + (rudel-obby-document::rudel-unique-name): new method; return + unique name based on document name and suffix + * obby/rudel-obby-client.el + (rudel-obby-connection::rudel-obby/obby_sync_doclist_document): do + not ignore the suffix when creating the document object + (rudel-obby-connection::rudel-obby/obby_document_create): do not + ignore the suffix when creating the document object + (rudel-obby-connection::rudel-obby/obby_document/rename): change + document name and suffix as requested + * rudel.el (rudel-document::rudel-unique-name): new method; + returns a unique name for the document + (rudel-document::rudel-suggested-buffer-name): new method; returns + a suggested name for the buffer attached to the document + (rudel-subscribe): use `rudel-suggested-buffer-name' instead of + the object name + * rudel-interactive.el (rudel-read-document): use the unique names + of the documents instead of the object names + +2009-02-27 Jan Moringen + + * rudel.el (rudel-document::rudel-attach-to-buffer): add hook to + detach document from the buffer when the buffer is killed + (rudel-document::rudel-detach-from-buffer): remove unpublish + function kill buffer hook + +2009-02-23 Jan Moringen + + * rudel.el (rudel-document): minor cleanup + (rudel-document::rudel-attach-to-buffer): stylistic changes + (rudel-document::rudel-detach-from-buffer): fixed argument order in + call to `rudel-set-buffer-document' + + * obby/rudel-obby-server.el + (rudel-obby-server::rudel-remove-client): Make sure there is a + user object before setting the status to offline + + * obby/rudel-obby-client.el (rudel-obby/net6_encryption_failed): + only fail if encryption has been requested in the first + place. otherwise, just carry on + + * rudel.el (rudel-document::rudel-attach-to-buffer): use + `rudel-set-buffer-document' + (rudel-document::rudel-detach-from-buffer): use + `rudel-set-buffer-document' + (rudel-buffer-has-document-p): new function; test whether a buffer + has an associated document object + (rudel-buffer-document): new function; returns associated document + object of a buffer + (rudel-set-buffer-document): new functions; sets associated + document object of a buffer + (rudel-handle-buffer-change): use `rudel-buffer-has-document-p' + (rudel-publish-buffer): use `rudel-buffer-has-document-p' + (rudel-unpublish-buffer): use `rudel-buffer-has-document-p' and + `rudel-buffer-document' + * rudel-mode.el (rudel-minor-keymap): use + `rudel-buffer-has-document-p' + + * obby/rudel-obby-client.el (rudel-obby/obby_document/rename): + new method; dummy implementation + + * obby/rudel-obby-client.el (rudel-obby/net6_client_join): + stylistic change + +2009-02-21 Jan Moringen + + * obby/rudel-obby-util.el (generic rudel-operation->message): new + generic function; serializes an operation + (jupiter-insert::rudel-operation->message): new method + (jupiter-delete::rudel-operation->message): new method + (jupiter-compound::rudel-operation->message): new method + (jupiter-nop::rudel-operation->message): new method + (rudel-message->operation): new function; deserializes an + operation from a received message + * obby/rudel-obby-client.el + (rudel-obby-connection::rudel-local-insert): do not construct + message string; use `rudel-operation->message' + (rudel-obby-connection::rudel-local-delete): do not construct + message string; use `rudel-operation->message' + (rudel-obby-connection::rudel-local-operation): new method; + handles operation objects that represent local operations + (rudel-obby-connection::rudel-remote-operation): new method; + handles operation objects that represent remote operations + (rudel-obby-connection::rudel-obby/obby_document/record/ins): + construct operation name correctly; do not call jupiter context to + transform operation + (rudel-obby-connection::rudel-obby/obby_document/record/del): + construct operation name correctly; do not call jupiter context to + transform operation + (rudel-obby-connection::rudel-obby/obby_document/record/split): + new method; handles split operation messages + (rudel-obby-connection::rudel-obby/obby_document/record/noop): new + method; handles nop messages + +2009-02-12 Jan Moringen + + * obby/rudel-obby.el (rudel-obby-backend::rudel-ask-connect-info): + ask whether to encrypt the connection + (rudel-obby-backend::rudel-connect): create connection object + capable of StartTLS encryption when encryption was requested + * obby/rudel-obby-client.el + (rudel-obby-connection::rudel-obby/net6_encryption): do not fail + when the server requests encryption + (rudel-obby-connection::rudel-obby/net6_encryption_begin): start + TLS encryption for the connection + (rudel-obby-connection::rudel-obby/net6_encryption_failed): new + method; stub + * rudel-tls.el (new file): StartTLS encryption for Rudel + * Project.ede ("rudel"): added rudel-tls.el + * Makefile (rudel_LISP): added rudel-tls.el + +2009-02-06 Jan Moringen + + * rudel-compat.el (header): fixed email address, keywords, legal + notice and file commentary + +2009-02-05 Jan Moringen + + * obby/rudel-obby.el (rudel-compat): require rudel-compat for + `read-color' + * rudel.el (rudel-compat): require rudel-compat for `read-color' + * rudel-interactive.el (rudel-compat): require rudel-compat for + `read-color' + * rudel-compat.el (new file): compatibility code + * Project.ede (rudel): added rudel-compat.el + * Makefile (rudel_LISP): regenerated: added rudel-compat.el + +2009-02-04 Jan Moringen + + * obby/rudel-obby.el (require rudel) + * obby/rudel-obby-util.el (require rudel) + * obby/rudel-obby-client.el (require rudel-obby): make compilation + succeed + + * rudel.el (include eieio-speedbar): I need it for now; I should + get rid of it later + + * INSTALL (REQUIREMENTS): added note that CVS version of cedet is + required + (INSTALLING): added subdirectories jupiter and obby in load path + listing; fixed name of autoload file + +2009-02-02 Jan Moringen + + * obby/rudel-obby.el (rudel-obby-backend::rudel-ask-connect-info) + (rudel-obby-backend::rudel-host, rudel-obby-replace-in-string) + * obby/rudel-obby-util.el (rudel-obby-dispatch) + * obby/rudel-obby-server.el (rudel-obby-client::rudel-obby/obby_document) + (rudel-obby-server::rudel-broadcast, rudel-obby-server::rudel-make-user) + * obby/rudel-obby-client.el (rudel-obby-connection::rudel-obby/net6_client_join) + (rudel-obby-connection::rudel-obby/obby_document) + * jupiter/jupiter-operation.el (jupiter-operation) + * rudel.el (rudel-backend, rudel-session, rudel-server-session) + (rudel-connection, rudel-document) + (rudel-document::rudel-attach-to-buffer) + (rudel-document::rudel-detach-from-buffer) + (rudel-document::rudel-insert, rudel-document::rudel-delete) + (rudel-change-color) + * rudel-util.el (rudel-assemble-line-fragments, rudel-loop-lines) + * rudel-overlay.el (rudel-make-author-overlay) + * rudel-interactive.el (rudel-read-backend, rudel-read-user-color) + (rudel-read-user, rudel-read-document): replaced 't by t + + * rudel-operators.el (rudel-overlay-operators::rudel-insert): + Fixed computation of insertion offset when appending to the end of + the buffer string + + * rudel.el (rudel-document::rudel-chunks): fixed invalid access to + last chunk for empty buffer + + * rudel.el (rudel-document::rudel-attach-to-buffer): fixed + incorrect slot reference + + * obby/rudel-obby-server.el + (rudel-obby-client::rudel-obby/obby_document/subscribe): minor + rearrangement of expressions + (rudel-obby-client::rudel-obby/obby_document/unsubscribe): minor + rearrangement of expressions + + * obby/rudel-obby-server.el + (rudel-obby-client::rudel-obby/obby_document/record/ins): added + documentation string + (rudel-obby-client::rudel-obby/obby_document/record/del): added + documentation string + (rudel-obby-server): cosmetic change + + * jupiter/jupiter.el (jupiter-context::jupiter-remote-operation): + improved documentation string; cosmetic changes + + * jupiter/jupiter-insert.el (jupiter-insert::jupiter-transform): + improved some comments + + * rudel.el (rudel-document::rudel-attach-to-buffer): renamed + some variables; added documentation string + (rudel-document::rudel-insert): improved documentation string + (rudel-document::rudel-chunks): do not create chunks when buffer + string is empty; improved comments + (rudel-choose-backend): compare number using `=' not `eq' + + * obby/rudel-obby-server.el + (rudel-obby-client::rudel-obby/obby_document/record/ins): use + `rudel-remote-operation' instead of `rudel-remote-insert' + (rudel-obby-client::rudel-obby/obby_document/record/del): use + `rudel-remote-operation' instead of `rudel-remote-delete' + * obby/rudel-obby-client.el (include rudel-operations): for + rudel-insert-op and rudel-delete-op + (rudel-obby-connection::rudel-obby/obby_document/sync_chunk): use + `rudel-remote-operation' with rudel-insert-op to insert chunks + (rudel-obby-connection::rudel-obby/obby_document/record/ins): use + `rudel-remote-operation' instead of `rudel-remote-insert' + (rudel-obby-connection::rudel-obby/obby_document/record/del): use + `rudel-remote-operation' instead of `rudel-remote-delete' + * jupiter/jupiter-operation.el (include rudel-operations): for + rudel-operation + (jupiter-operation): derived from rudel-operation + (jupiter-operation::jupiter-apply): removed; replaced by generic + `rudel-apply' + * jupiter/jupiter-nop.el (jupiter-nop::jupiter-apply): removed; + replaced by `rudel-apply' + (jupiter-nop::rudel-apply): new method; implements generic + `rudel-apply' + * jupiter/jupiter-insert.el (include rudel-operations): for + jupiter-insert-op + (jupiter-insert): derived from jupiter-insert-op + (jupiter-insert::jupiter-apply): removed; inherited from + jupiter-insert-op + (jupiter-insert::slot-missing): removed; inherited from + jupiter-insert-op + * jupiter/jupiter-delete.el (include rudel-operations): for + jupiter-delete-op + (jupiter-delete): derived from jupiter-delete-op + (jupiter-delete::jupiter-apply): removed; inherited from + jupiter-delete-op + (jupiter-delete::slot-missing): removed; inherited from + jupiter-delete-op + * jupiter/jupiter-compound.el (jupiter-compound::jupiter-apply): + removed; replaced by `rudel-apply' + (jupiter-compound::rudel-apply): new method; implements generic + `rudel-apply' + * rudel.el (include rudel-operations): everything is represented + in terms of operations + (include rudel-operators): operations apply changes to objects + through operators + (rudel-document::rudel-insert): new method; performs insert + operation + (rudel-document::rudel-delete): new method; performs delete + operation + (rudel-document::rudel-local-insert): removed; document does not + deal with aspects other than the actual insert and delete + (rudel-document::rudel-local-delete): removed; document does not + deal with aspects other than the actual insert and delete + (rudel-document::rudel-remote-insert): removed; document does not + deal with aspects other than the actual insert and delete + (rudel-document::rudel-remote-delete): removed; document does not + deal with aspects other than the actual insert and delete + (rudel-document::rudel-local-operation): new method; apply + operation using overlay and connection operators + (rudel-document::rudel-remote-operation): new method; apply + operation using document and overlay operators + (rudel-handle-buffer-change): realize buffer changes using + operations + * rudel-operators.el (new file): collections of operations on + Rudel data types + * rudel-operations.el (new file): operation classes + +2009-02-01 Jan Moringen + + * jupiter/jupiter-insert.el (jupiter-insert::jupiter-transform): + handle jupiter-nop + + * jupiter/jupiter-delete.el (jupiter-delete::jupiter-transform): + in inner cond, use matching pattern but empty body for + no-operation cases; in outer cond, handle jupiter-nop + + * jupiter/jupiter-compound.el (jupiter-compound): now derived from + jupiter-operation; should have been right from the start + +2009-01-31 Jan Moringen + + * rudel.el (rudel-default-username): Default name used when + prompting for user name; required by rudel-interactive + + * rudel-interactive.el (rudel-read-backend): fixed typo + +2009-01-30 Jan Moringen + + * jupiter/jupiter-insert.el (jupiter-insert::jupiter-transform): + fixed two offset calculations + * jupiter/jupiter-delete.el (jupiter-delete::jupiter-transform): + fixed offset calculation + + * rudel.el (rudel-backend::rudel-ask-connect-info): changed from + method to generic + (rudel-backend::rudel-connect): changed from method to generic + (rudel-backend::rudel-ask-host-info): changed from method to + generic + (rudel-backend::rudel-host): changed from method to generic + (rudel-backend::rudel-make-document): changed from method to + generic + (rudel-session::rudel-disconnect): changed from method to generic + (rudel-session::rudel-change-color-): changed from method to + generic + (rudel-session::rudel-publish): changed from method to generic + (rudel-session::rudel-subscribe-to): changed from method to + generic + (rudel-session::rudel-unsubscribe-from): changed from method to + generic + (rudel-session::rudel-local-insert): changed from method to + generic + (rudel-session::rudel-local-delete): changed from method to + generic + (rudel-session::rudel-remote-insert): changed from method to + generic + (rudel-session::rudel-remote-delete): changed from method to + generic + +2009-01-28 Jan Moringen + + * rudel-overlay.el (header): fixed version + (whole file): cosmetic changes + (rudel-author-overlay-p): added documentation string + (rudel-author-overlays): added documentation string + + * rudel-mode.el (rudel-minor-keymap): cosmetic changes + + * rudel-mode.el (rudel-minor-keymap): Separated session + participation and hosting items + + * obby/rudel-obby.el (rudel-obby-long-message-threshold): Added + documentation string + (rudel-obby-long-message-chunk-size): Added documentation string + (rudel-obby-backend::rudel-connect): Do not set process object; + this is done in the `initialize-instance' method of the base class + (rudel-obby-format-color): retrieve color components with + `color-values' + + * obby/rudel-obby-client.el + (rudel-obby-connection::rudel-remove-context): improved + documentation string + (rudel-obby-connection::rudel-publish): added a comment + (rudel-obby-connection::rudel-subscribe-to): added some comments; + cleaned up code + (rudel-obby-connection::rudel-unsubscribe-from): added a comment + (rudel-obby-connection::rudel-obby/net6_ping): added documentation + string + (rudel-obby-connection::rudel-obby/net6_encryption): added + documentation string + (rudel-obby-connection::rudel-obby/net6_login_failed): added + documentation string + (rudel-obby-connection::rudel-obby/net6_client_part): use `=' + instead of `eq' to compare client ids; fixed documentation string; + improved comments + (rudel-obby-connection::rudel-obby/obby_user_colour): use `=' + instead of `eq' to compare user ids + (rudel-obby-connection::rudel-obby/obby_document/sync_chunk): use + `=' instead of `eq' to compare user ids; use accessor + `user-id-numeric' + + * obby/rudel-obby-util.el (rudel-obby-dispatch): new functions; + dispatches to class methods based on message name; handles errors + properly + * obby/rudel-obby-server.el (rudel-obby-client::rudel-message): + use `rudel-obby-dispatch' to dispatch message + (rudel-obby-client::rudel-obby/obby_document): use + `rudel-obby-dispatch' to dispatch message + (rudel-obby-client::rudel-obby/obby_document/record): use + `rudel-obby-dispatch' to dispatch message + * obby/rudel-obby-client.el + (rudel-obby-connection::rudel-message): use `rudel-obby-dispatch' + to dispatch message; moved to a different location + (rudel-obby-connection::rudel-obby/obby_document): use + `rudel-obby-dispatch' to dispatch message + (rudel-obby-connection::rudel-obby/obby_document/record): use + `rudel-obby-dispatch' to dispatch message + + * obby/rudel-obby-util.el (generic rudel-message): made the method + a generic function and updated the documentation string + +2009-01-26 Jan Moringen + + * obby/rudel-obby.el (rudel-obby-document::revision): removed; The + slot is no longer needed + * obby/rudel-obby-server.el (require jupiter): uses jupiter + algorithm + (rudel-obby-client::rudel-obby/obby_document_create): add a + jupiter context for the document + (rudel-obby-client::rudel-obby/obby_document/subscribe): add a + jupiter context for the document + (rudel-obby-client::rudel-obby/obby_document/unsubscribe): remove + the jupiter context associated to the document + (rudel-obby-client::rudel-obby/obby_document/record/ins): + transformed the operation before applying it to the buffer; use + the respective jupiter contexts of the receivers when sending the + operation + (rudel-obby-client::rudel-obby/obby_document/record/del): + transformed the operation before applying it to the buffer; use + the respective jupiter contexts of the receivers when sending the + operation + (rudel-obby-server::contexts): new slot; stores jupiter contexts + for pairs of clients and documents + (rudel-obby-server::initialize-instance): new method; store an + empty hash-table in the `contexts' slot + (rudel-obby-server::rudel-find-context): find the jupiter context + for a pair of a client and a document + (rudel-obby-server::rudel-add-context): add a jupiter context for + a pair of a client and a document + (rudel-obby-server::rudel-remove-context): remove the jupiter + context for a pair of a client and a document + (rudel-obby-context-key): return a list of client id and document + id + * obby/rudel-obby-client.el (require jupiter): uses jupiter + algorithm + (rudel-obby-connection::contexts): new slot; stores jupiter + contexts for documents + (rudel-obby-connection::initialize-instance): new method; store an + empty hash-table in the `contexts' slot + (rudel-obby-connection::rudel-find-context): new method; return + the jupiter context for a document + (rudel-obby-connection::rudel-add-context): new method; add a + jupiter context for a document + (rudel-obby-connection::rudel-remove-context): new method; remove + the jupiter context for a document + (rudel-obby-connection::rudel-publish): add a jupiter context for + the new document + (rudel-obby-connection::rudel-subscribe-to): add a jupiter context + for the new document + (rudel-obby-connection::rudel-unsubscribe-from): remove the + jupiter context associated to the document + (rudel-obby-connection::rudel-local-insert): use revision + information from the jupiter context instead of the document; + supply the operation to the jupiter context + (rudel-obby-connection::rudel-local-delete): use revision + information from the jupiter context instead of the document; + supply the operation to the jupiter context + (rudel-obby-connection::rudel-obby/obby_document/record/ins): + transform the operation using the jupiter context instead of using + it unmodified + (rudel-obby-connection::rudel-obby/obby_document/record/del): + transform the operation using the jupiter context instead of using + it unmodified + +2009-01-22 Jan Moringen + + * obby/rudel-obby-client.el (rudel-obby-connection): removed + redundant slot `socket' (inherited from base class) + +2009-01-21 Jan Moringen + + * rudel-interactive.el (rudel-read-user): added comments + (rudel-allocate-buffer-clear-existing): added documentation string + (rudel-allocate-buffer-make-unique): added documentation string + +2009-01-19 Jan Moringen + + * rudel.el, rudel-util.el, rudel-telepathy.el, rudel-speedbar.el, + rudel-overlay.el, rudel-mode.el, jupiter/jupiter.el, + jupiter/jupiter-operation.el, jupiter/jupiter-nop.el, + jupiter/jupiter-insert.el, jupiter/jupiter-delete.el, + jupiter/jupiter-compound.el, obby/rudel-obby.el, + obby/rudel-obby-util.el, obby/rudel-obby-server.el, + obby/rudel-obby-client.el (header): changed email address + -> + + + * rudel-interactive.el (header): added keywords to file header + comment + + * jupiter/jupiter.el (new file): core Jupiter algorithm + * jupiter/jupiter-operation.el (new file): base class for Jupiter + operations + * jupiter/jupiter-insert.el (new file): insert operation for + Jupiter algorithm + * jupiter/jupiter-delete.el (new file): delete operation for + Jupiter algorithm + * jupiter/jupiter-nop.el (new file): no-operation for Jupiter + algorithm + * jupiter/jupiter-compound.el (new file): compound operation for + Jupiter algorithm + +2009-01-11 Jan Moringen + + * obby/rudel-obby.el (rudel-obby-user::client-id): added rationale + for type (or null integer) + * obby/rudel-obby-client.el + (rudel-obby-connection::rudel-obby/net6_client_join): added + documentation string; cosmetic changes + (rudel-obby-connection::rudel-obby/net6_client_part): use accessor + `rudel-client-id' when searching for the user object; set + client-id to nil in the user object; added documentation string + (rudel-obby-connection::rudel-obby/obby_sync_usertable_user): + store parsed user-id and color in temporaries + (rudel-obby-connection::rudel-obby/obby_user_colour):store parsed + color in a temporary; use accessor `rudel-id' when finding the + user object + + * obby/rudel-obby-client.el + (rudel-obby-connection::rudel-change-color-): new function; + implements changing the color + + * obby/rudel-obby-util.el + (rudel-obby-socket-owner::rudel-receive): improved documentation + string + +2009-01-05 Jan Moringen + + * INSTALL (REQUIREMENTS): proper list of requirements and sources + from which they can be obtained + (INSTALLING): initial version of installation instructions + (COMPLETING): some notes about compiling + * README (INTRODUCTION): short introduction + (GETTING STARTED): some notes about enabling Rudel, joining and + hosting sessions + +2009-01-04 Jan Moringen + + * obby/rudel-obby.el (rudel-obby-long-message-threshold): new + variable; threshold for message size, above which messages are + sent in multiple chunks + (rudel-obby-long-message-chunk-size): Chunk size used, when + chunking long messages. + (rudel-obby-user::client-id): allow value nil; added accessor; + added documentation string + (rudel-obby-send): new function; handles low-level aspects of + sending obby protocol messages + * obby/rudel-obby-util.el: new file; contains helper + functionality, mainly the class `rudel-obby-socket-owner', which + handles sending and receiving message + * obby/rudel-obby-server.el (includes): replaced rudel-obby with + rudel-obby-util, since it contains `rudel-obby-socket-owner' + (class rudel-obby-client): added base class + `rudel-obby-socket-owner' + (rudel-obby-client::rudel-receive): deleted, the functionality is + provided by the base class `rudel-obby-socket-owner' + (rudel-obby-client::rudel-send): deleted, the functionality is + provided by the base class `rudel-obby-socket-owner' + (rudel-obby-client::rudel-message): new method; called by base + class when a message is received; dispatches to appropriate + handler method + (rudel-obby-client::rudel-obby/obby_user_colour): minor change in + documentation string + * obby/rudel-obby-client.el (includes): replaced rudel-obby with + rudel-obby-util, since it contains `rudel-obby-socket-owner' + (class rudel-obby-connection): added base class + `rudel-obby-socket-owner' + (rudel-obby-connection::rudel-disconnect): just call next method; + it does what this method formerly did + (rudel-obby-connection::rudel-close): new method; end the session, + when the connection is closed + (rudel-obby-connection::rudel-receive): deleted, the functionality + is provided by the base class `rudel-obby-socket-owner' + (rudel-obby-connection::rudel-send): deleted, the functionality is + provided by the base class `rudel-obby-socket-owner' + (rudel-obby-connection::rudel-message): new method; called by + base class when a message is received; dispatches to appropriate + handler method + + * rudel.el (rudel-document::rudel-detach-from-buffer): do nothing, + if the document is not attached to any buffer + + * obby/rudel-obby.el (rudel-obby-user): added missing accessor + `rudel-connected' + + * obby/rudel-obby-server.el + (rudel-obby-client::rudel-obby/net6_client_login): transmit number + of synchronization items; transmit list of disconnected users + + * obby/rudel-obby-server.el + (rudel-obby-client::rudel-obby/obby_document/record/ins): + broadcast only to clients, which are subscribed to the document; + send user id of author instead of client id + (rudel-obby-client::rudel-obby/obby_document/record/del): + broadcast only to clients, which are subscribed to the document; + send user id of author instead of client id + (rudel-obby-client::rudel-subscribed-clients-not-self): new + method; return a list of clients subscribed to a document + excluding the client itself. + + * obby/rudel-obby-server.el (rudel-obby-server::next-client-id): + first id should be 1, not 0; fixed initform accordingly + (rudel-obby-server::next-user-id): + first id should be 1, not 0; fixed initform accordingly + + * rudel.el (rudel-document::rudel-chunks): fixed void variable + `chunks-' -> `augmented-chunks' + + * obby/rudel-obby-server.el + (rudel-obby-client::rudel-obby/obby_document/subscribe): send + individual buffer chunks with their respective authors instead of + one large string without author information + * rudel.el (rudel-document::rudel-chunks): new method; return a + list of buffer position ranges and the respective authors, that + wrote the text + +2009-01-03 Jan Moringen + + * obby/rudel-obby.el (rudel-obby-backend::rudel-host): cleanup + * obby/rudel-obby-client.el + (rudel-obby-connection::rudel-local-insert): accept arguments + `position' and `data' instead of `from', `to' and `what'; since + position is zero-based, transmit it literally + (rudel-obby-connection::rudel-local-delete): instead of `from' and + `to' accept argument `position'; since position is + zero-based. transmit it literally + (rudel-obby-connection::rudel-obby/obby_document/record): + identified remaining arguments; dispatch actions to appropriate + methods; identify methods by interning their symbols + (rudel-obby-connection::rudel-obby/obby_document/record/ins): new + method; handle remote insert actions + (rudel-obby-connection::rudel-obby/obby_document/record/del): new + method; handle remote delete actions + * rudel.el (includes): include rudel-overlay + (rudel-document::rudel-detach-from-buffer): improved readability + (rudel-document::rudel-local-insert): instead of redundant + arguments `from', `to' and `what' accept only `position' and + `data'; update overlays + (rudel-document::rudel-local-delete): instead of redundant + arguments `from', `to' and `length' accept only `position' and + `length'; update overlays + (rudel-document::rudel-remote-insert): renamed arguments `from' -> + `position', `what' -> `data'; update overlays + (rudel-document::rudel-remote-delete): replaced arguments `from' + and `to' by `position'; update overlays + (rudel-handle-buffer-change): call rudel-local-{insert, delete} + with changed arguments + +2009-01-01 Jan Moringen + + * rudel.el (rudel-session::rudel-unsubscribed-documents): new + method; returns documents, to which the user associated with the + session is not yet subscribed + (rudel-subscribe): when called interactively, use + `rudel-unsubscribed-documents' to offer only unsubscribed + documents in completing read + + * rudel-interactive.el (rudel-read-user-name): new function; read + a user name; could be used to enforce certain constraints on the + name + (rudel-read-user-color): new function; read a user color; could be + used to enforce certain constraints on the color + + * obby/rudel-obby.el (rudel-obby-backend::rudel-ask-host-info): + new method; ask user for port number + (rudel-obby-backend::rudel-host): new method; require obby server + component, make the network process and construct the server + * obby/rudel-obby-server.el (new file): initial revision of obby + server for rudel + * obby/rudel-obby-client.el (header section): added keyword and + x-rcs + (rudel-obby-connection::rudel-publish): new method; send document + to server + (rudel-obby-connection::rudel-unsubscribe-from): send unsubscribe + notification to server + (rudel-obby-connection::rudel-local-insert): cleanup + (rudel-obby-connection::rudel-local-delete): new method; send + delete record to server and increase local revision + (rudel-obby-connection::rudel-obby/obby_document/sync_chunk): + improved user locating code; do not complain, when the user is not + found + (rudel-obby-connection::rudel-obby/obby_document/record): removed + useless debug message + * rudel.el (class rudel-session): update documentation string + (class rudel-server-session): new class; base class for server + sessions + (rudel-choose-backend): fixed void-variable when called + interactively + (rudel-host-session): provided initial implementation, which uses + the selected backend to create a server + (rudel-subscribe): call `set-window-buffer' instead of + `show-buffer' + + * obby/rudel-obby.el (header section): fixed history + (rudel-obby-version): new constant; holds version of the obby + backend + (rudel-obby-protocol-version): new constant; holds the obby + protocol version understood by the backend + (rudel-obby-document::rudel-both-ids): new method; useful when + locating documents by means of owner and document id + + * rudel-mode.el (header section): added keywords + + * rudel-interactive.el (header section): added file comment + +2008-12-30 Jan Moringen + + * rudel.el (class rudel-session): converted to base class for + other session classes; removed slots `connection' and `self' which + are specific for client sessions + (rudel-session::rudel-end): empty now; derived classes do the work + (rudel-session::rudel-add-user): use `object-add-to-list' + (rudel-session::rudel-remove-user): use `object-remove-from-list' + (rudel-session::rudel-add-document): use `object-add-to-list' + (rudel-session::rudel-remove-document): use + `object-remove-from-list' + (class rudel-client-session): derived from `rudel-session'; + additional slots `connection' and `self' + (rudel-client-session::rudel-end): detach buffers from documents + and call `rudel-disconnect' on connection + (class rudel-connection): documentation string + (rudel-connection::rudel-disconnect): remove hook + `after-change-functions' only locally + (rudel-join-session): construct a proper session name; store + backend object in the session; some comments + + * obby/rudel-obby.el (rudel-obby-document): cleanup; improved + documentation strings + + * rudel-overlay.el (new file): functions for managing overlays, + which indicate the authors of contributions in collaborative + buffers + + * rudel.el (rudel-allocate-buffer-function): customization option + for buffer allocation function + (rudel-subscribe): call buffer allocation function instead of just + using the provided name + * rudel-interactive.el (rudel-allocate-buffer-clear-existing): new + function; in case of a conflict, allocate buffer for subscription + by clearing the existing buffer + (rudel-allocate-buffer-make-unique): new function; in case of a + conflict, allocate buffer for subscription by producing a unique + name + + * rudel.el (customization): added customization group definition + for `rudel' + + * obby/rudel-obby.el (includes): require `rudel-util' instead of + `rudel' + (rudel-connect): attach connection to socket object + (rudel-obby-document): removed slot `subscribed' as it is now + contained in the base class `rudel-document' + (rudel-obby-escape-string): call `rudel-obby-replace-in-string' + instead of `obby-replace-in-string' + (rudel-obby-unescape-string): call `rudel-obby-replace-in-string' + instead of `obby-replace-in-string' + * obby/rudel-obby-client.el + (rudel-obby-connection::rudel-state-change): required by + `rudel-sentinel-dispatch' + (rudel-obby-connection::rudel-subscribe-to): do not touch slot + `subscribed' + (rudel-obby-connection::rudel-obby/obby_sync_doclist_document): + retrieve subscribed users and add to `subscribed' slot + (rudel-obby-connection::rudel-obby/obby_document_create): add + document owner to `subscribed' slot + (rudel-obby-connection::rudel-obby/obby_document/subscribe): add + user to `subscribed' slot + (rudel-obby-connection::rudel-obby/obby_document/unsubscribe): + remove user from `subscribed' slot + * rudel.el (rudel-document): added slot `subscribed' which + contains a list of subscribed users + (rudel-subscribe): do not use `rudel-unsubscribed-documents'; + instead list all documents for now + (rudel-publish-buffer): add self to `subscribed' slot + + * rudel-util.el (rudel-state-change): cleanup; added comments + + * rudel-mode.el (rudel-minor-keymap): Fixed invalid menu + definition + + * obby/rudel-obby.el (whole file): moved class + `rudel-obby-connection' and related methods into file + `rudel-obby-client.el' + (rudel-obby-backend): added capability `track-subscriptions' + (rudel-obby-backend::rudel-connect): require `rudel-obby-client' + before constructing the connection object + * obby/rudel-obby-client.el (new file): moved class + `rudel-obby-connection' and related methods into this file + +2008-12-29 Jan Moringen + + * rudel.el (rudel-connection::rudel-change-color-): new method + handles color changes + (rudel-change-color): added basic implementation, which checks the + backend, asks the user for a new color and calls the connection + object + + * rudel-util.el (rudel-socket-owner::rudel-state-change): called + when the state of the connection changes + (rudel-socket-owner::rudel-close): called when the connection is + closed + (rudel-sentinel-dispatch): the argument is a message, not the + actual state, the state is retrieved with `process-state' + + * rudel-speedbar.el (whole file): cleanup; improved comments + + * rudel-mode.el (whole file): improved comments + (rudel-read-{backend, document}): moved to rudel-interactive.el + (rudel-minor-keymap): added key binding for `rudel-change-color'; + added `options' menu item + + * rudel-interactive.el (whole file): user interaction functions + used by all Rudel components + + * rudel-util.el (whole file): utility functions used by all Rudel + components + + * rudel.el (whole file): improved comments + (rudel-backend::make-document): new function create an appropriate + document object for the backend + (rudel-session::rudel-end): added documentation string + (rudel-session::rudel-add-user): added documentation string + (rudel-session::rudel-remove-user): added documentation string + (rudel-session::rudel-remove-document): new method; remove + document from session + (rudel-connection::rudel-publish): new function; called when a + buffer is published + (rudel-connection::rudel-unsubscribe-from): new function; called + when a subscription is canceled + (class rudel-user): added documentation strings + (class rudel-document): added documentation strings + (rudel-document::rudel-attach-to-buffer): add to + `after-change-functions' hook only for the buffer in question; + added some comments + (rudel-document::rudel-detach-from-buffer): cleanup + (rudel-document::rudel-remote-insert): added comments + (rudel-document::rudel-remote-delete): added comments + (rudel-handle-buffer-change): added comments + (rudel-choose-backend): added comments + (rudel-end-session): additional error check + (rudel-subscribe): call `rudel-unsubscribed-documents' when + completing document name; added comments + (rudel-unpublish-buffer): call `rudel-detach-from-buffer' and + `rudel-unsubscribe-from'; added comments + + * obby/rudel-obby.el (whole file): improved comments + (rudel-obby-backend::rudel-ask-connect-info): removed :override + tag; added comments + (rudel-obby-backend::rudel-connect): removed :override tag; use + `make-network-process' instead of `open-network-stream' and attach + filter and sentinel right away; removed some debug code + (rudel-obby-backend::rudel-disconnect): removed :override tag + (rudel-obby-backend::rudel-subscribe-to): removed :override tag + (rudel-obby-backend::rudel-local-insert): removed :override tag + (rudel-obby-backend::rudel-local-delete): removed :override tag + (rudel-obby-backend::rudel-make-document): new method; creates a + new rudel-obby-document object + (rudel-obby-backend::rudel-available-document-id): obtains an + unused document id, which can be assigned to a new document + (class rudel-obby-connection): removed useless `host' and `port' + slots + (rudel-obby-connection::rudel-receive): removed some debug code + (rudel-obby-connection::rudel-send): removed some debug code + (rudel-obby-connection::rudel-obby/net6_client_join): fixed syntax + error + (class rudel-obby-user): added accessors for slots `client-id' and + `user-id' + (rudel-obby-user::eieio-speedbar-description): removed :override + tag + (rudel-obby-user::eieio-speedbar-object-buttonname): removed + :override tag + (class rudel-obby-document): added accessors and documentation for + slot `id' + (rudel-obby-document::eieio-speedbar-object-buttonname): removed + :override tag + (rudel-obby-replace-in-string): new function; replace a set of + patterns in a string + (rudel-obby-escape-string): new function; replace obby control + characters with their escape sequences + (rudel-obby-unescape-string): new function; inverse of + `rudel-obby-escape-string' + (rudel-obby-parse-color): added documentation + (rudel-obby-format-color): added documentation + (rudel-obby-assemble-message): properly escape message components + (rudel-obby-parse-message): properly unescape message components + + * README (whole file): some initial notes + * INSTALL (whole file): some initial notes + +2008-12-02 Jan Moringen + + * obby (directory): new directory for files belonging to the obby + backend + * rudel-obby.el (whole file): moved to `obby' directory + * obby/rudel-obby.el (whole file): moved here from parent + directory + + * Changelog (whole file): renamed to `ChangeLog?' + * ChangeLog? (whole file): fixed name + + * INSTALL (whole file): added + + * rudel.el (whole file): fixed some comments, removed some test + code + (rudel-version): new variable; global Rudel version + (rudel-sessions): removed; we only allow one session for now + (rudel-session): cleaned up + (rudel-session::rudel-end): cleaned up; added some comments + (rudel-session::rudel-add-user): cosmetic changes + (rudel-session::rudel-remove-user): cosmetic changes + (rudel-session::rudel-find-user): cosmetic changes + (rudel-session::rudel-add-document): cosmetic changes + (rudel-session::rudel-find-document): cosmetic changes + (rudel-backend::rudel-connect): improved documentation string + (rudel-backend::rudel-ask-host-info): renamed from + `rudel-ask-listen-info' + (rudel-backend::rudel-host): renamed from `rudel-listen' + (rudel-document::rudel-attach-to-buffer): cosmetic changes + (rudel-document::rudel-remote-insert): cleaned up + (rudel-document::rudel-remote-delete): cleaned up + (rudel-load-backends): cosmetic changes + (rudel-choose-backend): fixed message display + (rudel-host-session): improved documentation string + (rudel-change-color): raise an error since this is not yet + implemented + (rudel-subscribe): added comments + (rudel-unpublish-buffer): raise an error if the buffer has not + been published + + * rudel.el (whole file): cleanup up some obsolete code diff --git a/emacs.d/lisp/rudel/INSTALL b/emacs.d/lisp/rudel/INSTALL new file mode 100644 index 0000000..2555d09 --- /dev/null +++ b/emacs.d/lisp/rudel/INSTALL @@ -0,0 +1,58 @@ +* REQUIREMENTS + + Rudel is developed and tested only with GNU Emacs and therefore + unlikely to run on other Emacs variants like XEmacs. + + To use Rudel, the following software is required: + +** GNU Emacs 22 or above + Rudel should work with recent versions of GNU Emacs starting from + version 22. Older versions of GNU Emacs or XEmacs may or not work + but are not actively tested. + +** Collection of Emacs Development Environment Tools (CEDET) + Cedet contains the object system Eieio, which is used in Rudel's + object-oriented implementation. Cedet can be obtained from + http://cedet.sourceforge.net/ + + IMPORTANT: It is necessary to use at least the 1.0pre6 version of + CEDET since it fixes a serious problem in the object system Eieio. + + As of October 2009, Eieio is included in GNU Emacs. If you are + using a version built since then, you do not have to install it + yourself. + +** GnuTLS (optional) + Connections to Gobby servers require the gnutls-cli program. + +** Avahi (optional) + The Avahi daemon (http://avahi.org) is required for automatic + session discovery and advertising. + + A version of GNU Emacs with Zeroconf support (GNU Emacs 23 or + above) is required to talk to the Avahi daemon. + +* INSTALLING + + To install Rudel, download a released version or the current + development version from http://sourceforge.net/projects/rudel/ and + place the code in any directory you like. + + Once Eieio (see CEDET in the REQUIREMENTS section above) is + installed, add the following to your personal Emacs configuration: + + (load-file "/PATH/TO/RUDEL/rudel-loaddefs.el") + + This will set Rudel up to be loaded on demand when one of the + commands `rudel-join-session', `rudel-host-session' or + `global-rudel-minor-mode' is invoked. + +* COMPILING + + In order to achieve better performance, Emacs can byte-compile the + Rudel code. This can be done by opening rudel-compile.el in Emacs + and invoking M-x eval-buffer. + +Local variables: +mode: org +end: diff --git a/emacs.d/lisp/rudel/Project.ede b/emacs.d/lisp/rudel/Project.ede new file mode 100644 index 0000000..ff3a264 --- /dev/null +++ b/emacs.d/lisp/rudel/Project.ede @@ -0,0 +1,24 @@ +;; Object rudel +;; EDE project file. +(ede-proj-project "rudel" + :name "rudel" + :version "0.3" + :file "Project.ede" + :targets (list + (ede-proj-target-elisp-autoloads "autoloads" + :name "autoloads" + :path "" + :autoload-file "rudel-loaddefs.el" + :autoload-dirs '("." "jupiter" "obby" "wave" "zeroconf") + ) + (ede-proj-target-elisp "compile" + :name "rudel" + :path "" + :source '("rudel.el" "rudel-util.el" "rudel-mode.el" "rudel-interactive.el" "rudel-overlay.el" "rudel-speedbar.el" "rudel-operators.el" "rudel-operations.el" "rudel-compat.el" "rudel-tls.el" "rudel-errors.el" "rudel-state-machine.el" "rudel-backend.el" "rudel-protocol.el" "rudel-session-initiation.el" "rudel-icons.el" "rudel-hooks.el" "rudel-transport.el" "rudel-chat.el") + ) + ) + :mailinglist "rudel-devel@lists.sourceforge.net" + :web-site-url "http://rudel.sourceforge.net/" + :web-site-directory "/scymtym,rudel@web.sourceforge.net:/home/groups/r/ru/rudel/htdocs" + :configuration-variables 'nil + ) diff --git a/emacs.d/lisp/rudel/README b/emacs.d/lisp/rudel/README new file mode 100644 index 0000000..aeacba7 --- /dev/null +++ b/emacs.d/lisp/rudel/README @@ -0,0 +1,88 @@ +* INTRODUCTION + + Rudel is collaborative editing environment for GNU Emacs. Its + purpose is to share buffers with other users in order to edit the + contents of those buffers collaboratively. Rudel supports multiple + backends to enable communication with other collaborative editors + using different protocols, though currently Obby (for use with the + Gobby editor) is the only fully-functional one. + + Since Rudel is not an application, but an extension to Emacs, it is + not started and used like most applications (not even Emacs + applications like Gnus). Rudel mostly works in the background to + change the behavior of the set of Emacs buffers for which it has + been activated. + + The user interface consists of a set of key bindings, a menu entry + and some visual status indicators, which are added to the text and + mode line of buffers for which Rudel has been activated. + +* GETTING STARTED + + Assuming Rudel has already been installed and auto loading has been + set up, a global Rudel mode can be enabled as follows: + + : M-x global-rudel-minor-mode + + This will enabled Rudel's key bindings and menu entry. + +** JOINING A SESSION + + : M-x rudel-join-session [ C-c c j ] + + Depending on the installed Rudel backends, system environment and + configuration, a number of questions will be asked, followed by an + attempt to join session described by your answers. + + A typical example of the questions asked when joining a session may + look like this: + + Server: localhost RET + Port (default 6522): RET + Username: jan RET + Color: light sky blue RET + Use Encryption (y or n): n RET + Global Password: RET + User Password: RET + + IMPORTANT: For sessions using the obby backend (like in the example + above), the following restriction has to be taken into account: + + When the server is Rudel inside an Emacs process: + Encryption cannot be used currently in this case. Consequently + the answer to the `Use Encryption (y or n):' prompt above has to + be `n RET'. + + When the server is a Gobby process: + Gobby only supports encrypted connections. So the answer has to + be `y RET' is this case. + + It is possible to configure frequently used sessions using the + customization options `rudel-configured-sessions'. When one or more + sessions are configured, `rudel-join-session' will provide choices + like "my-configured-session", ... and "ask-protocol". Selecting + "ask-protocol" invokes the behavior described above. Selecting one + of the configured sessions connects to that session without asking + for all the data. + +** HOSTING A SESSION + + : M-x rudel-host-session [ C-c c h ] + + Note that the session starts out without any participating users + (This is sometimes referred to as being a dedicated server). If you + want to participate in the session you host, you have to join it as + described above. + +* KNOWN ISSUES + + + Publishing eshell buffers will cause your session to be + disconnected since eshell disables the hooks that Rudel uses to + catch changes to the buffer. As a workaround, you can use M-x + ansi-term or another terminal emulator. + +* LICENSE + + Rudel is licensed under the same terms as GNU Emacs. + +Local variables: +mode: org +end: diff --git a/emacs.d/lisp/rudel/TODO b/emacs.d/lisp/rudel/TODO new file mode 100644 index 0000000..e5e4bf4 --- /dev/null +++ b/emacs.d/lisp/rudel/TODO @@ -0,0 +1,436 @@ +* Future +** NEW Handle messages spanning multiple frames + + Component :: beep-transport + + Type :: defect + + Reporter :: jan + + Assigned :: +** NEW Operation log can grow beyond all bounds (#37) + + Component :: obby-general + + Type :: defect + + Reporter :: jan + + Assigned :: + When no remote operations are received, the log of local operation + is not reset and therefore grows beyond all bounds. +** NEW Terminating sessions does not work (#47) + + Component :: rudel-general + + Type :: defect + + Reporter :: jan + + Assigned :: + There is a menu entry for terminating sessions which are hosted by + Rudel, but it does not do anything. +** NEW Rename document message is not understood (#7) + + Component :: obby-client + + Type :: defect + + Reporter :: jan + + Assigned :: +** NEW Rename document message is not understood (#8) + + Component :: obby-backend + + Type :: defect + + Reporter :: jan + + Assigned :: +** TODO Notification mechanism + + Component :: user-interface + + Type :: task + + Reporter :: jan + + Type :: task + + Assigned :: +** TODO SubEthaEdit client functionality + + Component :: subethaedit-backend + + Type :: task + + Reporter :: jan + + Assigned :: +** TODO Show cursor positions of other users (#5) + + Component :: rudel-user-interface + + Type :: enhancement + + Reporter :: jan + + Assigned :: +** TODO Some kind of server log buffer (#11) + + Component :: rudel-general + + Type :: enhancement + + Reporter :: jan + + Assigned :: + It would be nice to log server events. This could be done in a + separate buffer or using a dedicated mechanism like + rudel-notification. +** TODO Backends should be able to offer additional menu items (#14) + + Component :: rudel-general + + Type :: enhancement + + Reporter :: jan + + Assigned :: +** TODO Obby session can be protected by passwords (#15) + + Component :: obby-general + + Type :: enhancement + + Reporter :: jan + + Assigned :: +** TODO Obby users can protect their accounts with passwords (#16) + + Component :: obby-general + + Type :: enhancement + + Reporter :: jan + + Assigned :: + The Gobby implementation is in obby/inc/server_buffer.hpp:851 +** TODO Zeroconf session notification (#52) + + Component :: zeroconf + + Type :: task + + Reporter :: jan + + Assigned :: + Watch interesting Zeroconf services and use `rudel-notify` if new + services are discovered +** TODO State machine diagram (#59) + + Component :: obby-client + + Type :: task + + Reporter :: jan + + Assigned :: +** TODO State machine diagram (#60) + + Component :: obby-backend + + Type :: task + + Reporter :: jan + + Assigned :: +** TODO Send key presses as chat messages (#61) + + Component :: rudel-general + + Type :: task + + Reporter :: Jan + + Assigned :: + Sending key presses as chat messages could be really useful for + somebody something using rudel. +** STARTED BEEP transport + + Component :: beep-transport + + Type :: task + + Reporter :: jan + + Assigned :: jan +** STARTED Reference manual (#46) + + Component :: documentation + + Type :: task + + Reporter :: jan + + Assigned :: jan + In addition to the `README`, a proper reference manual would be + nice. At some point, complete info documentation may be + desirable. Docbook seems to be the best approach since we get (at + least): + + Pdf + + Html + + Info + + +* Milestone rudel-0.4 +** TODO Telepathy transport + + Component :: telepathy-backend + + Type :: task + + Reporter :: jan + + Assigned :: + +* Milestone rudel-0.3 +** TODO Multiple username/password attempts in one login attempt + + Component :: rudel-general + + Type :: task + + Reporter :: jan + + Assigned :: +** TODO Infinote client functionality + + Component :: infinote-backend + + Type :: task + + Reporter :: jan + + Assigned :: +** TODO Support for trees of documents + + Component :: rudel-general + + Type :: task + + Reporter :: jan + + Assigned :: +** NEW Get rid of error calls in the server (#58) + + Component :: obby-backend + + Type :: defect + + Reporter :: jan + + Assigned :: + It makes no sense to call `error` when something goes wrong in + server code that is called from the process filter. Instead, we + should try to recover. +** NEW Global mode line publish state mode does not work for all new buffers (#55) + + Component :: rudel-user-interface + + Type :: defect + + Reporter :: jan + + Assigned :: + `global-mode-line-publish-state-mode` is define using + `define-globalized-mode`. This seems to only enabled the associated + minor mode for buffers create by `find-file` and after major mode + changes. The minor mode is not activated for buffers create by + `create-buffer`. Since this is used when subscribing to documents, + this is a problem. +** NEW Handle net6_encryption_info messages (#57) + + Component :: obby-backend + + Type :: defect + + Reporter :: jan + + Assigned :: +** TODO Only read color hue, not complete colors (#53) + + Component :: rudel-user-interface + + Type :: enhancement + + Reporter :: jan + + Assigned :: + Taking control over saturation and value away from the user makes + it impossible to choose unreadable colors. + + +* Milestone rudel-0.2 +** DONE Use state pattern (#18) + + Component :: obby-backend + + Resolution :: fixed + + Type :: enhancement + + Priority :: minor + + Reporter :: jan +** FIXED Server buffers go out of sync when multi-byte characters are used (#56) + + Component :: obby-backend + + Resolution :: fixed + + Type :: defect + + Priority :: major + + Reporter :: jan +** FIXED Terminate connections properly when something goes wrong (#51) + + Component :: rudel-general + + Resolution :: fixed + + Type :: defect + + Priority :: major + + Reporter :: jan +** FIXED Removing documents does not work (#45) + + Component :: obby-backend + + Resolution :: fixed + + Type :: defect + + Priority :: major + + Reporter :: jan +** FIXED Search list of offline users when new users log in (#44) + + Component :: obby-backend + + Resolution :: fixed + + Type :: defect + + Priority :: major + + Reporter :: jan +** DONE Reference card (#2) + + Component :: documentation + + Resolution :: fixed + + Type :: enhancement + + Priority :: minor + + Reporter :: jan +** DONE ode-line indicator of buffer status (#6) + + Component :: rudel-user-interface + + Resolution :: fixed + + Type :: enhancement + + Priority :: minor + + Reporter :: jan +** FIXED Author overlay face may not exist (#54) + + Component :: rudel-user-interface + + Resolution :: fixed + + Type :: defect + + Priority :: major + + Reporter :: jan +** DONE Visualization of user status (#9) + + Component :: rudel-user-interface + + Resolution :: fixed + + Type :: enhancement + + Priority :: minor + + Reporter :: jan +** FIXED Update overlays when users change colors (#23) + + Component :: rudel-user-interface + + Resolution :: fixed + + Type :: defect + + Priority :: minor + + Reporter :: jan +** DONE Allow to toggle display of author overlays (#33) + + Component :: rudel-user-interface + + Resolution :: fixed + + Type :: enhancement + + Priority :: minor + + Reporter :: jan +** DONE Update file headers (#50) + + Component :: documentation + + Resolution :: fixed + + Type :: task + + Priority :: trivial + + Reporter :: jan +** DONE Proper Zeroconf support (#21) + + Component :: zeroconf + + Resolution :: fixed + + Type :: enhancement + + Priority :: minor + + Reporter :: jan +** DONE Add discovery component (#22) + + Component :: obby-general + + Resolution :: fixed + + Type :: enhancement + + Priority :: minor + + Reporter :: jan +** DONE Define initialize-instance with slots or &rest slots? (#49) + + Component :: rudel-general + + Resolution :: fixed + + Type :: task + + Priority :: major + + Reporter :: jan +** DONE Use oref to get object names (#24) + + Component :: rudel-general + + Resolution :: fixed + + Type :: enhancement + + Priority :: minor + + Reporter :: jan +** DONE Overlays should be removed when a buffer is detached from its document (#39) + + Component :: rudel-user-interface + + Resolution :: fixed + + Type :: enhancement + + Priority :: minor + + Reporter :: jan +** FIXED Unsafe use of (call-next-method) (#48) + + Component :: rudel-general + + Resolution :: fixed + + Type :: defect + + Priority :: major + + Reporter :: jan +** FIXED Handle `net6_login_failed' message (#10) + + Component :: obby-backend + + Resolution :: fixed + + Type :: defect + + Priority :: minor + + Reporter :: jan +** DONE Add debug hints to macros (#43) + + Component :: rudel-general + + Resolution :: fixed + + Type :: enhancement + + Priority :: minor + + Reporter :: jan +** DONE Use state pattern (#17) + + Component :: obby-backend + + Resolution :: fixed + + Type :: enhancement + + Priority :: minor + + Reporter :: jan +** DONE Use with-parsed-arguments (#40) + + Component :: obby-general + + Resolution :: fixed + + Type :: enhancement + + Priority :: minor + + Reporter :: jan + + +* Milestone rudel-0.1 +** FIXED User names and colors are not checked for conflicts (#12) + + Component :: obby-backend + + Resolution :: fixed + + Type :: defect + + Priority :: minor + + Reporter :: jan +** DONE Write some html for rudel.sourceforge.net (#27) + + Component :: www + + Resolution :: fixed + + Type :: task + + Priority :: major + + Reporter :: jan +** INVALID Repeated publishing leads to multiple document instances (#30) + + Component :: obby-backend + + Resolution :: invalid + + Type :: defect + + Priority :: minor + + Reporter :: jan +** FIXED Document suffixes are not handled properly (#42) + + Component :: obby-backend + + Resolution :: fixed + + Type :: defect + + Priority :: major + + Reporter :: jan +** DONE Fix license texts (#32) + + Component :: rudel-general + + Resolution :: fixed + + Type :: enhancement + + Priority :: trivial + + Reporter :: jan +** WONTFIX Overlays break on last character (#29) + + Component :: rudel-user-interface + + Resolution :: worksforme + + Type :: defect + + Priority :: minor + + Reporter :: jan +** FIXED Encodings are not handled in obby backend (#1) + + Component :: obby-general + + Resolution :: fixed + + Type :: defect + + Priority :: major + + Reporter :: jan +** FIXED Major mode changes break subscribed buffers (#19) + + Component :: rudel-general + + Resolution :: fixed + + Type :: defect + + Priority :: major + + Reporter :: jan +** FIXED Editing in overwrite mode breaks synchronization (#35) + + Component :: rudel-general + + Resolution :: fixed + + Type :: defect + + Priority :: major + + Reporter :: jan +** FIXED Yanking produces insertion and immediate deletion of the region (#36) + + Component :: rudel-general + + Resolution :: fixed + + Type :: defect + + Priority :: major + + Reporter :: jan +** FIXED Documents with identical names but distinct suffixes map to same buffer (#41) + + Component :: obby-backend + + Resolution :: fixed + + Type :: defect + + Priority :: major + + Reporter :: jan +** FIXED Killing a buffer does not detach it from its document (#38) + + Component :: rudel-general + + Resolution :: fixed + + Type :: defect + + Priority :: major + + Reporter :: jan +** INVALID Rudel client crashes Gobby (#25) + + Component :: obby-general + + Resolution :: invalid + + Type :: defect + + Priority :: major + + Reporter :: jan +** DONE Add screenshot of session with Gobby (#20) + + Component :: www + + Resolution :: fixed + + Type :: task + + Priority :: trivial + + Reporter :: jan +** DONE Replace 't with t (#34) + + Component :: rudel-general + + Resolution :: fixed + + Type :: enhancement + + Priority :: minor + + Reporter :: jan +** DONE Operations of type jupiter-compound cannot be applied to buffers + (#31) + + Component :: rudel-general + + Resolution :: fixed + + Type :: defect + + Priority :: major + + Reporter :: jan +** DONE Do not sync any chunks when buffer is empty (#28) + + Component :: obby-backend + + Resolution :: fixed + + Type :: enhancement + + Priority :: minor + + Reporter :: jan +** DONE Implement Jupiter algorithm (#13) + + Component :: obby-general + + Resolution :: fixed + + Type :: enhancement + + Priority :: major + + Reporter :: jan +** DONE Replace email address (#26) + + Component :: rudel-general + + Resolution :: fixed + + Type :: task + + Priority :: major + + Reporter :: jan +** FIXED Mark contributions using overlays (#4) + + Component :: rudel-user-interface + + Resolution :: fixed + + Type :: enhancement + + Priority :: major + + Reporter :: jan +** FIXED When a user leaves and joins a second user object is created (#3) + + Component :: obby-general + + Resolution :: fixed + + Type :: defect + + Priority :: major + + Reporter :: jan + +Local variables: +mode: org +end: diff --git a/emacs.d/lisp/rudel/doc/.svn/all-wcprops b/emacs.d/lisp/rudel/doc/.svn/all-wcprops new file mode 100644 index 0000000..28e96e7 --- /dev/null +++ b/emacs.d/lisp/rudel/doc/.svn/all-wcprops @@ -0,0 +1,23 @@ +K 25 +svn:wc:ra_dav:version-url +V 37 +/svnroot/rudel/!svn/ver/399/trunk/doc +END +card.pdf +K 25 +svn:wc:ra_dav:version-url +V 46 +/svnroot/rudel/!svn/ver/396/trunk/doc/card.pdf +END +Project.ede +K 25 +svn:wc:ra_dav:version-url +V 49 +/svnroot/rudel/!svn/ver/297/trunk/doc/Project.ede +END +card.tex +K 25 +svn:wc:ra_dav:version-url +V 46 +/svnroot/rudel/!svn/ver/399/trunk/doc/card.tex +END diff --git a/emacs.d/lisp/rudel/doc/.svn/entries b/emacs.d/lisp/rudel/doc/.svn/entries new file mode 100644 index 0000000..5128808 --- /dev/null +++ b/emacs.d/lisp/rudel/doc/.svn/entries @@ -0,0 +1,130 @@ +10 + +dir +545 +https://rudel.svn.sourceforge.net/svnroot/rudel/trunk/doc +https://rudel.svn.sourceforge.net/svnroot/rudel + + + +2009-10-10T00:39:48.026786Z +399 +scymtym + + + + + + + + + + + + + + +694b31df-dcbb-44e8-af88-74c7ea918228 + +card.pdf +file + + + + +2009-11-18T14:01:45.000000Z +2df6dba381ea9728a9850132f09a68af +2009-10-10T00:38:34.122289Z +396 +scymtym + + + + + + + + + + + + + + + + + + + + + +68211 + +Project.ede +file + + + + +2009-11-18T14:01:45.000000Z +7b9a11f15f24e39437250456cd11bae7 +2009-10-03T01:25:19.588187Z +297 +scymtym + + + + + + + + + + + + + + + + + + + + + +408 + +card.tex +file + + + + +2009-11-18T14:01:45.000000Z +be52a552897987dec702a9a2eee7d347 +2009-10-10T00:39:48.026786Z +399 +scymtym + + + + + + + + + + + + + + + + + + + + + +9459 + diff --git a/emacs.d/lisp/rudel/doc/.svn/text-base/Project.ede.svn-base b/emacs.d/lisp/rudel/doc/.svn/text-base/Project.ede.svn-base new file mode 100644 index 0000000..0348d84 --- /dev/null +++ b/emacs.d/lisp/rudel/doc/.svn/text-base/Project.ede.svn-base @@ -0,0 +1,20 @@ +;; Object doc +;; EDE project file. +(ede-proj-project "rudel/doc" + :name "doc" + :file "Project.ede" + :targets (list + (ede-proj-target-makefile-miscelaneous "card.pdf" + :name "card.pdf" + :path "" + :source '("card.tex") + :rules (list + (ede-makefile-rule "pdftex" + :target "card.pdf" + :dependencies "card.tex" + :rules '("pdftex -t landscape $<") + ) + ) + ) + ) + ) diff --git a/emacs.d/lisp/rudel/doc/.svn/text-base/card.pdf.svn-base b/emacs.d/lisp/rudel/doc/.svn/text-base/card.pdf.svn-base new file mode 100644 index 0000000..60ddf36 Binary files /dev/null and b/emacs.d/lisp/rudel/doc/.svn/text-base/card.pdf.svn-base differ diff --git a/emacs.d/lisp/rudel/doc/.svn/text-base/card.tex.svn-base b/emacs.d/lisp/rudel/doc/.svn/text-base/card.tex.svn-base new file mode 100644 index 0000000..8be513d --- /dev/null +++ b/emacs.d/lisp/rudel/doc/.svn/text-base/card.tex.svn-base @@ -0,0 +1,329 @@ +%% card.tex --- Reference Card for Rudel 0.2 +% +% Copyright (c) 1987, 1992 Free Software Foundation, Inc. +% Copyright (c) 2009 Jan Moringen +% +% Author: Stephen Gildea (refcard.tex format) +% Dave Gillespie (Calc reference card) +% Jan Moringen (Rudel) +% +% This file is part of Rudel. +% +% Rudel 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. +% +% Rudel 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 rudel. If not, see . + + +%% Commentary: +% +% The format for this file is adapted from the GNU Emacs reference +% card version 1.9, by Stephen Gildea and the GNU Calc reference card +% by Dave Gillespie. +% +% This file is intended to be processed by plain TeX. +% Typical command to format: pdftex -t landscape card.tex +% Typical command to print: tex card.tex +% dvips -t landscape card.dvi + + +%% Constants +% + +\def\versionnumber{0.2} +\def\versiondate{September 2009} +\def\year{2009} + +\def\copyrightnotice{ +\vskip 1ex plus 2 fill\begingroup\small +\centerline{Copyright \copyright\ \year\ Jan Moringen} +\centerline{written by Jan Moringen} +\centerline{for Rudel version \versionnumber\ (\versiondate)} + +Permission is granted to make and distribute copies of +this card provided the copyright notice and this permission notice +are preserved on all copies. + +%For copies of the GNU Emacs Calc manual, write to the Free Software +%Foundation, Inc., 675 Massachusetts Ave, Cambridge MA 02139. + +\endgroup} + + + +%% Header +% + +% make \bye not \outer so that the \def\bye in the \else clause below +% can be scanned without complaint. +\def\bye{\par\vfill\supereject\end} + +\newdimen\intercolumnskip +\newbox\columna +\newbox\columnb + +\def\scaledmag#1{ scaled \magstep #1} + +% This format was designed by Stephen Gildea in October 1986. +\hsize 3.2in +\vsize 7.95in +\hoffset -.75in +\voffset -.745in +\font\titlefont=cmbx10 \scaledmag2 +\font\headingfont=cmbx10 \scaledmag1 +\font\subheadingfont=cmbx10 \scaledmag0 +\font\smallfont=cmr6 +\font\smallsy=cmsy6 +\font\eightrm=cmr8 +\font\eightbf=cmbx8 +\font\eightit=cmti8 +\font\eighttt=cmtt8 +\font\eightsy=cmsy8 +\textfont0=\eightrm +\textfont2=\eightsy +\font\notefont=cmti7 +\def\rm{\eightrm} +\def\bf{\eightbf} +\def\it{\eightit} +\def\tt{\eighttt} +\normalbaselineskip=.8\normalbaselineskip +\normallineskip=.8\normallineskip +\normallineskiplimit=.8\normallineskiplimit +\normalbaselines\rm %make definitions take effect + +\let\maxcolumn=c +\nopagenumbers + +\intercolumnskip=.46in +\def\abc{a} +\output={% + % This next line is useful when designing the layout. + %\immediate\write16{Column \folio\abc\space starts with \firstmark} + \if \maxcolumn\abc \multicolumnformat \global\def\abc{a} + \else\if a\abc + \global\setbox\columna\columnbox \global\def\abc{b} + %% in case we never use \columnb (two-column mode) + \global\setbox\columnb\hbox to -\intercolumnskip{} + \else + \global\setbox\columnb\columnbox \global\def\abc{c}\fi\fi} + +\def\multicolumnformat{\shipout\vbox{\makeheadline + \hbox{\box\columna\hskip\intercolumnskip + \box\columnb\hskip\intercolumnskip\columnbox} + \makefootline}\advancepageno} +\def\columnbox{\leftline{\pagebody}} + +\def\bye{\par\vfill\supereject + \if a\abc \else\null\vfill\eject\fi + \if a\abc \else\null\vfill\eject\fi + \end} + +% we won't be using math mode much, so redefine some of the characters +% we might want to talk about +\catcode`\^=12 +\catcode`\_=12 + +\chardef\\=`\\ +\chardef\{=`\{ +\chardef\}=`\} + +\parindent 0pt +\parskip 1ex plus .5ex minus .5ex + +\def\small{\smallfont\textfont2=\smallsy\baselineskip=.8\baselineskip} + +\outer\def\newcolumn{\vfill\eject} + +\outer\def\title#1{{\titlefont\centerline{#1}}\vskip 1ex plus .5ex} + +\outer\def\section#1{\par\filbreak + \vskip 3ex plus 2ex minus 2ex {\headingfont #1}\mark{#1}% + \vskip 2ex plus 1ex minus 1.5ex} + +\outer\def\subsection#1{\par\filbreak + \vskip 3ex plus 2ex minus 2ex {\subheadingfont #1}\mark{#1}% + \vskip 2ex plus 1ex minus 1.5ex} + +\newdimen\keyindent + +\def\beginindentedkeys{\keyindent=1em} +\def\endindentedkeys{\keyindent=0em} +\endindentedkeys + +\def\paralign{\vskip\parskip\halign} + +\def\<#1>{$\langle${\rm #1}$\rangle$} + +\def\kbd#1{{\tt#1}\null} %\null so not an abbrev even if period follows + +\def\key#1#2{\leavevmode\hbox to \hsize{\vtop + {\hsize=.75\hsize\rightskip=1em + \hskip\keyindent\relax#1}\kbd{#2}\hfil}} + +\newbox\metaxbox +\setbox\metaxbox\hbox{\kbd{M-x }} +\newdimen\metaxwidth +\metaxwidth=\wd\metaxbox + +% Redefine to make spaces a bit smaller +\let\wkbd=\kbd +\def\kbd#1{{\spaceskip=.37em\tt#1}\null} + +\def\wkey#1#2{\leavevmode\hbox to \hsize{\vtop + {\hsize=.75\hsize\rightskip=1em + \hskip\keyindent\relax#1}\wkbd{#2}\hfil}} + +\def\ccc{C-c\ c\ } +\def\,{{\rm ,\hskip.55em}\ignorespaces} +\def\lesssectionskip{\vskip-1.5ex} + +\def\iline#1{\par\line{\hskip1em\relax #1\hfill}\par} + +\font\eighti=cmmi8 +\textfont1=\eighti + + +%% Document +% + +\title{Rudel Reference Card}\vskip-1ex +\centerline{\tt http://rudel.sourceforge.net}\vskip1ex +\centerline{(for Rudel version \versionnumber\ of \versiondate)} + +\section{Key Bindings} + +\lesssectionskip +{\notefont All key bindings are only available when the global Rudel +minor mode is active. See ``Minor Modes'' below.} + +\subsection{Hosting and Joining} + +\wkey{Join a Rudel session}{\ccc j} +\wkey{Leave a Rudel session}{\ccc e} + +\wkey{Host a Rudel session}{\ccc h} + +\subsection{In a Session} + +The following commands are available once a Rudel session has been +joined: + +\key{Change color}{\ccc c} + +\key{Create and publish document for buffer}{\ccc p} +\key{Subscribe to a document}{\ccc s} + +The following commands are only available if the current buffer has an +associated document (achieved by publishing or subscribing): + +\key{Detach buffer from document (unsubscribe)}{\ccc u} + +\section{Minor Modes} + +\subsection{Global Rudel Minor Mode} + +The global Rudel minor mode global-rudel-minor-mode is responsible for +installing the Rudel menu and keymap. + +\subsection{Participants in Header Line} + +The mode ({\tt rudel-header-subscriptions-minor-mode}) shows +subscribed users in the header line of the buffer. + +The associated global mode ({\tt global-rudel-header-subscriptions +mode}) turns the mode on or off in all suitable buffers. + +\subsection{Buffer Status in Mode Line} + +The mode ({\tt rudel-mode-line-publish-state-minor-mode}) shows the +publication state of the buffer in its mode line ({\tt P}: published, +{\tt -}: not published) + +The associated global mode ({\tt +global-rudel-mode-line-publish-state-mode}) turns the mode on or off +in all suitable buffers. + +\eject + +\section{Backends} + +\lesssectionskip +In Rudel, various kinds of backends are used to provide multiple +pluggable implementations of functionalities like communication +protocols, network transport, session initiation and parts of the user +interface. + +\subsection{Protocol} + +{\bf Obby} + +This backend implements the obby protocol used by the Gobby +collaborative editor until (including) version 4.9. + +\subsection{Session Initiation} + +{\bf Configured Sessions} + +Frequently used sessions can be configured permanently using the {\tt +rudel-configured-sessions} customization variable. When there are +configured sessions, connecting to one of these session is offered +when joining a session using \kbd{\ccc j}. + +{\bf Zeroconf} + +The Zeroconf backends discovers collaboration sessions which are +advertised using, well Zeroconf. Currently, it works for Rudel (obby +backend) and Obby servers. When there are Zeroconf-advertised +sessions, using \kbd{\ccc j} to join a session will display these +sessions. + +\section{Examples} + +\subsection{Joining a Session with the obby backend} + +{\bf Without Zeroconf Discovery} + +\kbd{\ccc j}\hfil\break +{\tt Server: }{\it SERVER} \kbd{RET}\hfil\break +{\tt Port (default 6522): } \kbd{RET}\hfil\break +{\tt Username: }{\it NAME} \kbd{RET}\hfil\break +{\tt Color: }{\it COLOR} \kbd{RET}\hfil\break +{\notefont (use bright colors like yellow)}\hfil\break +{\it When connecting to a Rudel server:}\hfil\break +-\hskip2ex {\tt Use Encryption (yes or no): }{\it NO} \kbd{RET}\hfil\break +{\it When connecting to a Gobby server:}\hfil\break +-\hskip2ex {\tt Use Encryption (yes or no): }{\it YES} \kbd{RET}\hfil\break +{\tt Global Password: }{\it PASSWORD} \kbd{RET}\hfil\break +{\it This password controls access to the session; just \kbd{RET} for +none}\hfil\break +{\tt User Password: }{\it PASSWORD} \kbd{RET}\hfil\break +{\it This password prevents name stealing; just \kbd{RET} for +none}\hfil\break + +{\bf With Zeroconf Discovery} + +\kbd{\ccc j}\hfil\break +{\tt Choose Session: }Zeroconf advertised session {\it +"NAME"}\kbd{RET}\hfil\break +{\notefont (Note: by choosing "ask-protocol" here, you can still get the +above behavior)}\hfil\break +{\tt Username: }{\it NAME} \kbd{RET}\hfil\break +{\tt Color: }{\it COLOR} \kbd{RET}\hfil\break +{\tt Global Password: }{\it PASSWORD} \kbd{RET}\hfil\break +{\tt User Password: }{\it PASSWORD} \kbd{RET}\hfil + +\copyrightnotice + +\bye + +% Local variables: +% mode: plain-TeX; eval: (tex-pdf-mode); +% End: diff --git a/emacs.d/lisp/rudel/doc/Project.ede b/emacs.d/lisp/rudel/doc/Project.ede new file mode 100644 index 0000000..0348d84 --- /dev/null +++ b/emacs.d/lisp/rudel/doc/Project.ede @@ -0,0 +1,20 @@ +;; Object doc +;; EDE project file. +(ede-proj-project "rudel/doc" + :name "doc" + :file "Project.ede" + :targets (list + (ede-proj-target-makefile-miscelaneous "card.pdf" + :name "card.pdf" + :path "" + :source '("card.tex") + :rules (list + (ede-makefile-rule "pdftex" + :target "card.pdf" + :dependencies "card.tex" + :rules '("pdftex -t landscape $<") + ) + ) + ) + ) + ) diff --git a/emacs.d/lisp/rudel/doc/card.pdf b/emacs.d/lisp/rudel/doc/card.pdf new file mode 100644 index 0000000..60ddf36 Binary files /dev/null and b/emacs.d/lisp/rudel/doc/card.pdf differ diff --git a/emacs.d/lisp/rudel/doc/card.tex b/emacs.d/lisp/rudel/doc/card.tex new file mode 100644 index 0000000..8be513d --- /dev/null +++ b/emacs.d/lisp/rudel/doc/card.tex @@ -0,0 +1,329 @@ +%% card.tex --- Reference Card for Rudel 0.2 +% +% Copyright (c) 1987, 1992 Free Software Foundation, Inc. +% Copyright (c) 2009 Jan Moringen +% +% Author: Stephen Gildea (refcard.tex format) +% Dave Gillespie (Calc reference card) +% Jan Moringen (Rudel) +% +% This file is part of Rudel. +% +% Rudel 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. +% +% Rudel 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 rudel. If not, see . + + +%% Commentary: +% +% The format for this file is adapted from the GNU Emacs reference +% card version 1.9, by Stephen Gildea and the GNU Calc reference card +% by Dave Gillespie. +% +% This file is intended to be processed by plain TeX. +% Typical command to format: pdftex -t landscape card.tex +% Typical command to print: tex card.tex +% dvips -t landscape card.dvi + + +%% Constants +% + +\def\versionnumber{0.2} +\def\versiondate{September 2009} +\def\year{2009} + +\def\copyrightnotice{ +\vskip 1ex plus 2 fill\begingroup\small +\centerline{Copyright \copyright\ \year\ Jan Moringen} +\centerline{written by Jan Moringen} +\centerline{for Rudel version \versionnumber\ (\versiondate)} + +Permission is granted to make and distribute copies of +this card provided the copyright notice and this permission notice +are preserved on all copies. + +%For copies of the GNU Emacs Calc manual, write to the Free Software +%Foundation, Inc., 675 Massachusetts Ave, Cambridge MA 02139. + +\endgroup} + + + +%% Header +% + +% make \bye not \outer so that the \def\bye in the \else clause below +% can be scanned without complaint. +\def\bye{\par\vfill\supereject\end} + +\newdimen\intercolumnskip +\newbox\columna +\newbox\columnb + +\def\scaledmag#1{ scaled \magstep #1} + +% This format was designed by Stephen Gildea in October 1986. +\hsize 3.2in +\vsize 7.95in +\hoffset -.75in +\voffset -.745in +\font\titlefont=cmbx10 \scaledmag2 +\font\headingfont=cmbx10 \scaledmag1 +\font\subheadingfont=cmbx10 \scaledmag0 +\font\smallfont=cmr6 +\font\smallsy=cmsy6 +\font\eightrm=cmr8 +\font\eightbf=cmbx8 +\font\eightit=cmti8 +\font\eighttt=cmtt8 +\font\eightsy=cmsy8 +\textfont0=\eightrm +\textfont2=\eightsy +\font\notefont=cmti7 +\def\rm{\eightrm} +\def\bf{\eightbf} +\def\it{\eightit} +\def\tt{\eighttt} +\normalbaselineskip=.8\normalbaselineskip +\normallineskip=.8\normallineskip +\normallineskiplimit=.8\normallineskiplimit +\normalbaselines\rm %make definitions take effect + +\let\maxcolumn=c +\nopagenumbers + +\intercolumnskip=.46in +\def\abc{a} +\output={% + % This next line is useful when designing the layout. + %\immediate\write16{Column \folio\abc\space starts with \firstmark} + \if \maxcolumn\abc \multicolumnformat \global\def\abc{a} + \else\if a\abc + \global\setbox\columna\columnbox \global\def\abc{b} + %% in case we never use \columnb (two-column mode) + \global\setbox\columnb\hbox to -\intercolumnskip{} + \else + \global\setbox\columnb\columnbox \global\def\abc{c}\fi\fi} + +\def\multicolumnformat{\shipout\vbox{\makeheadline + \hbox{\box\columna\hskip\intercolumnskip + \box\columnb\hskip\intercolumnskip\columnbox} + \makefootline}\advancepageno} +\def\columnbox{\leftline{\pagebody}} + +\def\bye{\par\vfill\supereject + \if a\abc \else\null\vfill\eject\fi + \if a\abc \else\null\vfill\eject\fi + \end} + +% we won't be using math mode much, so redefine some of the characters +% we might want to talk about +\catcode`\^=12 +\catcode`\_=12 + +\chardef\\=`\\ +\chardef\{=`\{ +\chardef\}=`\} + +\parindent 0pt +\parskip 1ex plus .5ex minus .5ex + +\def\small{\smallfont\textfont2=\smallsy\baselineskip=.8\baselineskip} + +\outer\def\newcolumn{\vfill\eject} + +\outer\def\title#1{{\titlefont\centerline{#1}}\vskip 1ex plus .5ex} + +\outer\def\section#1{\par\filbreak + \vskip 3ex plus 2ex minus 2ex {\headingfont #1}\mark{#1}% + \vskip 2ex plus 1ex minus 1.5ex} + +\outer\def\subsection#1{\par\filbreak + \vskip 3ex plus 2ex minus 2ex {\subheadingfont #1}\mark{#1}% + \vskip 2ex plus 1ex minus 1.5ex} + +\newdimen\keyindent + +\def\beginindentedkeys{\keyindent=1em} +\def\endindentedkeys{\keyindent=0em} +\endindentedkeys + +\def\paralign{\vskip\parskip\halign} + +\def\<#1>{$\langle${\rm #1}$\rangle$} + +\def\kbd#1{{\tt#1}\null} %\null so not an abbrev even if period follows + +\def\key#1#2{\leavevmode\hbox to \hsize{\vtop + {\hsize=.75\hsize\rightskip=1em + \hskip\keyindent\relax#1}\kbd{#2}\hfil}} + +\newbox\metaxbox +\setbox\metaxbox\hbox{\kbd{M-x }} +\newdimen\metaxwidth +\metaxwidth=\wd\metaxbox + +% Redefine to make spaces a bit smaller +\let\wkbd=\kbd +\def\kbd#1{{\spaceskip=.37em\tt#1}\null} + +\def\wkey#1#2{\leavevmode\hbox to \hsize{\vtop + {\hsize=.75\hsize\rightskip=1em + \hskip\keyindent\relax#1}\wkbd{#2}\hfil}} + +\def\ccc{C-c\ c\ } +\def\,{{\rm ,\hskip.55em}\ignorespaces} +\def\lesssectionskip{\vskip-1.5ex} + +\def\iline#1{\par\line{\hskip1em\relax #1\hfill}\par} + +\font\eighti=cmmi8 +\textfont1=\eighti + + +%% Document +% + +\title{Rudel Reference Card}\vskip-1ex +\centerline{\tt http://rudel.sourceforge.net}\vskip1ex +\centerline{(for Rudel version \versionnumber\ of \versiondate)} + +\section{Key Bindings} + +\lesssectionskip +{\notefont All key bindings are only available when the global Rudel +minor mode is active. See ``Minor Modes'' below.} + +\subsection{Hosting and Joining} + +\wkey{Join a Rudel session}{\ccc j} +\wkey{Leave a Rudel session}{\ccc e} + +\wkey{Host a Rudel session}{\ccc h} + +\subsection{In a Session} + +The following commands are available once a Rudel session has been +joined: + +\key{Change color}{\ccc c} + +\key{Create and publish document for buffer}{\ccc p} +\key{Subscribe to a document}{\ccc s} + +The following commands are only available if the current buffer has an +associated document (achieved by publishing or subscribing): + +\key{Detach buffer from document (unsubscribe)}{\ccc u} + +\section{Minor Modes} + +\subsection{Global Rudel Minor Mode} + +The global Rudel minor mode global-rudel-minor-mode is responsible for +installing the Rudel menu and keymap. + +\subsection{Participants in Header Line} + +The mode ({\tt rudel-header-subscriptions-minor-mode}) shows +subscribed users in the header line of the buffer. + +The associated global mode ({\tt global-rudel-header-subscriptions +mode}) turns the mode on or off in all suitable buffers. + +\subsection{Buffer Status in Mode Line} + +The mode ({\tt rudel-mode-line-publish-state-minor-mode}) shows the +publication state of the buffer in its mode line ({\tt P}: published, +{\tt -}: not published) + +The associated global mode ({\tt +global-rudel-mode-line-publish-state-mode}) turns the mode on or off +in all suitable buffers. + +\eject + +\section{Backends} + +\lesssectionskip +In Rudel, various kinds of backends are used to provide multiple +pluggable implementations of functionalities like communication +protocols, network transport, session initiation and parts of the user +interface. + +\subsection{Protocol} + +{\bf Obby} + +This backend implements the obby protocol used by the Gobby +collaborative editor until (including) version 4.9. + +\subsection{Session Initiation} + +{\bf Configured Sessions} + +Frequently used sessions can be configured permanently using the {\tt +rudel-configured-sessions} customization variable. When there are +configured sessions, connecting to one of these session is offered +when joining a session using \kbd{\ccc j}. + +{\bf Zeroconf} + +The Zeroconf backends discovers collaboration sessions which are +advertised using, well Zeroconf. Currently, it works for Rudel (obby +backend) and Obby servers. When there are Zeroconf-advertised +sessions, using \kbd{\ccc j} to join a session will display these +sessions. + +\section{Examples} + +\subsection{Joining a Session with the obby backend} + +{\bf Without Zeroconf Discovery} + +\kbd{\ccc j}\hfil\break +{\tt Server: }{\it SERVER} \kbd{RET}\hfil\break +{\tt Port (default 6522): } \kbd{RET}\hfil\break +{\tt Username: }{\it NAME} \kbd{RET}\hfil\break +{\tt Color: }{\it COLOR} \kbd{RET}\hfil\break +{\notefont (use bright colors like yellow)}\hfil\break +{\it When connecting to a Rudel server:}\hfil\break +-\hskip2ex {\tt Use Encryption (yes or no): }{\it NO} \kbd{RET}\hfil\break +{\it When connecting to a Gobby server:}\hfil\break +-\hskip2ex {\tt Use Encryption (yes or no): }{\it YES} \kbd{RET}\hfil\break +{\tt Global Password: }{\it PASSWORD} \kbd{RET}\hfil\break +{\it This password controls access to the session; just \kbd{RET} for +none}\hfil\break +{\tt User Password: }{\it PASSWORD} \kbd{RET}\hfil\break +{\it This password prevents name stealing; just \kbd{RET} for +none}\hfil\break + +{\bf With Zeroconf Discovery} + +\kbd{\ccc j}\hfil\break +{\tt Choose Session: }Zeroconf advertised session {\it +"NAME"}\kbd{RET}\hfil\break +{\notefont (Note: by choosing "ask-protocol" here, you can still get the +above behavior)}\hfil\break +{\tt Username: }{\it NAME} \kbd{RET}\hfil\break +{\tt Color: }{\it COLOR} \kbd{RET}\hfil\break +{\tt Global Password: }{\it PASSWORD} \kbd{RET}\hfil\break +{\tt User Password: }{\it PASSWORD} \kbd{RET}\hfil + +\copyrightnotice + +\bye + +% Local variables: +% mode: plain-TeX; eval: (tex-pdf-mode); +% End: diff --git a/emacs.d/lisp/rudel/icons/.svn/all-wcprops b/emacs.d/lisp/rudel/icons/.svn/all-wcprops new file mode 100644 index 0000000..74d10a4 --- /dev/null +++ b/emacs.d/lisp/rudel/icons/.svn/all-wcprops @@ -0,0 +1,41 @@ +K 25 +svn:wc:ra_dav:version-url +V 39 +/svnroot/rudel/!svn/ver/271/trunk/icons +END +connected.svg +K 25 +svn:wc:ra_dav:version-url +V 53 +/svnroot/rudel/!svn/ver/271/trunk/icons/connected.svg +END +disconnected.svg +K 25 +svn:wc:ra_dav:version-url +V 56 +/svnroot/rudel/!svn/ver/271/trunk/icons/disconnected.svg +END +person.svg +K 25 +svn:wc:ra_dav:version-url +V 50 +/svnroot/rudel/!svn/ver/271/trunk/icons/person.svg +END +plaintext.svg +K 25 +svn:wc:ra_dav:version-url +V 53 +/svnroot/rudel/!svn/ver/271/trunk/icons/plaintext.svg +END +encrypted.svg +K 25 +svn:wc:ra_dav:version-url +V 53 +/svnroot/rudel/!svn/ver/271/trunk/icons/encrypted.svg +END +document.svg +K 25 +svn:wc:ra_dav:version-url +V 52 +/svnroot/rudel/!svn/ver/271/trunk/icons/document.svg +END diff --git a/emacs.d/lisp/rudel/icons/.svn/entries b/emacs.d/lisp/rudel/icons/.svn/entries new file mode 100644 index 0000000..066c288 --- /dev/null +++ b/emacs.d/lisp/rudel/icons/.svn/entries @@ -0,0 +1,232 @@ +10 + +dir +545 +https://rudel.svn.sourceforge.net/svnroot/rudel/trunk/icons +https://rudel.svn.sourceforge.net/svnroot/rudel + + + +2009-10-03T01:13:36.139341Z +271 +scymtym + + + + + + + + + + + + + + +694b31df-dcbb-44e8-af88-74c7ea918228 + +connected.svg +file + + + + +2009-11-18T14:01:46.000000Z +92dabaaee1e0a5aa817a2375f3123ff9 +2009-10-03T01:13:36.139341Z +271 +scymtym + + + + + + + + + + + + + + + + + + + + + +120859 + +disconnected.svg +file + + + + +2009-11-18T14:01:46.000000Z +36648b21e2242f6eab978ea2a92a21c7 +2009-10-03T01:13:36.139341Z +271 +scymtym + + + + + + + + + + + + + + + + + + + + + +125346 + +person.svg +file + + + + +2009-11-18T14:01:46.000000Z +677399e71aa07204caf1006847c274bf +2009-10-03T01:13:36.139341Z +271 +scymtym + + + + + + + + + + + + + + + + + + + + + +17086 + +plaintext.svg +file + + + + +2009-11-18T14:01:46.000000Z +60734063eebaaa9e5278698aaf162270 +2009-10-03T01:13:36.139341Z +271 +scymtym + + + + + + + + + + + + + + + + + + + + + +20965 + +encrypted.svg +file + + + + +2009-11-18T14:01:46.000000Z +4f02b49c101291ffcce34950f135b3fe +2009-10-03T01:13:36.139341Z +271 +scymtym + + + + + + + + + + + + + + + + + + + + + +33798 + +document.svg +file + + + + +2009-11-18T14:01:46.000000Z +d1cba46d3ce1aa5ac2fc9fd04139b131 +2009-10-03T01:13:36.139341Z +271 +scymtym + + + + + + + + + + + + + + + + + + + + + +26034 + diff --git a/emacs.d/lisp/rudel/icons/.svn/text-base/connected.svg.svn-base b/emacs.d/lisp/rudel/icons/.svn/text-base/connected.svg.svn-base new file mode 100644 index 0000000..3777037 --- /dev/null +++ b/emacs.d/lisp/rudel/icons/.svn/text-base/connected.svg.svn-base @@ -0,0 +1,2479 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Network + 2005-03-08 + + + Lapo Calamandrei + + + + + + + + + + Jakub Steiner, Luca Ferretti + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/emacs.d/lisp/rudel/icons/.svn/text-base/disconnected.svg.svn-base b/emacs.d/lisp/rudel/icons/.svn/text-base/disconnected.svg.svn-base new file mode 100644 index 0000000..5a8de65 --- /dev/null +++ b/emacs.d/lisp/rudel/icons/.svn/text-base/disconnected.svg.svn-base @@ -0,0 +1,2600 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Network Offline + 2005-03-08 + + + Lapo Calamandrei + + + + + + + + + + Jakub Steiner, Luca Ferretti + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/emacs.d/lisp/rudel/icons/.svn/text-base/document.svg.svn-base b/emacs.d/lisp/rudel/icons/.svn/text-base/document.svg.svn-base new file mode 100644 index 0000000..aa5163c --- /dev/null +++ b/emacs.d/lisp/rudel/icons/.svn/text-base/document.svg.svn-base @@ -0,0 +1,608 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Generic Text + + + text + plaintext + regular + document + + + + + + Lapo Calamandrei + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/emacs.d/lisp/rudel/icons/.svn/text-base/encrypted.svg.svn-base b/emacs.d/lisp/rudel/icons/.svn/text-base/encrypted.svg.svn-base new file mode 100644 index 0000000..8c32cc6 --- /dev/null +++ b/emacs.d/lisp/rudel/icons/.svn/text-base/encrypted.svg.svn-base @@ -0,0 +1,902 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Edit Paste + 2005-10-10 + + + Andreas Nilsson + + + + + edit + paste + + + + + + Jakub Steiner + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/emacs.d/lisp/rudel/icons/.svn/text-base/person.svg.svn-base b/emacs.d/lisp/rudel/icons/.svn/text-base/person.svg.svn-base new file mode 100644 index 0000000..9abbb02 --- /dev/null +++ b/emacs.d/lisp/rudel/icons/.svn/text-base/person.svg.svn-base @@ -0,0 +1,388 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Person + + + Jakub Steiner + + + http://jimmac.musichall.cz + + + user + person + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/emacs.d/lisp/rudel/icons/.svn/text-base/plaintext.svg.svn-base b/emacs.d/lisp/rudel/icons/.svn/text-base/plaintext.svg.svn-base new file mode 100644 index 0000000..1086ba2 --- /dev/null +++ b/emacs.d/lisp/rudel/icons/.svn/text-base/plaintext.svg.svn-base @@ -0,0 +1,474 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Edit Paste + 2005-10-10 + + + Andreas Nilsson + + + + + edit + paste + + + + + + Jakub Steiner + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/emacs.d/lisp/rudel/icons/connected.svg b/emacs.d/lisp/rudel/icons/connected.svg new file mode 100644 index 0000000..3777037 --- /dev/null +++ b/emacs.d/lisp/rudel/icons/connected.svg @@ -0,0 +1,2479 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Network + 2005-03-08 + + + Lapo Calamandrei + + + + + + + + + + Jakub Steiner, Luca Ferretti + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/emacs.d/lisp/rudel/icons/disconnected.svg b/emacs.d/lisp/rudel/icons/disconnected.svg new file mode 100644 index 0000000..5a8de65 --- /dev/null +++ b/emacs.d/lisp/rudel/icons/disconnected.svg @@ -0,0 +1,2600 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Network Offline + 2005-03-08 + + + Lapo Calamandrei + + + + + + + + + + Jakub Steiner, Luca Ferretti + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/emacs.d/lisp/rudel/icons/document.svg b/emacs.d/lisp/rudel/icons/document.svg new file mode 100644 index 0000000..aa5163c --- /dev/null +++ b/emacs.d/lisp/rudel/icons/document.svg @@ -0,0 +1,608 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Generic Text + + + text + plaintext + regular + document + + + + + + Lapo Calamandrei + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/emacs.d/lisp/rudel/icons/encrypted.svg b/emacs.d/lisp/rudel/icons/encrypted.svg new file mode 100644 index 0000000..8c32cc6 --- /dev/null +++ b/emacs.d/lisp/rudel/icons/encrypted.svg @@ -0,0 +1,902 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Edit Paste + 2005-10-10 + + + Andreas Nilsson + + + + + edit + paste + + + + + + Jakub Steiner + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/emacs.d/lisp/rudel/icons/person.svg b/emacs.d/lisp/rudel/icons/person.svg new file mode 100644 index 0000000..9abbb02 --- /dev/null +++ b/emacs.d/lisp/rudel/icons/person.svg @@ -0,0 +1,388 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Person + + + Jakub Steiner + + + http://jimmac.musichall.cz + + + user + person + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/emacs.d/lisp/rudel/icons/plaintext.svg b/emacs.d/lisp/rudel/icons/plaintext.svg new file mode 100644 index 0000000..1086ba2 --- /dev/null +++ b/emacs.d/lisp/rudel/icons/plaintext.svg @@ -0,0 +1,474 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Edit Paste + 2005-10-10 + + + Andreas Nilsson + + + + + edit + paste + + + + + + Jakub Steiner + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/emacs.d/lisp/rudel/jupiter/.svn/all-wcprops b/emacs.d/lisp/rudel/jupiter/.svn/all-wcprops new file mode 100644 index 0000000..87ab5a7 --- /dev/null +++ b/emacs.d/lisp/rudel/jupiter/.svn/all-wcprops @@ -0,0 +1,47 @@ +K 25 +svn:wc:ra_dav:version-url +V 41 +/svnroot/rudel/!svn/ver/402/trunk/jupiter +END +jupiter-operation.el +K 25 +svn:wc:ra_dav:version-url +V 62 +/svnroot/rudel/!svn/ver/181/trunk/jupiter/jupiter-operation.el +END +jupiter-delete.el +K 25 +svn:wc:ra_dav:version-url +V 59 +/svnroot/rudel/!svn/ver/402/trunk/jupiter/jupiter-delete.el +END +jupiter.el +K 25 +svn:wc:ra_dav:version-url +V 52 +/svnroot/rudel/!svn/ver/401/trunk/jupiter/jupiter.el +END +Project.ede +K 25 +svn:wc:ra_dav:version-url +V 53 +/svnroot/rudel/!svn/ver/127/trunk/jupiter/Project.ede +END +jupiter-compound.el +K 25 +svn:wc:ra_dav:version-url +V 61 +/svnroot/rudel/!svn/ver/401/trunk/jupiter/jupiter-compound.el +END +jupiter-insert.el +K 25 +svn:wc:ra_dav:version-url +V 59 +/svnroot/rudel/!svn/ver/401/trunk/jupiter/jupiter-insert.el +END +jupiter-nop.el +K 25 +svn:wc:ra_dav:version-url +V 56 +/svnroot/rudel/!svn/ver/401/trunk/jupiter/jupiter-nop.el +END diff --git a/emacs.d/lisp/rudel/jupiter/.svn/entries b/emacs.d/lisp/rudel/jupiter/.svn/entries new file mode 100644 index 0000000..9c980a3 --- /dev/null +++ b/emacs.d/lisp/rudel/jupiter/.svn/entries @@ -0,0 +1,266 @@ +10 + +dir +545 +https://rudel.svn.sourceforge.net/svnroot/rudel/trunk/jupiter +https://rudel.svn.sourceforge.net/svnroot/rudel + + + +2009-10-12T01:25:58.278168Z +402 +scymtym + + + + + + + + + + + + + + +694b31df-dcbb-44e8-af88-74c7ea918228 + +jupiter-operation.el +file + + + + +2009-11-18T14:01:45.000000Z +550a78114bf28f2e455b046b044de3ab +2009-10-03T00:38:22.402651Z +181 +scymtym + + + + + + + + + + + + + + + + + + + + + +1768 + +jupiter-delete.el +file + + + + +2009-11-18T14:01:45.000000Z +6d404ed5777e97ebc85ecceccd7c192f +2009-10-12T01:25:58.278168Z +402 +scymtym + + + + + + + + + + + + + + + + + + + + + +4567 + +jupiter.el +file + + + + +2009-11-18T14:01:45.000000Z +f8061c6bda7d06bb8baec72c4ad99455 +2009-10-10T00:40:22.102805Z +401 +scymtym + + + + + + + + + + + + + + + + + + + + + +4289 + +Project.ede +file + + + + +2009-11-18T14:01:45.000000Z +9c70097606c8b04470919770f3ee7f27 +2009-10-03T00:27:43.856107Z +127 +scymtym + + + + + + + + + + + + + + + + + + + + + +380 + +jupiter-compound.el +file + + + + +2009-11-18T14:01:45.000000Z +924928b8dcad42bebf1892a1f8815d84 +2009-10-10T00:40:22.102805Z +401 +scymtym + + + + + + + + + + + + + + + + + + + + + +2569 + +jupiter-insert.el +file + + + + +2009-11-18T14:01:45.000000Z +418f814ecd47f022790826fd91547e19 +2009-10-10T00:40:22.102805Z +401 +scymtym + + + + + + + + + + + + + + + + + + + + + +4126 + +jupiter-nop.el +file + + + + +2009-11-18T14:01:45.000000Z +3af498a68a91644e00dfb63a1a2ca4a7 +2009-10-10T00:40:22.102805Z +401 +scymtym + + + + + + + + + + + + + + + + + + + + + +1457 + diff --git a/emacs.d/lisp/rudel/jupiter/.svn/text-base/Project.ede.svn-base b/emacs.d/lisp/rudel/jupiter/.svn/text-base/Project.ede.svn-base new file mode 100644 index 0000000..275d1ed --- /dev/null +++ b/emacs.d/lisp/rudel/jupiter/.svn/text-base/Project.ede.svn-base @@ -0,0 +1,14 @@ +;; Object rudel/jupiter +;; EDE project file. +(ede-proj-project "rudel/jupiter" + :name "jupiter" + :file "Project.ede" + :targets (list + (ede-proj-target-elisp "jupiter" + :name "jupiter" + :path "" + :source '("jupiter.el" "jupiter-operation.el" "jupiter-insert.el" "jupiter-delete.el" "jupiter-compound.el" "jupiter-nop.el") + :aux-packages '("rudel") + ) + ) + ) diff --git a/emacs.d/lisp/rudel/jupiter/.svn/text-base/jupiter-compound.el.svn-base b/emacs.d/lisp/rudel/jupiter/.svn/text-base/jupiter-compound.el.svn-base new file mode 100644 index 0000000..789bbc2 --- /dev/null +++ b/emacs.d/lisp/rudel/jupiter/.svn/text-base/jupiter-compound.el.svn-base @@ -0,0 +1,89 @@ +;;; jupiter-compound.el --- Jupiter compound operation +;; +;; Copyright (C) 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: jupiter, operation, compound +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; Class `jupiter-compound' implements a compound operation comprised +;; of a number of child operations. + + +;;; History: +;; +;; 0.1 - Initial revision + + +;;; Code: +;; + +(require 'eieio) + +(require 'jupiter-operation) + + +;;; Class jupiter-compound +;; + +(defclass jupiter-compound (jupiter-operation) + ((children :initarg :children + :type list + :initform nil + :documentation + "")) + "Objects of this class are operations, which are composed of a +number of child operation.") + +;; TODO this has side effects. It can only be called once +(defmethod rudel-apply ((this jupiter-compound) object) + "Apply THIS to BUFFER by applying the child operation." + (with-slots (children) this + (let ((child (first children)) + (rest (rest children))) + ;; Apply all child operations + (while child + (rudel-apply child object) + ;; For each applied child operation, transform remaining + ;; operation with the applied operation. + (dolist (next rest) + (setf next (jupiter-transform child next))) + ;; Advance to next child operation. + (setq child (first rest) + rest (rest rest))))) + ) + +(defmethod jupiter-transform ((this jupiter-compound) other) + "Transform OTHER using the child operations of THIS." + (with-slots (children) this + (dolist (child children) ;; TODO reverse children? + (setq other (jupiter-transform child other))) + other)) + +(defmethod object-print ((this jupiter-compound) &rest strings) + "Add number of children to string representation of THIS." + (with-slots (children) this + (call-next-method + this + (format " children %d" (length children))))) + +(provide 'jupiter-compound) +;;; jupiter-compound.el ends here diff --git a/emacs.d/lisp/rudel/jupiter/.svn/text-base/jupiter-delete.el.svn-base b/emacs.d/lisp/rudel/jupiter/.svn/text-base/jupiter-delete.el.svn-base new file mode 100644 index 0000000..db2e985 --- /dev/null +++ b/emacs.d/lisp/rudel/jupiter/.svn/text-base/jupiter-delete.el.svn-base @@ -0,0 +1,176 @@ +;;; jupiter-delete.el --- Jupiter delete operation +;; +;; Copyright (C) 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: jupiter, operation, delete +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; Class `jupiter-delete' implements a delete operation for the +;; Jupiter algorithm. + + +;;; History: +;; +;; 0.1 - Initial revision + + +;;; Code: +;; + +(require 'eieio) + +(require 'rudel-operations) +(require 'jupiter-operation) + + +;;; Class jupiter-delete +;; + +(defclass jupiter-delete (jupiter-operation + rudel-delete-op) + () + "Objects of this class represent deletions in buffers.") + +(defmethod jupiter-transform ((this jupiter-delete) other) + "Transform other using THIS. +OTHER is destructively modified or replaced." + (cond + + ;; + ;; Transform an insert operation + ;; + ((jupiter-insert-p other) + (with-slots ((this-from :from) + (this-to :to) + (this-length :length)) this + (with-slots ((other-from :from) + (other-to :to) + (other-length :length)) other + (cond + ;; + ;; + ;; + ;; + ((<= other-to this-from)) + + ;; + ;; + ((> other-from this-to) + (decf other-from this-length)) + + ;; + ;; < this > + ((and (> other-from this-from) (< other-to this-to)) + (setq other-from this-from)) + ))) + ) + + ;; + ;; Transform a delete operation + ;; + ((jupiter-delete-p other) + (with-slots ((this-from :from) + (this-to :to) + (this-length :length)) this + (with-slots ((other-from :from) + (other-to :to) + (other-length :length)) other + (cond + + ;; + ;; + ;; OTHER deleted a region after the region deleted by + ;; THIS. Therefore OTHER has to be shifted by the length of + ;; the deleted region. + ((> other-from this-to) + (decf other-from this-length) + (decf other-to this-length)) + + ;; + ;; + ;; OTHER deleted a region before the region affected by + ;; THIS. That is not affected by THIS operation. + ((<= other-to this-from)) + + ;; < other > + ;; + ((and (>= other-from this-from) (>= other-to this-to)) + (decf other-to this-length)) + + ;; + ;; + ((and (< other-from this-from) (< other-to this-to)) + (decf other-to (- other-to this-to))) + + ;; + ;; + ;; The region deleted by OTHER overlaps with the region + ;; deleted by THIS, such that a part of the region of this is + ;; before the region of OTHER. The first part of the region + ;; deleted by OTHER has already been deleted. Therefore, the + ;; start of OTHER has to be shifted by the length of the + ;; overlap. + ((and (< other-from this-to) (> other-to this-to)) + (setq other-from this-from) + (incf other-to (+ other-from (- other-to this-to)))) + + ;; + ;; < this > + ;; The region deleted by OTHER is completely contained in + ;; the region affected by THIS. Therefore, OTHER must not + ;; be executed. + ((and (>= other-from this-from) (<= other-to this-to)) + (setq other (jupiter-nop "nop"))) + + (t (error "logic error in (jupiter-transform (x jupiter-delete) (y jupiter-delete))")) + )))) + + ;; + ;; Transform a compound operation + ;; + ((jupiter-compound-p other) ;; TODO encapsulation violation + (with-slots (children) other + (dolist (child children) + (setf child (jupiter-transform this child))))) + + ;; + ;; Transform a nop operation + ;; + ((jupiter-nop-p other)) + + ;; TODO this is for debugging + (t (error "Cannot transform operation of type `%s'" + (object-class other)))) + other) + +(defmethod object-print ((this jupiter-delete) &rest strings) + "Add from, to and length to string representation of THIS." + (with-slots (from to length) this + (call-next-method + this + (format " from %d" from) + (format " to %d" to) + (format " length %d" length))) + ) + +(provide 'jupiter-delete) +;;; jupiter-delete.el ends here diff --git a/emacs.d/lisp/rudel/jupiter/.svn/text-base/jupiter-insert.el.svn-base b/emacs.d/lisp/rudel/jupiter/.svn/text-base/jupiter-insert.el.svn-base new file mode 100644 index 0000000..339270e --- /dev/null +++ b/emacs.d/lisp/rudel/jupiter/.svn/text-base/jupiter-insert.el.svn-base @@ -0,0 +1,165 @@ +;;; jupiter-insert.el --- Jupiter insert operation +;; +;; Copyright (C) 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: jupiter, operation, insert +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; Class `jupiter-insert' implements an insert operation for the +;; Jupiter algorithm. + + +;;; History: +;; +;; 0.1 - Initial revision + + +;;; Code: +;; + +(require 'eieio) + +(require 'rudel-operations) +(require 'jupiter-operation) + + +;;; Class jupiter-insert +;; + +(defclass jupiter-insert (jupiter-operation + rudel-insert-op) + () + "Objects of this class represent insertions into buffers.") + +(defmethod jupiter-transform ((this jupiter-insert) other) + "Transform OTHER using THIS." + (cond + + ;; + ;; Transform an insert operation + ;; + ((jupiter-insert-p other) + (with-slots ((this-from :from) + (this-to :to) + (this-length :length) + (this-data :data)) this + (with-slots ((other-from :from) + (other-to :to) + (other-length :length) + (other-data :data)) other + (cond + ;; + ;; + ;; + ;; No need to do anything in this case. + ;; ((< other-from this-from)) + + ;; + ;; + ;; + ;; + ((> other-from this-from) + (incf other-from this-length)) + + ;; + ;; + ;; + ;; OTHER inserted at the same start position as we did. Since + ;; the situation is symmetric in the location properties of + ;; OTHER and THIS, we use the inserted data to construct an + ;; ordering. + ((= other-from this-from) + (when (string< this-data other-data) + (incf other-from this-length))))))) + + ;; + ;; Transform a delete operation + ;; + ((jupiter-delete-p other) + (with-slots ((this-from :from) + (this-to :to) + (this-length :length)) this + (with-slots ((other-from :from) + (other-to :to) + (other-length :length)) other + (cond + + ;; + ;; + ;; + ;; just keep OTHER + + ;; + ;; and and + ;; + ((>= other-from this-from) + (incf other-from this-length) + (incf other-to this-length)) + + ;; + ;; < other > + ;; + ;; OTHER deleted a region that includes the point at which THIS + ;; inserted in its interior. OTHER has to be split into one + ;; deletion before and one delete after the inserted data. + ((and (< other-from this-from) (> other-to this-to)) + (setq other + (jupiter-compound "compound" + :children (list (jupiter-delete "delete-left" + :from other-from + :to this-from) + (jupiter-delete "delete-right" + :from this-to + :to (+ other-to this-length)))))) + )))) + + ;; + ;; Transform a compound operation + ;; + ((jupiter-compound-p other) ;; TODO encapsulation violation + (with-slots (children) other + (dolist (child children) + (setf child (jupiter-transform this child))))) + + ;; + ;; Transform a nop operation + ;; + ((jupiter-nop-p other)) + + ;; TODO this is for debugging + (t (error "Cannot transform operation of type `%s'" + (object-class other)))) + other) + +(defmethod object-print ((this jupiter-insert) &rest strings) + "Add from, to, length and data to string representation of THIS." + (with-slots (from to length data) this + (call-next-method + this + (format " from %d" from) + (format " to %d" to) + (format " length %d" length) + (format " data \"%s\"" data))) + ) + +(provide 'jupiter-insert) +;;; jupiter-insert.el ends here diff --git a/emacs.d/lisp/rudel/jupiter/.svn/text-base/jupiter-nop.el.svn-base b/emacs.d/lisp/rudel/jupiter/.svn/text-base/jupiter-nop.el.svn-base new file mode 100644 index 0000000..e0f4a5c --- /dev/null +++ b/emacs.d/lisp/rudel/jupiter/.svn/text-base/jupiter-nop.el.svn-base @@ -0,0 +1,59 @@ +;;; jupiter-nop.el --- Jupiter no operation +;; +;; Copyright (C) 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: jupiter, operation, nop +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; Class `jupiter-nop' implements a no-operation for the Jupiter +;; algorithm. + + +;;; History: +;; +;; 0.1 - Initial revision. + + +;;; Code: +;; + +(require 'eieio) + +(require 'jupiter-operation) + + +;;; Class jupiter-nop +;; + +(defclass jupiter-nop (jupiter-operation) + () + "Operation, which does not change anything.") + +(defmethod rudel-apply ((this jupiter-nop) object) + "Applying THIS does not change OBJECT.") + +(defmethod jupiter-transform ((this jupiter-nop) other) + "Transforming OTHER with THIS simply returns OTHER." + other) + +(provide 'jupiter-nop) +;;; jupiter-nop.el ends here diff --git a/emacs.d/lisp/rudel/jupiter/.svn/text-base/jupiter-operation.el.svn-base b/emacs.d/lisp/rudel/jupiter/.svn/text-base/jupiter-operation.el.svn-base new file mode 100644 index 0000000..bc91fee --- /dev/null +++ b/emacs.d/lisp/rudel/jupiter/.svn/text-base/jupiter-operation.el.svn-base @@ -0,0 +1,61 @@ +;;; jupiter-operation.el --- Operation base class for jupiter algorithm +;; +;; Copyright (C) 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Jupiter, operation, base +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; The class jupiter-operation is a base class for Jupiter operation +;; classes. + + +;;; History: +;; +;; 0.1 - Initial revision. + + +;;; Code: +;; + +(require 'eieio) + +(require 'rudel-operations) + + +;;; Class jupiter-operation +;; + +(defclass jupiter-operation (rudel-operation) + () + "Objects of derived classes represent operations, which change documents. +Objects can transform each other to produce sequences of +operations, which produce identical changes than permutations of +the same operations." + :abstract t) + +;; This one really could use multiple dispatch +(defgeneric jupiter-transform ((this jupiter-operation) other) + "Transform OTHER such that the effect of applying it after THIS are equal to applying OTHER before THIS unmodified. +In general, OTHER is destructively modified or replaced.") + +(provide 'jupiter-operation) +;;; jupiter-operation.el ends here diff --git a/emacs.d/lisp/rudel/jupiter/.svn/text-base/jupiter.el.svn-base b/emacs.d/lisp/rudel/jupiter/.svn/text-base/jupiter.el.svn-base new file mode 100644 index 0000000..d285041 --- /dev/null +++ b/emacs.d/lisp/rudel/jupiter/.svn/text-base/jupiter.el.svn-base @@ -0,0 +1,135 @@ +;;; jupiter.el --- An implementation of the Jupiter algorithm +;; +;; Copyright (C) 2008, 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: rudel, jupiter, algorithm, distributed, integrity +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; This file contains an implementation of the jupiter algorithm, +;; which ensures the synchronization of data shared between multiple +;; peers despite differences in network latency. +;; +;; This implementation is partly based on the implementation used in +;; the obby library . Note, however, that +;; the details of the implementations differ. + + +;;; History: +;; +;; 0.1 - Initial revision. + + +;;; Code: +;; + +(eval-when-compile + (require 'cl)) + +(require 'eieio) + +(require 'jupiter-operation) +(require 'jupiter-insert) +(require 'jupiter-delete) +(require 'jupiter-compound) +(require 'jupiter-nop) + + +;;; Class jupiter-context +;; + +(defclass jupiter-context () + ((local-revision :initarg :local-revision + :type (integer 0) + :initform 0 + :documentation + "Revision number of the local data.") + (remote-revision :initarg :remote-revision + :type (integer 0) + :initform 0 + :documentation + "Revision number of the remote data.") + (local-log :initarg :local-log + :type list + :initform nil + :documentation + "List of local operations, that have not been +acknowledged by the remote side.")) + "Objects of this class store the state of one side of a +concurrent modification activity, which is synchronized using the +jupiter algorithm.") + +(defmethod jupiter-local-operation ((this jupiter-context) operation) + "Store OPERATION in the operation log of THIS and increase local revision count." + (with-slots (local-revision local-log) this + (push (cons local-revision operation) local-log) + (incf local-revision))) + +(defmethod jupiter-remote-operation ((this jupiter-context) + local-revision remote-revision + operation) + "Transform OPERATION with revisions LOCAL-REVISION and REMOTE-REVISION using the local operations stored in THIS. +LOCAL-REVISION is the local revision of THIS context, the remote +site is referring to." + (let ((transformed-operation operation)) + (with-slots ((this-remote-revision :remote-revision) + local-log) this + + ;; Discard stored local operations which are older than the + ;; local revision to which the remote site refers. + (setq local-log (delete-if + (lambda (revision) (< revision local-revision)) + local-log + :key 'car)) + + ;; Transform the operation + (mapc + (lambda (log-operation) + + ;; Transform the remote operation using the stored operation. + (setq transformed-operation + (jupiter-transform (cdr log-operation) + transformed-operation)) + + ;; Transform the stored operation using the already + ;; transformed remote operation. + (setf (cdr log-operation) + (jupiter-transform transformed-operation + (cdr log-operation)))) + (reverse local-log)) + + ;; Increase remote revision + (incf this-remote-revision)) + ;; The transformed operation is the result of the computation. + transformed-operation) + ) + +(defmethod object-print ((this jupiter-context) &rest strings) + "Add revisions and log length to string representation of THIS." + (with-slots (local-revision remote-revision local-log) this + (call-next-method + this + (format " local %d" local-revision) + (format " remote %d" remote-revision) + (format " log-items %d" (length local-log))))) + +(provide 'jupiter) +;;; jupiter.el ends here diff --git a/emacs.d/lisp/rudel/jupiter/Project.ede b/emacs.d/lisp/rudel/jupiter/Project.ede new file mode 100644 index 0000000..275d1ed --- /dev/null +++ b/emacs.d/lisp/rudel/jupiter/Project.ede @@ -0,0 +1,14 @@ +;; Object rudel/jupiter +;; EDE project file. +(ede-proj-project "rudel/jupiter" + :name "jupiter" + :file "Project.ede" + :targets (list + (ede-proj-target-elisp "jupiter" + :name "jupiter" + :path "" + :source '("jupiter.el" "jupiter-operation.el" "jupiter-insert.el" "jupiter-delete.el" "jupiter-compound.el" "jupiter-nop.el") + :aux-packages '("rudel") + ) + ) + ) diff --git a/emacs.d/lisp/rudel/jupiter/jupiter-compound.el b/emacs.d/lisp/rudel/jupiter/jupiter-compound.el new file mode 100644 index 0000000..789bbc2 --- /dev/null +++ b/emacs.d/lisp/rudel/jupiter/jupiter-compound.el @@ -0,0 +1,89 @@ +;;; jupiter-compound.el --- Jupiter compound operation +;; +;; Copyright (C) 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: jupiter, operation, compound +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; Class `jupiter-compound' implements a compound operation comprised +;; of a number of child operations. + + +;;; History: +;; +;; 0.1 - Initial revision + + +;;; Code: +;; + +(require 'eieio) + +(require 'jupiter-operation) + + +;;; Class jupiter-compound +;; + +(defclass jupiter-compound (jupiter-operation) + ((children :initarg :children + :type list + :initform nil + :documentation + "")) + "Objects of this class are operations, which are composed of a +number of child operation.") + +;; TODO this has side effects. It can only be called once +(defmethod rudel-apply ((this jupiter-compound) object) + "Apply THIS to BUFFER by applying the child operation." + (with-slots (children) this + (let ((child (first children)) + (rest (rest children))) + ;; Apply all child operations + (while child + (rudel-apply child object) + ;; For each applied child operation, transform remaining + ;; operation with the applied operation. + (dolist (next rest) + (setf next (jupiter-transform child next))) + ;; Advance to next child operation. + (setq child (first rest) + rest (rest rest))))) + ) + +(defmethod jupiter-transform ((this jupiter-compound) other) + "Transform OTHER using the child operations of THIS." + (with-slots (children) this + (dolist (child children) ;; TODO reverse children? + (setq other (jupiter-transform child other))) + other)) + +(defmethod object-print ((this jupiter-compound) &rest strings) + "Add number of children to string representation of THIS." + (with-slots (children) this + (call-next-method + this + (format " children %d" (length children))))) + +(provide 'jupiter-compound) +;;; jupiter-compound.el ends here diff --git a/emacs.d/lisp/rudel/jupiter/jupiter-delete.el b/emacs.d/lisp/rudel/jupiter/jupiter-delete.el new file mode 100644 index 0000000..db2e985 --- /dev/null +++ b/emacs.d/lisp/rudel/jupiter/jupiter-delete.el @@ -0,0 +1,176 @@ +;;; jupiter-delete.el --- Jupiter delete operation +;; +;; Copyright (C) 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: jupiter, operation, delete +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; Class `jupiter-delete' implements a delete operation for the +;; Jupiter algorithm. + + +;;; History: +;; +;; 0.1 - Initial revision + + +;;; Code: +;; + +(require 'eieio) + +(require 'rudel-operations) +(require 'jupiter-operation) + + +;;; Class jupiter-delete +;; + +(defclass jupiter-delete (jupiter-operation + rudel-delete-op) + () + "Objects of this class represent deletions in buffers.") + +(defmethod jupiter-transform ((this jupiter-delete) other) + "Transform other using THIS. +OTHER is destructively modified or replaced." + (cond + + ;; + ;; Transform an insert operation + ;; + ((jupiter-insert-p other) + (with-slots ((this-from :from) + (this-to :to) + (this-length :length)) this + (with-slots ((other-from :from) + (other-to :to) + (other-length :length)) other + (cond + ;; + ;; + ;; + ;; + ((<= other-to this-from)) + + ;; + ;; + ((> other-from this-to) + (decf other-from this-length)) + + ;; + ;; < this > + ((and (> other-from this-from) (< other-to this-to)) + (setq other-from this-from)) + ))) + ) + + ;; + ;; Transform a delete operation + ;; + ((jupiter-delete-p other) + (with-slots ((this-from :from) + (this-to :to) + (this-length :length)) this + (with-slots ((other-from :from) + (other-to :to) + (other-length :length)) other + (cond + + ;; + ;; + ;; OTHER deleted a region after the region deleted by + ;; THIS. Therefore OTHER has to be shifted by the length of + ;; the deleted region. + ((> other-from this-to) + (decf other-from this-length) + (decf other-to this-length)) + + ;; + ;; + ;; OTHER deleted a region before the region affected by + ;; THIS. That is not affected by THIS operation. + ((<= other-to this-from)) + + ;; < other > + ;; + ((and (>= other-from this-from) (>= other-to this-to)) + (decf other-to this-length)) + + ;; + ;; + ((and (< other-from this-from) (< other-to this-to)) + (decf other-to (- other-to this-to))) + + ;; + ;; + ;; The region deleted by OTHER overlaps with the region + ;; deleted by THIS, such that a part of the region of this is + ;; before the region of OTHER. The first part of the region + ;; deleted by OTHER has already been deleted. Therefore, the + ;; start of OTHER has to be shifted by the length of the + ;; overlap. + ((and (< other-from this-to) (> other-to this-to)) + (setq other-from this-from) + (incf other-to (+ other-from (- other-to this-to)))) + + ;; + ;; < this > + ;; The region deleted by OTHER is completely contained in + ;; the region affected by THIS. Therefore, OTHER must not + ;; be executed. + ((and (>= other-from this-from) (<= other-to this-to)) + (setq other (jupiter-nop "nop"))) + + (t (error "logic error in (jupiter-transform (x jupiter-delete) (y jupiter-delete))")) + )))) + + ;; + ;; Transform a compound operation + ;; + ((jupiter-compound-p other) ;; TODO encapsulation violation + (with-slots (children) other + (dolist (child children) + (setf child (jupiter-transform this child))))) + + ;; + ;; Transform a nop operation + ;; + ((jupiter-nop-p other)) + + ;; TODO this is for debugging + (t (error "Cannot transform operation of type `%s'" + (object-class other)))) + other) + +(defmethod object-print ((this jupiter-delete) &rest strings) + "Add from, to and length to string representation of THIS." + (with-slots (from to length) this + (call-next-method + this + (format " from %d" from) + (format " to %d" to) + (format " length %d" length))) + ) + +(provide 'jupiter-delete) +;;; jupiter-delete.el ends here diff --git a/emacs.d/lisp/rudel/jupiter/jupiter-insert.el b/emacs.d/lisp/rudel/jupiter/jupiter-insert.el new file mode 100644 index 0000000..339270e --- /dev/null +++ b/emacs.d/lisp/rudel/jupiter/jupiter-insert.el @@ -0,0 +1,165 @@ +;;; jupiter-insert.el --- Jupiter insert operation +;; +;; Copyright (C) 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: jupiter, operation, insert +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; Class `jupiter-insert' implements an insert operation for the +;; Jupiter algorithm. + + +;;; History: +;; +;; 0.1 - Initial revision + + +;;; Code: +;; + +(require 'eieio) + +(require 'rudel-operations) +(require 'jupiter-operation) + + +;;; Class jupiter-insert +;; + +(defclass jupiter-insert (jupiter-operation + rudel-insert-op) + () + "Objects of this class represent insertions into buffers.") + +(defmethod jupiter-transform ((this jupiter-insert) other) + "Transform OTHER using THIS." + (cond + + ;; + ;; Transform an insert operation + ;; + ((jupiter-insert-p other) + (with-slots ((this-from :from) + (this-to :to) + (this-length :length) + (this-data :data)) this + (with-slots ((other-from :from) + (other-to :to) + (other-length :length) + (other-data :data)) other + (cond + ;; + ;; + ;; + ;; No need to do anything in this case. + ;; ((< other-from this-from)) + + ;; + ;; + ;; + ;; + ((> other-from this-from) + (incf other-from this-length)) + + ;; + ;; + ;; + ;; OTHER inserted at the same start position as we did. Since + ;; the situation is symmetric in the location properties of + ;; OTHER and THIS, we use the inserted data to construct an + ;; ordering. + ((= other-from this-from) + (when (string< this-data other-data) + (incf other-from this-length))))))) + + ;; + ;; Transform a delete operation + ;; + ((jupiter-delete-p other) + (with-slots ((this-from :from) + (this-to :to) + (this-length :length)) this + (with-slots ((other-from :from) + (other-to :to) + (other-length :length)) other + (cond + + ;; + ;; + ;; + ;; just keep OTHER + + ;; + ;; and and + ;; + ((>= other-from this-from) + (incf other-from this-length) + (incf other-to this-length)) + + ;; + ;; < other > + ;; + ;; OTHER deleted a region that includes the point at which THIS + ;; inserted in its interior. OTHER has to be split into one + ;; deletion before and one delete after the inserted data. + ((and (< other-from this-from) (> other-to this-to)) + (setq other + (jupiter-compound "compound" + :children (list (jupiter-delete "delete-left" + :from other-from + :to this-from) + (jupiter-delete "delete-right" + :from this-to + :to (+ other-to this-length)))))) + )))) + + ;; + ;; Transform a compound operation + ;; + ((jupiter-compound-p other) ;; TODO encapsulation violation + (with-slots (children) other + (dolist (child children) + (setf child (jupiter-transform this child))))) + + ;; + ;; Transform a nop operation + ;; + ((jupiter-nop-p other)) + + ;; TODO this is for debugging + (t (error "Cannot transform operation of type `%s'" + (object-class other)))) + other) + +(defmethod object-print ((this jupiter-insert) &rest strings) + "Add from, to, length and data to string representation of THIS." + (with-slots (from to length data) this + (call-next-method + this + (format " from %d" from) + (format " to %d" to) + (format " length %d" length) + (format " data \"%s\"" data))) + ) + +(provide 'jupiter-insert) +;;; jupiter-insert.el ends here diff --git a/emacs.d/lisp/rudel/jupiter/jupiter-nop.el b/emacs.d/lisp/rudel/jupiter/jupiter-nop.el new file mode 100644 index 0000000..e0f4a5c --- /dev/null +++ b/emacs.d/lisp/rudel/jupiter/jupiter-nop.el @@ -0,0 +1,59 @@ +;;; jupiter-nop.el --- Jupiter no operation +;; +;; Copyright (C) 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: jupiter, operation, nop +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; Class `jupiter-nop' implements a no-operation for the Jupiter +;; algorithm. + + +;;; History: +;; +;; 0.1 - Initial revision. + + +;;; Code: +;; + +(require 'eieio) + +(require 'jupiter-operation) + + +;;; Class jupiter-nop +;; + +(defclass jupiter-nop (jupiter-operation) + () + "Operation, which does not change anything.") + +(defmethod rudel-apply ((this jupiter-nop) object) + "Applying THIS does not change OBJECT.") + +(defmethod jupiter-transform ((this jupiter-nop) other) + "Transforming OTHER with THIS simply returns OTHER." + other) + +(provide 'jupiter-nop) +;;; jupiter-nop.el ends here diff --git a/emacs.d/lisp/rudel/jupiter/jupiter-operation.el b/emacs.d/lisp/rudel/jupiter/jupiter-operation.el new file mode 100644 index 0000000..bc91fee --- /dev/null +++ b/emacs.d/lisp/rudel/jupiter/jupiter-operation.el @@ -0,0 +1,61 @@ +;;; jupiter-operation.el --- Operation base class for jupiter algorithm +;; +;; Copyright (C) 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Jupiter, operation, base +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; The class jupiter-operation is a base class for Jupiter operation +;; classes. + + +;;; History: +;; +;; 0.1 - Initial revision. + + +;;; Code: +;; + +(require 'eieio) + +(require 'rudel-operations) + + +;;; Class jupiter-operation +;; + +(defclass jupiter-operation (rudel-operation) + () + "Objects of derived classes represent operations, which change documents. +Objects can transform each other to produce sequences of +operations, which produce identical changes than permutations of +the same operations." + :abstract t) + +;; This one really could use multiple dispatch +(defgeneric jupiter-transform ((this jupiter-operation) other) + "Transform OTHER such that the effect of applying it after THIS are equal to applying OTHER before THIS unmodified. +In general, OTHER is destructively modified or replaced.") + +(provide 'jupiter-operation) +;;; jupiter-operation.el ends here diff --git a/emacs.d/lisp/rudel/jupiter/jupiter.el b/emacs.d/lisp/rudel/jupiter/jupiter.el new file mode 100644 index 0000000..d285041 --- /dev/null +++ b/emacs.d/lisp/rudel/jupiter/jupiter.el @@ -0,0 +1,135 @@ +;;; jupiter.el --- An implementation of the Jupiter algorithm +;; +;; Copyright (C) 2008, 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: rudel, jupiter, algorithm, distributed, integrity +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; This file contains an implementation of the jupiter algorithm, +;; which ensures the synchronization of data shared between multiple +;; peers despite differences in network latency. +;; +;; This implementation is partly based on the implementation used in +;; the obby library . Note, however, that +;; the details of the implementations differ. + + +;;; History: +;; +;; 0.1 - Initial revision. + + +;;; Code: +;; + +(eval-when-compile + (require 'cl)) + +(require 'eieio) + +(require 'jupiter-operation) +(require 'jupiter-insert) +(require 'jupiter-delete) +(require 'jupiter-compound) +(require 'jupiter-nop) + + +;;; Class jupiter-context +;; + +(defclass jupiter-context () + ((local-revision :initarg :local-revision + :type (integer 0) + :initform 0 + :documentation + "Revision number of the local data.") + (remote-revision :initarg :remote-revision + :type (integer 0) + :initform 0 + :documentation + "Revision number of the remote data.") + (local-log :initarg :local-log + :type list + :initform nil + :documentation + "List of local operations, that have not been +acknowledged by the remote side.")) + "Objects of this class store the state of one side of a +concurrent modification activity, which is synchronized using the +jupiter algorithm.") + +(defmethod jupiter-local-operation ((this jupiter-context) operation) + "Store OPERATION in the operation log of THIS and increase local revision count." + (with-slots (local-revision local-log) this + (push (cons local-revision operation) local-log) + (incf local-revision))) + +(defmethod jupiter-remote-operation ((this jupiter-context) + local-revision remote-revision + operation) + "Transform OPERATION with revisions LOCAL-REVISION and REMOTE-REVISION using the local operations stored in THIS. +LOCAL-REVISION is the local revision of THIS context, the remote +site is referring to." + (let ((transformed-operation operation)) + (with-slots ((this-remote-revision :remote-revision) + local-log) this + + ;; Discard stored local operations which are older than the + ;; local revision to which the remote site refers. + (setq local-log (delete-if + (lambda (revision) (< revision local-revision)) + local-log + :key 'car)) + + ;; Transform the operation + (mapc + (lambda (log-operation) + + ;; Transform the remote operation using the stored operation. + (setq transformed-operation + (jupiter-transform (cdr log-operation) + transformed-operation)) + + ;; Transform the stored operation using the already + ;; transformed remote operation. + (setf (cdr log-operation) + (jupiter-transform transformed-operation + (cdr log-operation)))) + (reverse local-log)) + + ;; Increase remote revision + (incf this-remote-revision)) + ;; The transformed operation is the result of the computation. + transformed-operation) + ) + +(defmethod object-print ((this jupiter-context) &rest strings) + "Add revisions and log length to string representation of THIS." + (with-slots (local-revision remote-revision local-log) this + (call-next-method + this + (format " local %d" local-revision) + (format " remote %d" remote-revision) + (format " log-items %d" (length local-log))))) + +(provide 'jupiter) +;;; jupiter.el ends here diff --git a/emacs.d/lisp/rudel/obby/.svn/all-wcprops b/emacs.d/lisp/rudel/obby/.svn/all-wcprops new file mode 100644 index 0000000..1794a33 --- /dev/null +++ b/emacs.d/lisp/rudel/obby/.svn/all-wcprops @@ -0,0 +1,53 @@ +K 25 +svn:wc:ra_dav:version-url +V 38 +/svnroot/rudel/!svn/ver/468/trunk/obby +END +rudel-obby-client.el +K 25 +svn:wc:ra_dav:version-url +V 59 +/svnroot/rudel/!svn/ver/397/trunk/obby/rudel-obby-client.el +END +rudel-obby-state.el +K 25 +svn:wc:ra_dav:version-url +V 58 +/svnroot/rudel/!svn/ver/330/trunk/obby/rudel-obby-state.el +END +Project.ede +K 25 +svn:wc:ra_dav:version-url +V 50 +/svnroot/rudel/!svn/ver/215/trunk/obby/Project.ede +END +rudel-obby.el +K 25 +svn:wc:ra_dav:version-url +V 52 +/svnroot/rudel/!svn/ver/397/trunk/obby/rudel-obby.el +END +rudel-obby-debug.el +K 25 +svn:wc:ra_dav:version-url +V 58 +/svnroot/rudel/!svn/ver/318/trunk/obby/rudel-obby-debug.el +END +rudel-obby-server.el +K 25 +svn:wc:ra_dav:version-url +V 59 +/svnroot/rudel/!svn/ver/356/trunk/obby/rudel-obby-server.el +END +rudel-obby-errors.el +K 25 +svn:wc:ra_dav:version-url +V 59 +/svnroot/rudel/!svn/ver/319/trunk/obby/rudel-obby-errors.el +END +rudel-obby-util.el +K 25 +svn:wc:ra_dav:version-url +V 57 +/svnroot/rudel/!svn/ver/468/trunk/obby/rudel-obby-util.el +END diff --git a/emacs.d/lisp/rudel/obby/.svn/entries b/emacs.d/lisp/rudel/obby/.svn/entries new file mode 100644 index 0000000..2d21fc4 --- /dev/null +++ b/emacs.d/lisp/rudel/obby/.svn/entries @@ -0,0 +1,300 @@ +10 + +dir +545 +https://rudel.svn.sourceforge.net/svnroot/rudel/trunk/obby +https://rudel.svn.sourceforge.net/svnroot/rudel + + + +2009-10-16T01:48:03.160288Z +468 +scymtym + + + + + + + + + + + + + + +694b31df-dcbb-44e8-af88-74c7ea918228 + +rudel-obby-client.el +file + + + + +2009-11-18T14:01:44.000000Z +9cb0ae63b9f86199be36f208feb51b26 +2009-10-10T00:39:13.745463Z +397 +scymtym + + + + + + + + + + + + + + + + + + + + + +30971 + +rudel-obby-state.el +file + + + + +2009-11-18T14:01:44.000000Z +f7a018739f96fe975da0ab4cc112e7e4 +2009-10-03T01:58:59.186010Z +330 +scymtym + + + + + + + + + + + + + + + + + + + + + +4955 + +Project.ede +file + + + + +2009-11-18T14:01:44.000000Z +a99a4bf5a1243e8bd25e87d74b02db1d +2009-10-03T00:45:44.545055Z +215 +scymtym + + + + + + + + + + + + + + + + + + + + + +388 + +rudel-obby.el +file + + + + +2009-11-18T14:01:44.000000Z +af5ba069b2183fac68261ba2d34f199d +2009-10-10T00:39:13.745463Z +397 +scymtym + + + + + + + + + + + + + + + + + + + + + +15453 + +rudel-obby-debug.el +file + + + + +2009-11-18T14:01:44.000000Z +915461ef0b7ccb7dbad60a69aa973389 +2009-10-03T01:56:22.854481Z +318 +scymtym + + + + + + + + + + + + + + + + + + + + + +3300 + +rudel-obby-server.el +file + + + + +2009-11-18T14:01:44.000000Z +cd9ab22e9a8d09b558693edfc7b5505a +2009-10-03T02:08:53.322972Z +356 +scymtym + + + + + + + + + + + + + + + + + + + + + +24935 + +rudel-obby-errors.el +file + + + + +2009-11-18T14:01:44.000000Z +72c6c1dfe7ec7b8a95d5808d439088dd +2009-10-03T01:56:39.182216Z +319 +scymtym + + + + + + + + + + + + + + + + + + + + + +1839 + +rudel-obby-util.el +file + + + + +2009-11-18T14:01:44.000000Z +991ece971eaeba3b86359fc05a410b9d +2009-10-16T01:48:03.160288Z +468 +scymtym + + + + + + + + + + + + + + + + + + + + + +9523 + diff --git a/emacs.d/lisp/rudel/obby/.svn/text-base/Project.ede.svn-base b/emacs.d/lisp/rudel/obby/.svn/text-base/Project.ede.svn-base new file mode 100644 index 0000000..53d2422 --- /dev/null +++ b/emacs.d/lisp/rudel/obby/.svn/text-base/Project.ede.svn-base @@ -0,0 +1,14 @@ +;; Object rudel/obby +;; EDE project file. +(ede-proj-project "rudel/obby" + :name "obby" + :file "Project.ede" + :targets (list + (ede-proj-target-elisp "obby" + :name "obby" + :path "" + :source '("rudel-obby.el" "rudel-obby-util.el" "rudel-obby-client.el" "rudel-obby-server.el" "rudel-obby-errors.el" "rudel-obby-state.el") + :aux-packages '("rudel" "jupiter") + ) + ) + ) diff --git a/emacs.d/lisp/rudel/obby/.svn/text-base/rudel-obby-client.el.svn-base b/emacs.d/lisp/rudel/obby/.svn/text-base/rudel-obby-client.el.svn-base new file mode 100644 index 0000000..5c192db --- /dev/null +++ b/emacs.d/lisp/rudel/obby/.svn/text-base/rudel-obby-client.el.svn-base @@ -0,0 +1,973 @@ +;;; rudel-obby-client.el --- Client functions of the Rudel obby backend +;; +;; Copyright (C) 2008, 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Rudel, obby, backend, client +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; This file contains the client part of the obby backend. + + +;;; History: +;; +;; 0.2 - State machine. +;; +;; 0.1 - Initial revision. + + +;;; Code: +;; + +(require 'eieio) + +(require 'jupiter) + +(require 'rudel-state-machine) +(require 'rudel-operations) +(require 'rudel-chat) + +(require 'rudel-obby-errors) +(require 'rudel-obby-util) +(require 'rudel-obby-state) + + +;;; Class rudel-obby-client-state-new +;; + +(defclass rudel-obby-client-state-new + (rudel-obby-client-connection-state) + () + "Start state of newly established connections.") + +(defmethod rudel-obby/obby_welcome + ((this rudel-obby-client-state-new) version) + "Handle obby 'welcome' message." + ;; Examine announced protocol version. + (with-parsed-arguments ((version number)) + (message "Received Obby welcome message (version %d)" version)) + ;; Start encryption handshake + 'encryption-negotiate) + + +;;; Class rudel-obby-client-state-encryption-negotiate +;; + +(defclass rudel-obby-client-state-encryption-negotiate + (rudel-obby-client-connection-state) + () + "Start state of the encryption handshake.") + +(defmethod rudel-obby/net6_encryption + ((this rudel-obby-client-state-encryption-negotiate) value) + "Handle net6 'encryption' message." + (rudel-send this "net6_encryption_ok") + 'encryption-start) + + +;;; Class rudel-obby-client-connection-encryption-start +;; + +(defclass rudel-obby-client-state-encryption-start + (rudel-obby-client-connection-state) + () + "Second state of the encryption handshake.") + +(defmethod rudel-obby/net6_encryption_begin + ((this rudel-obby-client-state-encryption-start)) + "Handle net6 'encryption_begin' message." + ;; Start TLS encryption for the connection. + (with-slots (connection) this + (with-slots (socket) connection + (when (rudel-process-object socket :supports-tls) + (rudel-tls-start-tls socket) + (sit-for 1)))) + + ;; The connection is now established + 'joining) + +(defmethod rudel-obby/net6_encryption_failed + ((this rudel-obby-client-state-encryption-start)) + "Handle net6 'encryption_failed' message." + ;; The connection is now established; without encryption though + 'joining) + + +;;; Class rudel-obby-client-state-joining +;; + +(defclass rudel-obby-client-state-joining + (rudel-obby-client-connection-state) + () + "First state after the connection has been properly set up.") + +(defmethod rudel-enter ((this rudel-obby-client-state-joining)) + "When entering this state, send a login request." + ;; Send login request with username and color. This can easily fail + ;; (resulting in response 'net6_login_failed') if the username or + ;; color is already taken. + (with-slots (info) (oref this connection) + (let ((username (plist-get info :username)) + (color (plist-get info :color)) + (global-password (plist-get info :global-password)) + (user-password (plist-get info :user-password))) + (apply #'rudel-send + this + "net6_client_login" + username (rudel-obby-format-color color) + (append (when global-password + (list global-password)) + (when (and global-password user-password) + (list user-password)))))) + nil) + +(defmethod rudel-obby/obby_sync_init + ((this rudel-obby-client-state-joining) count) + "Handle obby 'sync_init' message." + ;; Switch to 'synching' state, passing the number of synchronization + ;; items. + (with-parsed-arguments ((count number)) + (list 'session-synching count))) + +(defmethod rudel-obby/net6_login_failed + ((this rudel-obby-client-state-joining) reason) + "Handle net6 'login_failed' message." + (with-parsed-arguments ((reason number)) + (with-slots (connection) this + (let ((error-data + (cond + ;; Invalid username + ((= reason rudel-obby-error-username-invalid) + (cons 'rudel-obby-username-invalid nil)) + ;; Username in use + ((= reason rudel-obby-error-username-in-use) + (cons 'rudel-obby-username-in-use nil)) + ;; Color in use + ((= reason rudel-obby-error-color-in-use) + (cons 'rudel-obby-color-in-use nil)) + ;; Wrong global password + ((= reason rudel-obby-error-wrong-global-password) + (cons 'rudel-obby-wrong-global-password nil)) + ;; Wrong user password + ((= reason rudel-obby-error-wrong-user-password) + (cons 'rudel-obby-wrong-user-password nil)) + ;; Otherwise, signal a generic join error + (t (cons 'rudel-join-error nil))))) + + ;; Switch to 'join-failed' state, pass the error data. + (list 'join-failed error-data)))) + ) + + +;;; Class rudel-obby-client-state-join-failed +;; + +(defclass rudel-obby-client-state-join-failed + (rudel-obby-client-connection-state) + ((error-symbol :initarg :error-symbol + :type symbol + :documentation + "Error symbol describing the reason for the +login failure.") + (error-data :initarg :error-data + :type list + :documentation + "Additional error data describing the login +failure.")) + "State for failed login attempts.") + +(defmethod rudel-enter ((this rudel-obby-client-state-join-failed) + error) + "When the state is entered, store the error data passed in ERROR." + (with-slots (error-symbol error-data) this + (setq error-symbol (car error) + error-data (cdr error))) + nil) + + +;;; Class rudel-obby-client-state idle +;; + +(defclass rudel-obby-client-state-idle + (rudel-obby-client-connection-state + rudel-obby-document-handler) + () + "Default state of the connection.") + +(defmethod rudel-obby/net6_client_join + ((this rudel-obby-client-state-idle) + client-id name encryption user-id color) + "Handle net6 'client_join' message." + (with-parsed-arguments ((client-id number) + (user-id number) + (color color)) + (with-slots (connection) this + (with-slots (session) connection + (let ((user (rudel-find-user session user-id + #'eq #'rudel-id))) + (if user + ;; If we have such a user object, update its state. + (with-slots ((client-id1 client-id) + (color1 color) + connected + (encryption1 encryption)) user + (setq client-id1 client-id + color1 color + connected t + encryption1 (string= encryption "1")) + + ;; Run the change hook of the user object. + (object-run-hook-with-args user 'change-hook)) + ;; Otherwise, create a new user object. + (let ((user (rudel-obby-user + name + :client-id client-id + :user-id user-id + :connected t + :encryption (string= encryption "1") + :color color))) + (rudel-add-user session user)))))) + (message "Client joined: %s %s" name color)) + nil) + +(defmethod rudel-obby/net6_client_part + ((this rudel-obby-client-state-idle) client-id) + "Handle net6 'client_part' message." + ;; Find the user object, associated to the client id. Remove the + ;; client id and change the user's state to disconnected. + (with-parsed-arguments ((client-id number)) + (with-slots (connection) this + (with-slots (session) connection + (let ((user (rudel-find-user session client-id + #'eql #'rudel-client-id))) + (if user + (with-slots (client-id connected) user + ;; Set slot values. + (setq client-id nil + connected nil) + + ;; Run the change hook of the user object. + (object-run-hook-with-args user 'change-hook)) + (display-warning + '(rudel obby) + (format "Cannot find user for client id: %d" + client-id) + :warning)))))) + nil) + +(defmethod rudel-obby/obby_user_colour + ((this rudel-obby-client-state-idle) user-id color) + "Handle obby 'user_colour' message." + (with-parsed-arguments ((user-id number) + (color color)) + ;; Find user object and set color. + (with-slots (connection) this + (with-slots (session) connection + (let ((user (rudel-find-user session user-id + #'= #'rudel-id))) + (with-slots ((name :object-name) (color1 :color)) user + ;; Set color in user object. + (setq color1 color) + + ;; Run the change hook of the user object. + (object-run-hook-with-args user 'change-hook) + + ;; Update overlays. + (rudel-overlay-set-face-attributes + (rudel-overlay-make-face-symbol 'author name) + color1)))))) + nil) + +(defmethod rudel-obby/obby_document_create + ((this rudel-obby-client-state-idle) + owner-id doc-id name suffix encoding) + "Handle obby 'document_create' message." + (with-parsed-arguments ((owner-id number) + (doc-id number) + (suffix number) + (encoding coding-system)) + (with-slots (connection) this + (with-slots (session) connection + (let ((owner (rudel-find-user session owner-id + #'= #'rudel-id))) + (rudel-add-document session (rudel-obby-document + name + :subscribed (list owner) + :id doc-id + :owner-id owner-id + :suffix suffix)))) + (message "New document: %s" name))) + nil) + +(defmethod rudel-obby/obby_document_remove + ((this rudel-obby-client-state-idle) doc-id) + "Handle obby 'document_remove' message." + (with-parsed-arguments ((doc-id document-id)) + (with-slots (connection) this + (with-slots (session) connection + (let ((document (rudel-find-document + session doc-id + #'equal #'rudel-both-ids))) + (if document + (progn + (rudel-remove-document session document) + (with-slots ((name :object-name)) document + (message "Document removed: %s" name))) + (display-warning + '(rudel obby) + (format "Document not found: %s" doc-id) + :warning)))))) + nil) + +(defmethod rudel-obby/obby_document/rename + ((this rudel-obby-client-state-idle) + document user new-name new-suffix) + "Handle obby 'rename' submessage of the 'obby_document' message." + (with-parsed-arguments ((new-suffix number)) + (with-slots ((name :object-name) suffix) document + (setq name new-name + suffix new-suffix))) + nil) + +(defmethod rudel-obby/obby_document/subscribe + ((this rudel-obby-client-state-idle) + document user-id) + "Handle 'subscribe' submessage of obby 'document' message." + (with-parsed-arguments ((user-id number)) + (with-slots (connection) this + (with-slots (session) connection + (let ((user (rudel-find-user session user-id + #'= #'rudel-id))) + (rudel-add-user document user))))) + nil) + +(defmethod rudel-obby/obby_document/unsubscribe + ((this rudel-obby-client-state-idle) + document user-id) + "Handle 'unsubscribe' submessage of obby 'document' message." + (with-parsed-arguments ((user-id number)) + (with-slots (connection) this + (with-slots (session) connection + (let ((user (rudel-find-user session user-id + #'= #'rudel-id))) + (rudel-remove-user document user))))) + nil) + +(defmethod rudel-obby/obby_document/record + ((this rudel-obby-client-state-idle) + document user-id local-revision remote-revision + action &rest arguments) + "Handle 'record' submessage of obby 'document' message." + (with-parsed-arguments ((user-id number) + (local-revision number) + (remote-revision number)) + ;; Locate the user. + (let ((user (with-slots (connection) this + (with-slots (session) connection + (rudel-find-user session user-id + #'= #'rudel-id))))) + (if user + (condition-case error + ;; Try to dispatch + (rudel-dispatch + this "rudel-obby/obby_document/record/" action + (append (list document user local-revision remote-revision) + arguments)) + ;; Warn if we failed to locate or execute the + ;; method. Return nil in this case, so we remain in the + ;; current state. + (rudel-dispatch-error + (progn + (display-warning + '(rudel obby) + (format "%s: no method (%s: %s): `%s:%s'; arguments: %s" + (object-print this) (car error) (cdr error) + "rudel-obby/obby_document/record/" action arguments) + :debug) + nil))) + ;; If we did not find the user, warn. + (progn + (display-warning + '(rudel obby) + (format "User not found: %d" user-id) + :warning) + nil)))) + ) + +(defmethod rudel-obby/obby_document/record/ins + ((this rudel-obby-client-state-idle) + document user local-revision remote-revision + position data) + "Handle 'ins' submessage of 'record' submessage of obby 'document' message." + (with-parsed-arguments ((position number)) + (let ((operation (jupiter-insert + (format "insert-%d-%d" + remote-revision local-revision) + :from position + :data data))) + (with-slots (connection) this + (rudel-remote-operation connection + document user + remote-revision local-revision + operation)))) + nil) + +(defmethod rudel-obby/obby_document/record/del + ((this rudel-obby-client-state-idle) + document user local-revision remote-revision + position length) + "Handle 'del' submessage of 'record' submessage of obby 'document' message." + (with-parsed-arguments ((position number) + (length number)) + (let ((operation (jupiter-delete + (format "delete-%d-%d" + remote-revision local-revision) + :from position + :to (+ position length)))) + (with-slots (connection) this + (rudel-remote-operation connection + document user + remote-revision local-revision + operation)))) + nil) + +(defmethod rudel-obby/obby_document/record/split + ((this rudel-obby-client-state-idle) + document user local-revision remote-revision + &rest operations) + "Handle 'split' submessage of 'record' submessage of obby 'document' message." + (let ((operation (rudel-message->operation + (cons "split" operations) + local-revision remote-revision))) + (with-slots (connection) this + (rudel-remote-operation connection + document user + remote-revision local-revision + operation))) + nil) + +(defmethod rudel-obby/obby_document/record/noop + ((this rudel-obby-client-state-idle) + document user local-revision remote-revision) + "Handle 'noop' submessage of 'record' submessage of obby 'document' message." + (let ((operation (jupiter-nop + (format "nop-%d-%d" + remote-revision local-revision)))) + (with-slots (connection) this + (rudel-remote-operation connection + document user + remote-revision local-revision + operation))) + nil) + +(defmethod rudel-obby/obby_message ((this rudel-obby-client-state-idle) + sender text) + "Handle obby 'message' message" + (with-parsed-arguments ((sender number)) + (with-slots (session) (oref this :connection) + (let ((sender (rudel-find-user session sender #'eq #'rudel-id))) + (rudel-chat-dispatch-message sender text)))) + nil) + + +;;; Class rudel-obby-client-state-session-synching +;; + +(defclass rudel-obby-client-state-session-synching + (rudel-obby-client-connection-state) + ((all-items :initarg :all-items + :type (integer 0) + :documentation + "Total number of synchronization items expected + to receive from the server.") + (remaining-items :initarg :remaining-items + :type (integer 0) + :documentation + "Number of synchronization items not yet + received from the server.") + (have-self :initarg :have-self + :type boolean + :documentation + "Flag that remembers, whether the session has + a 'self' user object.")) + "State used for synching session data.") + +(defmethod rudel-enter ((this rudel-obby-client-state-session-synching) + num-items) + "When entering state, store number of expected items." + (with-slots (all-items remaining-items have-self) this + (setq all-items num-items + remaining-items num-items + have-self nil)) + nil) + +(defmethod rudel-obby/net6_client_join + ((this rudel-obby-client-state-session-synching) + client-id name encryption user-id color) + "Handle net6 'client_join' message." + (with-parsed-arguments ((client-id number) + (user-id number) + (color color)) + (with-slots (connection remaining-items have-self) this + (with-slots (session) connection + ;; Construct user object and add it to the session. + (let ((user (rudel-obby-user + name + :client-id client-id + :user-id user-id + :connected t + :encryption (string= encryption "1") + :color color))) + (rudel-add-user session user) + + ;; The first user object describes the user of this client. + (unless have-self + (with-slots (self) session + (setq self user + have-self t))))) + + ;; Decrease number of not yet received synchronization items. + (decf remaining-items))) + nil) + +(defmethod rudel-obby/obby_sync_usertable_user + ((this rudel-obby-client-state-session-synching) user-id name color) + "Handle obby 'sync_usertable_user' message." + (with-parsed-arguments ((user-id number) + (color color)) + (with-slots (connection remaining-items) this + (with-slots (session) connection + (rudel-add-user session (rudel-obby-user + name + :user-id user-id + :connected nil + :color color))) + + ;; Decrease number of not yet received synchronization items. + (decf remaining-items))) + nil) + +(defmethod rudel-obby/obby_sync_doclist_document + ((this rudel-obby-client-state-session-synching) + owner-id doc-id name suffix encoding &rest subscribed-user-ids) + "Handle obby 'sync_doclist_document' message." + (with-parsed-arguments ((doc-id number) + (owner-id number) + (suffix number) + (encoding coding-system)) + (with-slots (connection remaining-items) this + (with-slots (session) connection + ;; Retrieve the subscribed users + (let ((subscribed-users + (mapcar + (lambda (user-id) + (with-parsed-arguments ((user-id number)) + (rudel-find-user session user-id + #'= #'rudel-id))) + subscribed-user-ids))) + + ;; Make a new document with the list of subscribed users. + (rudel-add-document session (rudel-obby-document + name + :subscribed subscribed-users + :id doc-id + :owner-id owner-id + :suffix suffix)))) + + ;; Decrease number of not yet received synchronization items. + (decf remaining-items))) + nil) + +(defmethod rudel-obby/obby_sync_final + ((this rudel-obby-client-state-session-synching)) + "Handle obby 'sync_final' message." + 'idle) + +(defmethod object-print ((this rudel-obby-client-state-session-synching) + &rest strings) + "Append number of remaining items to string representation." + (with-slots (remaining-items) this + (call-next-method this (format " remaining: %d" remaining-items)))) + + +;;; Class rudel-obby-client-state-subscribing +;; + +(defclass rudel-obby-client-state-subscribing + (rudel-obby-client-connection-state + rudel-obby-document-handler) + ((document :initarg :document + :type rudel-obby-document-child + :documentation + "")) + "") + +(defmethod rudel-enter ((this rudel-obby-client-state-subscribing) + user document) + "When entering this state, send a subscription request to the server." + (with-slots ((document1 :document)) this + (setq document1 document) + + (with-slots ((doc-id :id) owner-id) document1 + (with-slots (user-id) user + (rudel-send this "obby_document" + (format "%x %x" owner-id doc-id) + "subscribe" + (format "%x" user-id))))) + nil) + +(defmethod rudel-obby/obby_document/sync_init + ((this rudel-obby-client-state-subscribing) + document num-bytes) + "Handle obby 'sync_init' message." + (with-parsed-arguments ((num-bytes number)) + (with-slots (documents) this + (if (= num-bytes 0) + 'idle + (list 'document-synching document num-bytes)))) + ) + + +;;; Class rudel-obby-client-state-document-synching +;; + +(defclass rudel-obby-client-state-document-synching + (rudel-obby-client-connection-state + rudel-obby-document-handler) + ((document :initarg :document + :type rudel-obby-document-child + :documentation + "") + (all-bytes :initarg :all-bytes + :type (integer 0) + :documentation + "") + (remaining-bytes :initarg :remaining-bytes + :type (integer 0) + :documentation + "")) + "") + +(defmethod rudel-enter ((this rudel-obby-client-state-document-synching) + document num-bytes) + "" + (with-slots ((document1 :document) all-bytes remaining-bytes) this + (setq document1 document + all-bytes num-bytes + remaining-bytes num-bytes)) + nil) + +(defmethod rudel-obby/obby_document/sync_chunk + ((this rudel-obby-client-state-document-synching) + document data user-id) + "Handle obby 'sync_chunk' message." + (with-parsed-arguments ((user-id number)) + (with-slots (connection remaining-bytes) this + (with-slots (session) connection + (let* ((user (unless (zerop user-id) + (rudel-find-user session user-id + #'= #'rudel-id))) + (operation (rudel-insert-op "bulk-insert" + :from nil + :data data))) + (rudel-remote-operation document user operation))) + + ;; After all bytes are transferred, go back to idle state. + (decf remaining-bytes (string-bytes data)) + (if (= remaining-bytes 0) + 'idle + nil))) + ) + +(defmethod object-print ((this rudel-obby-client-state-document-synching) + &rest strings) + "Append number of remaining items to string representation." + (with-slots (remaining-bytes) this + (call-next-method this (format " remaining: %d" remaining-bytes)))) + + +;;; Class rudel-obby-client-state-they-finalized +;; + +(defclass rudel-obby-client-state-they-finalized + (rudel-obby-client-connection-state) + () + "State used to indicate that the connection was closed by the peer.") + + +;;; Client connection states. +;; + +(defvar rudel-obby-client-connection-states + '((new . rudel-obby-client-state-new) + (encryption-negotiate . rudel-obby-client-state-encryption-negotiate) + (encryption-start . rudel-obby-client-state-encryption-start) + (joining . rudel-obby-client-state-joining) + (join-failed . rudel-obby-client-state-join-failed) + (idle . rudel-obby-client-state-idle) + (session-synching . rudel-obby-client-state-session-synching) + (subscribing . rudel-obby-client-state-subscribing) + (document-synching . rudel-obby-client-state-document-synching) + (they-finalized . rudel-obby-client-state-they-finalized)) + "Name symbols and classes of connection states.") + + +;;; Class rudel-obby-connection +;; + +(defclass rudel-obby-connection (rudel-obby-socket-owner + rudel-connection + rudel-state-machine) + ((info :initarg :info + :type list + :documentation + "Stores connection information for later use.") + (contexts :initarg :contexts + :type hash-table + :documentation + "Contains jupiter context objects for all +documents.")) + "Class rudel-obby-connection ") + +(defmethod initialize-instance ((this rudel-obby-connection) &rest slots) + ;; Initialize slots of THIS + (when (next-method-p) + (call-next-method)) + + ;; Create a new hash-table object to hold jupiter contexts + ;; associated to documents. + (with-slots (contexts) this + (setq contexts (make-hash-table :test #'equal))) + + ;; Register states. + (rudel-register-states this rudel-obby-client-connection-states) + ) + +(defmethod rudel-register-state ((this rudel-obby-connection) + symbol state) + "Register SYMBOL and STATE and set connection slot of STATE." + ;; Associate THIS connection to STATE. + (oset state :connection this) + + ;; Register STATE. + (when (next-method-p) + (call-next-method)) + ) + +(defmethod rudel-disconnect ((this rudel-obby-connection)) + "" + (when (next-method-p) + (call-next-method))) + +(defmethod rudel-close ((this rudel-obby-connection)) + "" + ;; Move the state machine into an error state. + (rudel-switch this 'they-finalized) + + ;; Terminate the session. + (with-slots (session) this + (rudel-end session))) + +(defmethod rudel-find-context ((this rudel-obby-connection) document) + "Return the jupiter context associated to DOCUMENT in THIS connection." + (with-slots (contexts) this + (gethash (oref document :id) contexts))) + +(defmethod rudel-add-context ((this rudel-obby-connection) document) + "Add a jupiter context for DOCUMENT to THIS connection." + (with-slots (contexts) this + (with-slots ((doc-name :object-name) (doc-id :id)) document + (puthash doc-id + (jupiter-context (format "%s" doc-name)) + contexts))) + ) + +(defmethod rudel-remove-context ((this rudel-obby-connection) document) + "Remove the jupiter context associated to DOCUMENT from THIS connection." + (with-slots (contexts) this + (remhash (oref document :id) contexts))) + +(defmethod rudel-message ((this rudel-obby-connection) message) + "Dispatch MESSAGE to the current state of THIS object. +If the state has no suitable method, generate a warning, but do +nothing else." + ;; Dispatch message to state. + (rudel-accept this message)) + +(defmethod rudel-change-color- ((this rudel-obby-connection) color) + "" + (rudel-send this "obby_user_colour" + (rudel-obby-format-color color))) + +(defmethod rudel-publish ((this rudel-obby-connection) document) + "" + ;; Create a new jupiter context for DOCUMENT. + (rudel-add-context this document) + + ;; Announce the new document to the server. + (with-slots ((name :object-name) id buffer) document + (rudel-send this "obby_document_create" + (format "%x" id) + name + "UTF-8" + (with-current-buffer buffer + (buffer-string)))) + ) + +(defmethod rudel-unpublish ((this rudel-obby-connection) document) + "Remove DOCUMENT from the obby session THIS is connected to." + ;; Request removal of DOCUMENT. + (with-slots ((doc-id :id) owner-id) document + (rudel-send this "obby_document_remove" + (format "%x %x" owner-id doc-id))) + + ;; Remove the jupiter context for DOCUMENT. + (rudel-remove-context this document) + ) + +(defmethod rudel-subscribe-to ((this rudel-obby-connection) document) + "" + ;; Create a new jupiter context for DOCUMENT. + (rudel-add-context this document) + + ;; Switch to subscribing state and wait until the state goes back to + ;; idle. + (with-slots (session) this + (with-slots (self) session + (rudel-switch this 'subscribing self document))) + + (lexical-let ((reporter (make-progress-reporter "Subscribing " 0.0 1.0))) + (flet ((display-progress (state) + (cond + ;; Syncing document content, we can provide detailed progress. + ((and (consp state) + (eq (car state) 'document-synching)) + (with-slots (all-bytes remaining-bytes) (cdr state) + (progress-reporter-force-update + reporter + (- 1.0 (/ (float remaining-bytes) (float all-bytes))) + (format "Subscribing (%s) " (car state))))) + + ;; For other states, we just spin. + ((consp state) + (progress-reporter-force-update + reporter 0.5 + (format "Subscribing (%s) " (car state)))) + + ;; Done + (t + (progress-reporter-force-update reporter 1.0 "Subscribing ") + (progress-reporter-done reporter))))) + (rudel-state-wait this '(idle) '(they-finalized) #'display-progress))) + + ;; We receive a notification of our own subscription from the + ;; server. Consequently we do not add SELF to the list of subscribed + ;; users of DOCUMENT. + ) + +(defmethod rudel-unsubscribe-from ((this rudel-obby-connection) document) + "" + ;; Delete the jupiter context for DOCUMENT. + (rudel-remove-context this document) + + ;; Announce the end of our subscription to the server. + (with-slots (session) this + (with-slots (user-id) (oref session :self) + (with-slots ((doc-id :id) owner-id) document + (rudel-send this "obby_document" + (format "%x %x" owner-id doc-id) + "unsubscribe" + (format "%x" user-id))))) + + ;; We receive a notification of the end of our own subscription from + ;; the server. Consequently we do not remove SELF from the list of + ;; subscribed users of DOCUMENT. + ) + +(defmethod rudel-local-insert ((this rudel-obby-connection) + document position data) + "" + (rudel-local-operation + this + document + (jupiter-insert "insert" :from position :data data))) + +(defmethod rudel-local-delete ((this rudel-obby-connection) + document position length) + "" + (rudel-local-operation + this + document + (jupiter-delete "delete" :from position :to (+ position length)))) + +(defmethod rudel-local-operation ((this rudel-obby-connection) + document operation) + "Handle OPERATION performed on DOCUMENT by sending a message through THIS connection." + ;; Convert character positions in OPERATION to byte positions, since + ;; the obby protocol works with byte positions, but Emacs uses + ;; character positions. + (with-slots (buffer) document + (rudel-obby-char->byte operation buffer)) + + ;; Find jupiter context for DOCUMENT. + (let ((context (rudel-find-context this document))) + + ;; Notify the server of the operation. + (with-slots (owner-id (doc-id :id)) document + (with-slots (local-revision remote-revision) context + (apply #'rudel-send + this + "obby_document" + (format "%x %x" owner-id doc-id) + "record" + (format "%x" local-revision) + (format "%x" remote-revision) + (rudel-operation->message operation)))) + + ;; Submit the operation to the jupiter context. + (jupiter-local-operation context operation)) + ) + +(defmethod rudel-remote-operation ((this rudel-obby-connection) + document user + remote-revision local-revision + operation) + "Handle OPERATION received through THIS connection performed by USER on DOCUMENT." + (let* (;; Find jupiter context for DOCUMENT. + (context (rudel-find-context this document)) + ;; And transform the operation. + (transformed (jupiter-remote-operation + context + remote-revision local-revision + operation))) + + ;; Convert byte positions in OPERATION to character positions, + ;; since the obby protocol works with byte positions, but Emacs + ;; uses character positions. + (with-slots (buffer) document + (rudel-obby-byte->char transformed buffer)) ;; TODO operation's responsibility? + + ;; Apply the transformed operation to the document. + (rudel-remote-operation document user transformed)) + ) + +(provide 'rudel-obby-client) +;;; rudel-obby-client.el ends here diff --git a/emacs.d/lisp/rudel/obby/.svn/text-base/rudel-obby-debug.el.svn-base b/emacs.d/lisp/rudel/obby/.svn/text-base/rudel-obby-debug.el.svn-base new file mode 100644 index 0000000..8f5f168 --- /dev/null +++ b/emacs.d/lisp/rudel/obby/.svn/text-base/rudel-obby-debug.el.svn-base @@ -0,0 +1,122 @@ +;;; rudel-obby-debug.el --- Debugging functions for obby backend +;; +;; Copyright (C) 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Rudel, obby, debugging +;; X-RCS: $Id:$ +;; +;; This program 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. +;; +;; This program 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 this program. If not, see . + + +;;; Commentary: +;; +;; Debugging functions for the obby backend. + + +;;; History: +;; +;; 0.1 - Initial revision. + + +;;; Code: +;; + +(require 'eieio) + +(require 'rudel-debug) + +(require 'rudel-obby-util) + + +;;; Variables +;; + +(defvar rudel-obby-debug-old-state nil + "Saves state of state machines across one function call.") + + +;;; Functions +;; + +(defmethod rudel-send :before ((this rudel-obby-socket-owner) + name &rest arguments) + "Print NAME and ARGUMENTS to debug stream." + (let ((message (apply #'rudel-obby-assemble-message + name arguments))) + + (with-slots (socket) this + (rudel-debug-stream-insert + (rudel-debug-stream-name socket) + :sent + (concat (substring message 0 (min (length message) 100)) + (when (> (length message) 100) + "...")) + (append (list name) arguments)))) + ) + +(defmethod rudel-receive :before ((this rudel-obby-socket-owner) data) + "Print DATA to debug stream." + (with-slots (socket) this + (rudel-debug-stream-insert + (rudel-debug-stream-name socket) + :received + (concat (substring data 0 (min (length data) 100)) + (when (> (length data) 100) + "...")))) + ) + +(defmethod rudel-message :before ((this rudel-obby-socket-owner) + message) + "Print DATA to debug stream." + (let ((data (apply #'rudel-obby-assemble-message message))) + + (with-slots (socket) this + (rudel-debug-stream-insert + (rudel-debug-stream-name socket) + :received-processed + (concat (substring data 0 (min (length data) 100)) + (when (> (length data) 100) + "...")) + message) + )) + ) + +(defmethod rudel-switch :before ((this rudel-obby-socket-owner) + state &rest arguments) + "Store name of STATE for later printing." + (with-slots (state) this + (setq rudel-obby-debug-old-state + (if state + (object-name-string state) + "#start"))) + ) + +(defmethod rudel-switch :after ((this rudel-obby-socket-owner) + state &rest arguments) + "Print STATE and ARGUMENTS to debug stream." + (with-slots (socket state) this + (let ((old-state rudel-obby-debug-old-state) + (new-state (object-name-string state))) + (unless (string= old-state new-state) + (rudel-debug-stream-insert + (rudel-debug-stream-name socket) + :special + (if arguments + (format "%s -> %s %s" old-state new-state arguments) + (format "%s -> %s" old-state new-state)))))) + ) + +(provide 'rudel-obby-debug) +;;; rudel-obby-debug.el ends here diff --git a/emacs.d/lisp/rudel/obby/.svn/text-base/rudel-obby-errors.el.svn-base b/emacs.d/lisp/rudel/obby/.svn/text-base/rudel-obby-errors.el.svn-base new file mode 100644 index 0000000..689d4a8 --- /dev/null +++ b/emacs.d/lisp/rudel/obby/.svn/text-base/rudel-obby-errors.el.svn-base @@ -0,0 +1,65 @@ +;;; rudel-obby-errors.el --- Error data used in the obby Rudel backend +;; +;; Copyright (C) 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Rudel, obby, errors +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; This file contains definitions of error conditions and numeric +;; error codes used in the Rudel obby backend. + + +;;; History: +;; +;; 0.1 - Initial revision. + + +;;; Code: +;; + + +;;; Obby protocol error codes +;; + +(defconst rudel-obby-error-username-invalid #x0001 + "Error code for invalid username.") + +(defconst rudel-obby-error-username-in-use #x0002 + "Error code for username already in use.") + +(defconst rudel-obby-error-color-in-use #x0100 + "Error code for color already in use.") + +(defconst rudel-obby-error-wrong-global-password #x0101 + "Error code for wrong global password.") + +(defconst rudel-obby-error-wrong-user-password #x0102 + "Error code for wrong user password.") + +(defconst rudel-obby-error-protocol-version-mismatch #x0103 + "Error code for protocol version mismatch.") + +(defconst rudel-obby-error-not-encrypted #x0104 + "Error code for not encrypted.") + +(provide 'rudel-obby-errors) +;;; rudel-obby-errors.el ends here diff --git a/emacs.d/lisp/rudel/obby/.svn/text-base/rudel-obby-server.el.svn-base b/emacs.d/lisp/rudel/obby/.svn/text-base/rudel-obby-server.el.svn-base new file mode 100644 index 0000000..5bf1158 --- /dev/null +++ b/emacs.d/lisp/rudel/obby/.svn/text-base/rudel-obby-server.el.svn-base @@ -0,0 +1,798 @@ +;;; rudel-obby-server.el --- Server component of the Rudel obby backend +;; +;; Copyright (C) 2008, 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Rudel, obby, backend, server +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; This file contains the server part of the obby backend for Rudel. +;; +;; It is implemented using one state machine (class +;; `rudel-obby-client') for each client connection. These state +;; machines have the following states: +;; +;; + new `rudel-obby-server-state-new' +;; + encryption-negotiate `rudel-obby-server-state-encryption-negotiate' +;; + before-join `rudel-obby-server-state-before-join' +;; + idle `rudel-obby-server-state-idle' + + +;;; History: +;; +;; 0.2 - State machine. +;; +;; 0.1 - Initial revision. + + +;;; Code: +;; + +(eval-when-compile + (require 'cl)) + +(require 'eieio) + +(require 'jupiter) + +(require 'rudel-state-machine) + +(require 'rudel-obby-errors) +(require 'rudel-obby-util) +(require 'rudel-obby-state) + + +;;; Class rudel-obby-server-state-new +;; + +(defclass rudel-obby-server-state-new + (rudel-obby-server-connection-state) + () + "State in which new connections start out.") + +(defmethod rudel-enter ((this rudel-obby-server-state-new)) + "Sends welcome messages to the client and starts the session +timeout timer." + ;; Send greeting sequence to the client. + (rudel-send this + "obby_welcome" + (number-to-string rudel-obby-protocol-version)) + + ;; Switch to encryption negotiation state. + 'encryption-negotiate) + + +;;; Class rudel-obby-server-state-encryption-negotiate +;; + +(defclass rudel-obby-server-state-encryption-negotiate + (rudel-obby-server-connection-state) + () + "Encryption negotiation state.") + +(defmethod rudel-enter ((this rudel-obby-server-state-encryption-negotiate)) + "Send net6 'encryption' message requesting to not enable encryption." + (rudel-send this "net6_encryption" "0") + nil) + +(defmethod rudel-obby/net6_encryption_ok + ((this rudel-obby-server-state-encryption-negotiate)) + "Handle net6 'encryption_ok' message. +Even if the client requests an encrypted connection, we cancel +the negotiation." + (rudel-send this "net6_encryption_failed") + 'before-join) + +(defmethod rudel-obby/net6_encryption_failed + ((this rudel-obby-server-state-encryption-negotiate)) + "Handle net6 'encryption_failed' message. +No action has to be taken, since the client simply proceeds after +failed encryption negotiation." + 'before-join) + + +;;; Class rudel-obby-server-state-before-join +;; + +(defclass rudel-obby-server-state-before-join + (rudel-obby-server-connection-state) + () + "Waiting for client request joining the session.") + +(defmethod rudel-obby/net6_client_login + ((this rudel-obby-server-state-before-join) username color + &optional global-password user-password) + "Handle net6 'client_login' message." + (with-parsed-arguments ((color color)) + (with-slots (server + (client-id :id) + user + encryption) (oref this :connection) + ;; Make sure USERNAME and COLOR are valid. + (let ((error (rudel-check-username-and-color + server username color))) + (if error + ;; If USERNAME or COLOR are invalid, send the error code + ;; to the client and stay in the current state. + (progn + (rudel-send this + "net6_login_failed" + (format "%x" error)) + nil) + + ;; Create a user object for this client and add it to the + ;; server. + (setq user (rudel-make-user + server + username client-id color encryption)) + (rudel-add-user server user) + + ;; Broadcast the join event to all clients (including the + ;; new one). + (with-slots ((name :object-name) color (user-id :user-id)) user + (rudel-broadcast this (list 'exclude (oref this :connection)) + "net6_client_join" + (format "%x" client-id) + name + "0" + (format "%x" user-id) + (rudel-obby-format-color color))) + + ;; Get the new client up to date: + ;; - transmit user list + ;; - connected users + ;; - disconnected users + ;; - transmit document list + (with-slots (users clients documents) server + ;; Send number of synchronization items: sum of numbers of + ;; offline users and documents. + (let ((number-of-items (+ (length users) (length documents)))) + (rudel-send this + "obby_sync_init" + (format "%x" number-of-items))) + + ;; Transmit list of connected users. + (dolist (client clients) + (with-slots ((client-id :id) user) client + (when user + (with-slots ((name :object-name) + color + (user-id :user-id)) user + (rudel-send this + "net6_client_join" + (format "%x" client-id) + name + "0" + (format "%x" user-id) + (rudel-obby-format-color color)))))) + + ;; Transmit list of disconnected users. + (let ((offline-users (remove-if #'rudel-connected users))) + (dolist (user offline-users) + (with-slots ((name :object-name) user-id color) user + (rudel-send this + "obby_sync_usertable_user" + (format "%x" user-id) + name + (rudel-obby-format-color color))))) + + ;; Transmit document list + (dolist (document documents) + (with-slots ((name :object-name) + (doc-id :id) + owner-id + suffix + subscribed) document + (apply #'rudel-send + this + "obby_sync_doclist_document" + (format "%x" owner-id) + (format "%x" doc-id) + name + (format "%x" suffix) + "UTF-8" + (mapcar + (lambda (user1) ;; TODO we could use `user' here, but there is a bug in cl + (format "%x" (rudel-id user1))) + subscribed))))) + + (rudel-send this "obby_sync_final") + 'idle)))) + ) + + +;;; Class rudel-obby-server-state-idle +;; + +(defclass rudel-obby-server-state-idle + (rudel-obby-server-connection-state) + () + "Idle state of a server connection. + +The connection enters this state when all setup work is finished, +the client has joined the session and no operation is in +progress. In this state, the connection waits for new messages +from the client that initiate operations. Simple (which means +stateless in this case) operations are performed without leaving +the idle state.") + +(defmethod rudel-obby/obby_user_colour + ((this rudel-obby-server-state-idle) color-) + "Handle obby 'user_colour' message. +This method is called when the connected user requests a change +of her color to COLOR." + (with-parsed-arguments ((color- color)) + (with-slots (user) (oref this :connection) + (with-slots (color (user-id :user-id)) user + ;; Set color slot value. + (setq color color-) + + ;; Run change hook. + (object-run-hook-with-args user 'change-hook) + + (rudel-broadcast this (list 'exclude (oref this :connection)) + "obby_user_colour" + (format "%x" user-id) + (rudel-obby-format-color color))))) + nil) + +(defmethod rudel-obby/obby_document_create + ((this rudel-obby-server-state-idle) + doc-id name encoding content) + "Handle obby 'document_create' message." + (with-parsed-arguments ((doc-id number) + (encoding coding-system)) + (with-slots (user server) (oref this :connection) + (with-slots ((user-id :user-id)) user + ;; Create a (hidden) buffer for the new document. + (let* ((buffer (get-buffer-create + (generate-new-buffer-name + (concat " *" name "*")))) + ;; Create the new document object + (document (rudel-obby-document + name + :buffer buffer + :subscribed (list user) + :id doc-id + :owner-id user-id + :suffix 1))) + + ;; Initialize the buffer's content + (with-current-buffer buffer + (insert content)) + + (with-slots (suffix) document + ;; Determine an appropriate suffix to provide an unique + ;; name for the new document. + (while (rudel-find-document server + (if (= suffix 1) + name + (format "%s<%d>" name suffix)) + #'string= #'rudel-unique-name) + (incf suffix)) + + ;; Add the document to the server's document list + (rudel-add-document server document) + + ;; Maybe notify the creating client of the changed suffix. + (unless (= suffix 1) + (rudel-send this + "obby_document" + (format "%x %x" user-id doc-id) + "rename" + (format "%x" user-id) + name + (format "%x" suffix))) + + ;; Notify other clients of the new document + (rudel-broadcast this (list 'exclude (oref this :connection)) + "obby_document_create" + (format "%x" user-id) + (format "%x" doc-id) + name + (format "%x" suffix) + (upcase (symbol-name encoding)))) + + ;; Add a jupiter context for (THIS DOCUMENT). + (rudel-add-context server (oref this :connection) document)))) + nil) + ) + +(defmethod rudel-obby/obby_document + ((this rudel-obby-server-state-idle) doc-id action &rest arguments) + "Handle obby 'document' messages." + (with-parsed-arguments ((doc-id document-id)) + ;; Locate the document based on owner id and document id + (let ((document (with-slots (server) (oref this :connection) + (rudel-find-document server doc-id + #'equal #'rudel-both-ids)))) + (rudel-obby-dispatch this action + (append (list document) arguments) + "rudel-obby/obby_document/"))) + ) + +(defmethod rudel-obby/obby_document/subscribe + ((this rudel-obby-server-state-idle) document user-id) + "Handle 'subscribe' submessage of obby 'document' message." + (with-parsed-arguments ((user-id number)) + (let ((user (with-slots (server) (oref this :connection) + (rudel-find-user server user-id + #'= #'rudel-id)))) + (with-slots (owner-id (doc-id :id) subscribed buffer) document + + ;; Track subscription, handle duplicate subscription requests. + (when (memq user subscribed) + (error "User `%s' already subscribed to document `%s'" + (object-name user) (object-name document))) + (rudel-add-user document user) + + ;; Synchronize the buffer content to the client. + (with-current-buffer buffer + ;; Send overall buffer size + (rudel-send this + "obby_document" + (format "%x %x" owner-id doc-id) + "sync_init" + (format "%x" (1- (position-bytes (point-max))))) + + ;; Send buffer chunks with author ids + (dolist (chunk (rudel-chunks document)) + (multiple-value-bind (from to author) chunk + (let ((string (buffer-substring (+ from 1) (+ to 1)))) + (rudel-send this + "obby_document" + (format "%x %x" owner-id doc-id) + "sync_chunk" + string + (format "%x" + (if author + (oref author :user-id) + 0))))))) + + ;; Notify clients of the new subscription (including our own + ;; client, who requested the subscription). + (with-slots ((user-id :user-id)) user + (rudel-broadcast this nil + "obby_document" + (format "%x %x" owner-id doc-id) + "subscribe" + (format "%x" user-id))))) + + ;; Add a jupiter context for (THIS document). + (with-slots (server) (oref this :connection) + (rudel-add-context server (oref this :connection) document)) + nil) + ) + +(defmethod rudel-obby/obby_document/unsubscribe + ((this rudel-obby-server-state-idle) document user-id) + "Handle 'unsubscribe' submessage of 'obby_document' message." + (with-parsed-arguments ((user-id number)) + (let ((user (with-slots (server) (oref this :connection) + (rudel-find-user server user-id + #'= #'rudel-id)))) + (with-slots (owner-id (doc-id :id) subscribed) document + + ;; Track subscription, handle invalid unsubscribe requests + (unless (memq user subscribed) + (error "User `%s' not subscribed to document `%s'" + (object-name user) (object-name document))) + (rudel-remove-user document user) + + ;; Notify clients of the canceled subscription (including our + ;; own client, who requested being unsubscribed). + (with-slots ((user-id :user-id)) user + (rudel-broadcast this nil + "obby_document" + (format "%x %x" owner-id doc-id) + "unsubscribe" + (format "%x" user-id)))) + + ;; Remove jupiter context for (THIS DOCUMENT). + (with-slots (server) (oref this :connection) + (rudel-remove-context server (oref this :connection) document))) + nil) + ) + +(defmethod rudel-obby/obby_document/record + ((this rudel-obby-server-state-idle) + document local-revision remote-revision action &rest arguments) + "Handle 'record' submessages of 'obby_document' message." + (with-parsed-arguments ((local-revision number) + (remote-revision number)) + ;; Dispatch to specialized operation handlers. + (rudel-obby-dispatch + this action + (append (list document local-revision remote-revision) + arguments) + "rudel-obby/obby_document/record/")) + ) + +(defmethod rudel-obby/obby_document/record/ins + ((this rudel-obby-server-state-idle) + document local-revision remote-revision position data) + "Handle 'ins' submessage of 'record' submessages of 'obby_document' message." + (with-parsed-arguments ((position number)) + ;; Construct the operation object and process it. + (rudel-remote-operation + (oref this :connection) document + remote-revision local-revision + (jupiter-insert + (format "insert-%d-%d" + remote-revision local-revision) + :from position + :data data)) + nil) + ) + +(defmethod rudel-obby/obby_document/record/del + ((this rudel-obby-server-state-idle) + document local-revision remote-revision position length) + "Handle 'del' submessage of 'record' submessages of 'obby_document' message." + (with-parsed-arguments ((position number) + (length number)) + ;; Construct the operation object and process it. + (rudel-remote-operation + (oref this :connection) document + remote-revision local-revision + (jupiter-delete + (format "delete-%d-%d" + remote-revision local-revision) + :from position + :to (+ position length))) + nil) + ) + + +;;; Client connection states. +;; + +(defvar rudel-obby-server-connection-states + '((new . rudel-obby-server-state-new) + (encryption-negotiate . rudel-obby-server-state-encryption-negotiate) + (before-join . rudel-obby-server-state-before-join) + (idle . rudel-obby-server-state-idle)) + "Name symbols and classes of connection states.") + + +;;; Class rudel-obby-client +;; + +(defclass rudel-obby-client (rudel-obby-socket-owner + rudel-state-machine) + ((server :initarg :server + :type rudel-obby-server + :documentation + "") + (id :initarg :id + :type integer + :accessor rudel-id + :documentation + "") + (user :initarg :user + :type (or rudel-obby-user null) + :initform nil + :documentation + "") + (encryption :initarg :encryption + :type boolean + :documentation + "")) + "Each object of this class represents one client, that is +connected to the server. This object handles all direct +communication with the client, while broadcast messages are +handled by the server.") + +(defmethod initialize-instance ((this rudel-obby-client) &rest slots) + "Initialize slots of THIS and register state machine states." + ;; Initialize slots of THIS + (when (next-method-p) + (call-next-method)) + + ;; Register states. + (rudel-register-states this rudel-obby-server-connection-states) + ) + +(defmethod rudel-register-state ((this rudel-obby-client) symbol state) + "Register SYMBOL and STATE and set connection slot of STATE." + ;; Associate THIS connection to STATE. + (oset state :connection this) + + ;; Register STATE. + (call-next-method)) + +(defmethod rudel-end ((this rudel-obby-client)) + "" + (rudel-disconnect this)) + +(defmethod rudel-close ((this rudel-obby-client)) + "" + (with-slots (server) this + (rudel-remove-client server this))) + +(defmethod rudel-message ((this rudel-obby-client) message) + "Dispatch MESSAGE to the active state of THIS state machine." + ;; Dispatch message to state + (rudel-accept this message)) + +(defmethod rudel-broadcast ((this rudel-obby-client) + receivers name &rest arguments) + "Broadcast message NAME with arguments ARGUMENTS to RECEIVERS." + (with-slots (server) this + (apply #'rudel-broadcast server receivers name arguments))) + +(defmethod rudel-remote-operation ((this rudel-obby-client) + document + local-revision remote-revision + operation) + "Execute and relay OPERATION on DOCUMENT." + (with-slots (server user) this + ;; Transform OPERATION and find clients that need to receive + ;; notifications. + (let* ((context (rudel-find-context server this document)) + (transformed (jupiter-remote-operation + context + local-revision remote-revision + operation)) + (receivers (rudel-subscribed-clients-not-self + this document))) + + ;; Relay change notification to other clients. We use + ;; TRANSFORMED before the byte -> char conversion which is what + ;; the receivers expect. + (with-slots (user-id) user + (with-slots (owner-id (doc-id :id)) document + ;; Construct and send messages to all receivers individually + ;; since the contents of the messages depends on the state + ;; of the jupiter context associated the respective + ;; receiver. + (dolist (receiver receivers) + + ;; Find the jupiter context for RECEIVER and use its + ;; revision information. + (let ((context (rudel-find-context server receiver document))) + ;; Construct and send one message. + (with-slots (local-revision remote-revision) context + (apply #'rudel-send + receiver + "obby_document" + (format "%x %x" owner-id doc-id) + "record" + (format "%x" user-id) + (format "%x" local-revision) + (format "%x" remote-revision) + (rudel-operation->message transformed))) + + ;; Submit the operation to the jupiter context. + (jupiter-local-operation context transformed))))) + + ;; Incorporate change into DOCUMENT (the server-side + ;; document). We have to convert bytes -> chars before we can do + ;; this. + (with-slots (buffer) document + (rudel-obby-byte->char transformed buffer)) + + (rudel-remote-operation document user transformed))) + ) + +(defmethod rudel-subscribed-clients-not-self ((this rudel-obby-client) + document) + "Return a list of clients subscribed to DOCUMENT excluding THIS." + (with-slots (clients) (oref this :server) + (with-slots (subscribed) document + (remove-if + (lambda (client) + (with-slots (user) client + (or (eq client this) + (not (memq user subscribed))))) + clients))) + ) + + +;;; Class rudel-obby-server +;; + +(defclass rudel-obby-server (rudel-server-session + rudel-socket-owner) + ((clients :initarg :clients + :type list + :initform nil + :documentation + "") + (next-client-id :initarg :next-client-id + :type integer + :initform 1 + :documentation + "") + (next-user-id :initarg :next-user-id + :type integer + :initform 1 + :documentation + "") + (contexts :initarg :contexts + :type hash-table + :documentation + "")) + "Class rudel-obby-server ") + +(defmethod initialize-instance ((this rudel-obby-server) &rest slots) + "" + (when (next-method-p) + (call-next-method)) + + (with-slots (contexts) this + (setq contexts (make-hash-table :test 'equal)))) + +(defmethod rudel-end ((this rudel-obby-server)) + "" + (rudel-disconnect this)) + +(defmethod rudel-broadcast ((this rudel-obby-server) + receivers name &rest arguments) + "Send a message of type NAME with arguments ARGUMENTS to RECEIVERS. + +RECEIVERS can be a object derived from rudel-obby-client, a list +of such objects or a list with car 'exclude and cdr a list of +such objects derived from rudel-obby-client." + ;; Construct list of receivers. + (let ((receiver-list + (cond + ;; If RECEIVERS is nil, the message should be broadcast to + ;; all clients. + ((null receivers) (oref this :clients)) + ;; If RECEIVERS is a (non-empty) list of rudel-obby-client + ;; (or derived) objects, treat it as a list of receivers. + ((and (listp receivers) + (rudel-obby-client-child-p (car receivers))) + receivers) + ;; If RECEIVERS is a (non-empty) list with cdr equal to + ;; 'exclude treat it as a list of receivers to exclude. + ((and (listp receivers) + (eq (car receivers) 'exclude)) + (with-slots (clients) this + (set-difference clients (cdr receivers) + :key #'rudel-id))) + ;; If RECEIVERS is a single rudel-obby-client (or derived) + ;; object, send the message to that client. + ((rudel-obby-client-child-p receivers) + (list receivers)) + ;; + (t (signal 'wrong-type-argument (type-of receivers)))))) + + ;; Send message to receivers. + (dolist (receiver receiver-list) + (apply #'rudel-send receiver name arguments))) + ) + +(defmethod rudel-make-user ((this rudel-obby-server) + name client-id color encryption) + "" + (with-slots (next-user-id) this + (let ((user (rudel-obby-user name + :color color + :client-id client-id + :user-id next-user-id + :connected t + :encryption encryption))) + (incf next-user-id) + user)) + ) + +(defmethod rudel-check-username-and-color ((this rudel-obby-server) + username color) + "Check whether USERNAME and COLOR are valid. +USERNAME must not be empty and must not be used by another +user. COLOR has to be sufficiently different from used colors." + (cond + ;; The empty user name is not allowed + ((string= username "") + rudel-obby-error-username-invalid) + + ;; Make sure the user name is not already in use. + ((rudel-find-user this username + #'string= #'object-name-string) + rudel-obby-error-username-in-use) + + ;; Make sure the color is sufficiently dissimilar to all used + ;; colors. + ((rudel-find-user this color + (lambda (left right) + (< (color-distance left right) 20000)) ;; TODO constant + #'rudel-color) + rudel-obby-error-color-in-use)) + ) + +(defmethod rudel-add-client ((this rudel-obby-server) + client-socket) + "" + (with-slots (next-client-id clients) this + (let ((client (rudel-obby-client (process-name client-socket) + :server this + :socket client-socket + :id next-client-id + :encryption nil))) + (push client clients)) + (incf next-client-id)) + ) + +(defmethod rudel-remove-client ((this rudel-obby-server) + client) + "" + (with-slots ((client-id :id) user) client + ;; Broadcast the part event to all remaining clients. + (rudel-broadcast this (list 'exclude client) + "net6_client_part" + (format "%x" client-id)) + + ;; If the client has an associated user object, set the status of + ;; the user object to offline. + (when user + ;; Set slot value. + (with-slots (connected) user + (setq connected nil)) + + ;; Run change hook. + (object-run-hook-with-args user 'change-hook))) + + (object-remove-from-list this :clients client) + ) + +(defmethod rudel-find-context ((this rudel-obby-server) client document) + "Return the jupiter context associated to (CLIENT DOCUMENT) in THIS." + (with-slots (contexts) this + (gethash (rudel-obby-context-key client document) contexts))) + +(defmethod rudel-add-context ((this rudel-obby-server) client document) + "Add a jupiter context for (CLIENT DOCUMENT) to THIS." + (with-slots (contexts) this + (with-slots ((client-id :id)) client + (with-slots ((doc-name :object-name)) document + (puthash + (rudel-obby-context-key client document) + (jupiter-context (format "%d-%s" client-id doc-name)) + contexts)))) + ) + +(defmethod rudel-remove-context ((this rudel-obby-server) client document) + "Remove the jupiter context associated to (CLIENT DOCUMENT) from THIS." + (with-slots (contexts) this + (remhash + (rudel-obby-context-key client document) + contexts))) + +(defun rudel-obby-context-key (client document) + "Generate hash key based on CLIENT and DOCUMENT." + (with-slots ((client-id :id)) client + (with-slots ((doc-id :id)) document + (list client-id doc-id)))) + +(defmethod object-print ((this rudel-obby-server) &rest strings) + "Print THIS with number of clients." + (with-slots (clients) this + (apply #'call-next-method + this + (format " clients: %d" + (length clients)) + strings)) + ) + +(provide 'rudel-obby-server) +;;; rudel-obby-server.el ends here diff --git a/emacs.d/lisp/rudel/obby/.svn/text-base/rudel-obby-state.el.svn-base b/emacs.d/lisp/rudel/obby/.svn/text-base/rudel-obby-state.el.svn-base new file mode 100644 index 0000000..a190967 --- /dev/null +++ b/emacs.d/lisp/rudel/obby/.svn/text-base/rudel-obby-state.el.svn-base @@ -0,0 +1,169 @@ +;;; rudel-obby-state.el --- Base class for states used in the obby backend +;; +;; Copyright (C) 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Rudel, obby, state machine +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; This file contains a base class for finite state machine states +;; used in the obby backend. + + +;;; History: +;; +;; 0.1 - Initial revision + + +;;; Code: +;; + +(require 'eieio) + +(require 'rudel-util) +(require 'rudel-state-machine) + +(require 'rudel-obby-util) + + +;;; Class rudel-obby-state +;; + +(defclass rudel-obby-state (rudel-state) + ((connection :initarg :connection + :type rudel-obby-socket-owner + :documentation + "Connection object that uses the state.")) + "Base class for state classes used in the obby backend." + :abstract t) + +(defmethod rudel-enter ((this rudel-obby-state)) + "Default behavior is doing nothing when entering a state." + nil) + +(defmethod rudel-leave ((this rudel-obby-state)) + "Default behavior is doing nothing when leaving a state.") + +(defmethod rudel-accept ((this rudel-obby-state) message) + "Dispatch to appropriate handler based on MESSAGE. +Display a warning if no such handler is found." + ;; Try to dispatch to the correct message handler. If there is none, + ;; warn. + (let ((name (car message)) + (arguments (cdr message))) + (condition-case error + ;; Try to dispatch + (rudel-dispatch this "rudel-obby/" name arguments) + ;; Warn if we failed to locate or execute the method. Return nil + ;; in this case, so we remain in the current state. + (rudel-dispatch-error + (progn + (display-warning + '(rudel obby) + (format "%s: no method (%s: %s): `%s/%s'; arguments: %s" + (object-print this) (car error) (cdr error) + "rudel-obby" name arguments) + :debug) + nil)))) + ) + +(defmethod rudel-send ((this rudel-obby-state) &rest args) + "Send ARGS through the connection associated with THIS." + (with-slots (connection) this + (apply #'rudel-send connection args))) + + +;;; Class rudel-obby-client-connection-state +;; + +(defclass rudel-obby-client-connection-state (rudel-obby-state) + () + "Base class for state classes used by obby client connections." + :abstract t) + +(defmethod rudel-obby/net6_ping ((this rudel-obby-client-connection-state)) + "Handle net6 'ping' message." + (rudel-send this "net6_pong") + nil) + + +;;; Class rudel-obby-server-connection-state +;; + +(defclass rudel-obby-server-connection-state (rudel-obby-state) + () + "Base class for server connection states." + :abstract t) + +(defmethod rudel-broadcast ((this rudel-obby-server-connection-state) + receivers name &rest arguments) + "Broadcast message NAME with arguments ARGUMENTS to RECEIVERS." + (with-slots (connection) this + (apply #'rudel-broadcast connection receivers name arguments))) + + +;;; Class rudel-obby-document-handler +;; + +(defclass rudel-obby-document-handler () + () + "Mixin class that provides ability to process submessages of + obby 'document' messages.") + +(defmethod rudel-obby/obby_document + ((this rudel-obby-document-handler) doc-id action &rest arguments) + "Handle obby 'document' message family." + ;; Try to dispatch to the correct message handler. If there is none, + ;; warn. + (with-parsed-arguments ((doc-id document-id)) + ;; Locate the document based on owner id and document id. + (let ((document (with-slots (connection) this + (with-slots (session) connection + (rudel-find-document session doc-id + #'equal #'rudel-both-ids))))) + (if document + (condition-case error + ;; Try to dispatch + (rudel-dispatch this "rudel-obby/obby_document/" action + (cons document arguments)) + ;; Warn if we failed to locate or execute the + ;; method. Return nil in this case, so we remain in the + ;; current state. + (rudel-dispatch-error + (progn + (display-warning + '(rudel obby) + (format "%s: no method (%s: %s): `%s:%s'; arguments: %s" + (object-print this) (car error) (cdr error) + "rudel-obby/obby_document/" action arguments) + :debug) + nil))) + ;; If we did not find the document, warn. + (progn + (display-warning + '(rudel obby) + (format "Document not found: %s" doc-id) + :debug) + nil)))) + ) + +(provide 'rudel-obby-state) +;;; rudel-obby-state.el ends here diff --git a/emacs.d/lisp/rudel/obby/.svn/text-base/rudel-obby-util.el.svn-base b/emacs.d/lisp/rudel/obby/.svn/text-base/rudel-obby-util.el.svn-base new file mode 100644 index 0000000..faefe70 --- /dev/null +++ b/emacs.d/lisp/rudel/obby/.svn/text-base/rudel-obby-util.el.svn-base @@ -0,0 +1,314 @@ +;;; rudel-obby-util.el --- Miscellaneous functions for the Rudel obby backend +;; +;; Copyright (C) 2008, 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Rudel, obby, backend, miscellaneous +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; + + +;;; History: +;; +;; 0.1 - Initial revision. + + +;;; Code: +;; + +(eval-when-compile + (require 'cl)) + +(require 'eieio) + +(require 'rudel) +(require 'rudel-util) + +(require 'jupiter) + + +;;; Class rudel-obby-socket-owner +;; + +(defclass rudel-obby-socket-owner (rudel-socket-owner) + ((buffer :initarg :buffer + :type (or null string) + :initform nil + :documentation + "Stores message fragments until complete messages can +be assembled.")) + "This class adds functions for sending and receiving obby +messages to the base class rudel-socket-owner.") + +(defmethod rudel-send ((this rudel-obby-socket-owner) + name &rest arguments) + "Send obby message NAME with arguments ARGUMENTS through the socket associated to THIS." + (with-slots (socket) this + (rudel-obby-send socket name arguments))) + +(defmethod rudel-receive ((this rudel-obby-socket-owner) data) + "Reassemble lines in DATA received on the socket associated with THIS and call message handler." + ;; Assemble fragmented lines. + (with-slots (buffer) this + (rudel-assemble-line-fragments data buffer)) + + ;; Process all available lines. + (rudel-loop-lines data line + ;; `rudel-message' has to dispatch message to an appropriate + ;; handler. + (let ((message (rudel-obby-parse-message line))) + (rudel-message this message))) + ) + +(defgeneric rudel-message ((this rudel-obby-socket-owner) message) + "Called when a message arrives. +Should be implemented in derived classes.") + + +;;; Message serialization +;; + +(defgeneric rudel-operation->message ((this jupiter-operation)) + "Generate a list obby message components from THIS operation.") + +(defmethod rudel-operation->message ((this jupiter-insert)) + "Serialize THIS insert operation." + (with-slots (from data) this + (list "ins" (format "%x" from) data))) + +(defmethod rudel-operation->message ((this jupiter-delete)) + "Serialize THIS delete operation." + (with-slots (from length) this + (list "del" (format "%x" from) (format "%x" length)))) + +(defmethod rudel-operation->message ((this jupiter-compound)) + "Serialize THIS compound operation." + (with-slots (children) this + (apply #'append + (list "split" ) + (mapcar #'rudel-operation->message children)))) + +(defmethod rudel-operation->message ((this jupiter-nop)) + "Serialize THIS nop operation." + (list "nop")) + +(defun rudel-message->operation (message local-revision remote-revision) + "Construct an operation object from MESSAGE and LOCAL-REVISION and REMOTE-REVISION. +LOCAL-REVISION and REMOTE-REVISION are only used in the +construction of the name of the new operation. " + (let ((type (car message))) + (cond + + ;; Insert operation + ((string= type "ins") + (let ((position-numeric (string-to-number (nth 1 message) 16)) + (data (nth 2 message))) + (jupiter-insert + (format "insert-%d-%d" + remote-revision local-revision) + :from position-numeric + :data data))) + + ;; Delete operation + ((string= type "del") + (let ((position-numeric (string-to-number (nth 1 message) 16)) + (length-numeric (string-to-number (nth 2 message) 16))) + (jupiter-delete + (format "delete-%d-%d" + remote-revision local-revision) + :from position-numeric + :to (+ position-numeric length-numeric)))) + + ;; Compound operation + ((string= type "split") + (let* ((rest (cdr message)) + (offset (position-if + (lambda (item) + (member* item '("ins" "del" "nop") + :test #'string=)) + rest + :start 1)) + (first (subseq rest 0 offset)) + (second (subseq rest offset))) + (jupiter-compound + (format "compound-%d-%d" + remote-revision local-revision) + :children + (list (rudel-message->operation + first local-revision remote-revision) + (rudel-message->operation + second local-revision remote-revision))))) + + ;; No operation + ((string= type "nop") + (jupiter-nop + (format "nop-%d-%d" + remote-revision local-revision))) + + ;; Unknown operation type + (t (error "Unknown document record type: `%s'" type)))) + ) + + +;;; Character <-> byte position conversion +;; + +(defgeneric rudel-obby-char->byte ((this jupiter-operation) buffer) + "Convert character positions and lengths in THIS to bytes.") + +(defmethod rudel-obby-char->byte ((this jupiter-insert) buffer) + "Convert character positions and lengths in THIS insert to bytes." + (with-slots (from) this + (with-current-buffer buffer + (setq from (- (position-bytes (+ from 1)) 1))))) + +(defmethod rudel-obby-char->byte ((this jupiter-delete) buffer) + "Convert character positions and lengths in THIS delete to bytes." + (with-slots (from to length) this + (let ((old-from (+ from 1)) + (old-to (+ to 1))) + (with-current-buffer buffer + (destructuring-bind (change-from change-to string) + rudel-buffer-change-workaround-data + (setq from (- (position-bytes old-from) 1) + length (string-bytes + (substring string + (- old-from change-from) + (- old-to change-from)))))))) + ) + +(defmethod rudel-obby-char->byte ((this jupiter-compound) buffer) + "Convert character positions and lengths in THIS compound to bytes.." + (with-slots (children) this + (mapc + (lambda (child) + (rudel-obby-char->byte child buffer)) + children)) + ) + +(defmethod rudel-obby-char->byte ((this jupiter-nop) buffer) + "Nothing to convert if THIS is a nop.") + +(defgeneric rudel-obby-byte->char ((this jupiter-operation) buffer) + "Convert byte positions and lengths in THIS to character positions.") + +(defmethod rudel-obby-byte->char ((this jupiter-insert) buffer) + "Convert byte positions and lengths in THIS insert to character positions." + (with-slots (from) this + (with-current-buffer buffer + (setq from (- (byte-to-position (+ from 1)) 1))))) + +(defmethod rudel-obby-byte->char ((this jupiter-delete) buffer) + "Convert byte positions and lengths in THIS delete to character positions." + (with-slots (from to length) this + (let ((old-from from) + (old-length length)) + (with-current-buffer buffer + (setq from (- (byte-to-position (+ old-from 1)) 1) + to (- (byte-to-position (+ old-from old-length 1)) 1))))) + ) + +(defmethod rudel-obby-byte->char ((this jupiter-compound) buffer) + "Convert byte positions and lengths in THIS compound to character positions." + (with-slots (children) this + (mapc + (lambda (child) + (rudel-obby-byte->char child buffer)) + children)) + ) + +(defmethod rudel-obby-byte->char ((this jupiter-nop) buffer) + "Nothing to convert if THIS is a nop.") + + +;;; Miscellaneous functions +;; + +(defun rudel-obby-dispatch (object name arguments &optional prefix) + "Call method starting with PREFIX and ending in NAME of OBJECT with ARGUMENTS. +When PREFIX is not specified, \"rudel-obby/\" is used." + ;; Default prefix. + (unless prefix + (setq prefix "rudel-obby/")) + + ;; Construct a matching symbol. + (let ((method (intern-soft (concat prefix name)))) + ;; If we found a suitable method, run it; Otherwise warn and do + ;; nothing. + (unless (and method + (condition-case error + ;; Try to call METHOD. If successful, always + ;; return t. + (progn + (apply method object arguments) + t) + ;; Warn only when the condition is + ;; 'no-method-definition' and refers to METHOD, + ;; otherwise continue unwinding. + (no-method-definition + (if (eq method (cadr error)) + nil + (signal (car error) (cdr error)))))) + (display-warning + '(rudel obby) + (format "%s: in context `%s': no method: `%s'; arguments: %s" + (object-name-string object) prefix name arguments) + :debug))) + ) + +(defmacro with-parsed-arguments (specs &rest forms) + "Execute FORMS with variable bindings according to SPECS. +SPECS is structured as follows: +SPECS ::= (BINDING*) +BINDING ::= (VAR TYPE) +VAR is a symbol and TYPE is one of number, color, document-id and +coding-system." + (declare (indent 1) + (debug (listp &rest form))) + (let ((bindings + (mapcar + (lambda (spec) + (destructuring-bind (var type) spec + (list var + (case type + ;; Number + (number + `(string-to-number ,var 16)) + ;; Color + (color + `(rudel-obby-parse-color ,var)) + ;; Document Id + (document-id + `(mapcar + (lambda (string) + (string-to-number string 16)) + (split-string ,var " " t))) + ;; Coding System + (coding-system + `(rudel-get-coding-system (downcase ,var))))))) + specs))) + `(let (,@bindings) + ,@forms)) + ) + +(provide 'rudel-obby-util) +;;; rudel-obby-util.el ends here diff --git a/emacs.d/lisp/rudel/obby/.svn/text-base/rudel-obby.el.svn-base b/emacs.d/lisp/rudel/obby/.svn/text-base/rudel-obby.el.svn-base new file mode 100644 index 0000000..a09c0d3 --- /dev/null +++ b/emacs.d/lisp/rudel/obby/.svn/text-base/rudel-obby.el.svn-base @@ -0,0 +1,488 @@ +;;; rudel-obby.el --- An obby backend for Rudel +;; +;; Copyright (C) 2008, 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Rudel, obby, backend, implementation +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; This file contains a Rudel protocol backend, which implements the +;; obby protocol (used by the Gobby collaborative editor until version +;; 0.5). + + +;;; History: +;; +;; 0.2 - Refactored client and server to employ state machine. +;; +;; 0.1 - Initial revision. + + +;;; Code: +;; + +(eval-when-compile + (require 'cl)) + +(require 'eieio) + +(require 'rudel) +(require 'rudel-backend) +(require 'rudel-protocol) +(require 'rudel-util) +(require 'rudel-icons) +(require 'rudel-compat) ;; for `read-color' replacement + + +;;; Constants +;; + +(defconst rudel-obby-version '(0 2) + "Version of the obby backend for Rudel.") + +(defconst rudel-obby-protocol-version 8 + "Version of the obby protocol this library understands.") + +(defvar rudel-obby-long-message-threshold 32768 + "Threshold for message size, above which messages are sent in +multiple chunks.") + +(defvar rudel-obby-long-message-chunk-size 16384 + "Chunk size used, when chunking long messages.") + + +;;; Class rudel-obby-backend +;; + +;;;###autoload +(defclass rudel-obby-backend (rudel-protocol-backend) + ((capabilities :initform '(join host + change-color + track-subscriptions))) + "Main class of the Rudel obby backend. Creates obby client +connections and creates obby servers.") + +(defmethod initialize-instance ((this rudel-obby-backend) &rest slots) + "Initialize slots of THIS with SLOTS." + (when (next-method-p) + (call-next-method)) + + (oset this :version rudel-obby-version)) + +(defmethod rudel-ask-connect-info ((this rudel-obby-backend) &optional info) + "Ask user for the information required to connect to an obby server." + ;; Read server host and port. + (let ((host (or (and info (plist-get info :host)) + (read-string "Server: "))) + (port (or (and info (plist-get info :port)) + (read-number "Port: " 6522))) + ;; Read desired username and color + (username (or (and info (plist-get info :username)) + (read-string "Username: " user-login-name))) + (color (or (and info (plist-get info :color)) + (read-color "Color: " t))) + (encryption (if (and info (member :encryption info)) + (plist-get info :encryption) + (y-or-n-p "Use encryption? "))) + (global-password (if (and info (member :global-password info)) + (plist-get info :global-password) + (read-string "Global password: " ""))) + (user-password (if (and info (member :user-password info)) + (plist-get info :user-password) + (read-string "User password: " "")))) + (append (list :host host + :port port + :username username + :color color + :encryption encryption + :global-password (unless (string= global-password "") + global-password) + :user-password (unless (string= user-password "") + user-password)) + info)) + ) + +(defmethod rudel-connect ((this rudel-obby-backend) info) + "Connect to an obby server using the information INFO. +Return the connection object." + ;; Before we start, load the client functionality. + (require 'rudel-obby-client) + + ;; Create the network process + (let* ((session (plist-get info :session)) + (host (plist-get info :host)) + (port (plist-get info :port)) + (encryption (plist-get info :encryption)) + ;; Create the network process + (socket (funcall + (if encryption + (progn + (require 'rudel-tls) + #'rudel-tls-make-process) + #'make-network-process) + :name host + :host host + :service port + ;; Install connection filter to redirect data to + ;; the connection object + :filter #'rudel-filter-dispatch + ;; Install connection sentinel to redirect state + ;; changes to the connection object + :sentinel #'rudel-sentinel-dispatch + ;; Do not start receiving immediately since the + ;; filter function is not yet setup properly. + :stop t)) + (connection (rudel-obby-connection + host + :session session + :socket socket + :info info))) + + ;; Now start receiving and wait until the basic session setup is + ;; complete. + (continue-process socket) + + ;; Wait for the connection to reach one of the states idle, + ;; join-failed and they-finalized. + (condition-case error + (lexical-let ((reporter (make-progress-reporter "Joining "))) + (flet ((display-progress (state) + (cond + ;; For all states, just spin. + ((consp state) + (progress-reporter-force-update + reporter nil (format "Joining (%s)" (car state)))) + + ;; Done + (t + (progress-reporter-force-update reporter nil "Joining ") + (progress-reporter-done reporter))))) + + (rudel-state-wait connection + '(idle) '(join-failed they-finalized) + #'display-progress))) + + (rudel-entered-error-state + (destructuring-bind (symbol . state) (cdr error) + (if (eq (rudel-find-state connection 'join-failed) state) + (with-slots (error-symbol error-data) state + (signal 'rudel-join-error + (append (list error-symbol) error-data))) + (signal 'rudel-join-error nil))))) + + ;; The connection is now usable; return it. + connection) + ) + +(defmethod rudel-ask-host-info ((this rudel-obby-backend)) + "Ask user for information required to host an obby session." + (let ((port (read-number "Port: " 6522))) + (list :port port))) + +(defmethod rudel-host ((this rudel-obby-backend) info) + "Host an obby session using the information INFO. +Return the created server." + ;; Before we start, we load the server functionality. + (require 'rudel-obby-server) + + ;; Create the network process. + (let* ((port (plist-get info :port)) + ;; Make a server socket + (socket (make-network-process + :name "obby-server" + :host "0.0.0.0" + :service port + :server t + :filter #'rudel-filter-dispatch + :sentinel #'rudel-sentinel-dispatch + ;; + :log + (lambda (server-process client-process message) + (let ((server (rudel-process-object server-process))) + (rudel-add-client server client-process))))) + ;; Construct server object. + (server (rudel-obby-server "obby-server" + :backend this + :socket socket))) + + ;; Return the constructed server. + server) + ) + +(defmethod rudel-make-document ((this rudel-obby-backend) + name session) + "Make a new document in SESSION named NAME. +Return the new document." + ;; Find an unused document id and create a document with that id. + (let ((id (rudel-available-document-id this session))) + (with-slots (user-id) (oref session :self) + (rudel-obby-document name + :session session + :id id + :owner-id user-id + :suffix 1))) + ) + +(defmethod rudel-available-document-id ((this rudel-obby-backend) + session) + "Return a document id, which is not in use in SESSION." + ;; Look through some candidates until an unused id is hit. + (let* ((used-ids (with-slots (documents) session + (mapcar 'rudel-id documents))) + (test-ids (number-sequence 0 (length used-ids)))) + (car (sort (set-difference test-ids used-ids) '<))) + ) + + +;;; Class rudel-obby-user +;; + +(defclass rudel-obby-user (rudel-user) + ((client-id :initarg :client-id + :type (or null integer) ;; We allow nil instead of making + :accessor rudel-client-id ;; the slot unbound, to be able to + :initform nil ;; search with test `rudel-client-id + :documentation ;; without headaches + "Id of the client connection, which the user used to log in. +The value is an integer, if the user is connected, and nil +otherwise.") + (user-id :initarg :user-id + :type integer + :accessor rudel-id + :documentation + "") + (connected :initarg :connected + :type boolean + :accessor rudel-connected + :documentation + "") + (encryption :initarg :encryption ;; TODO maybe we should use unbound when the user is not connected + :type boolean + :documentation + "")) + "Class rudel-obby-user ") + +(defmethod eieio-speedbar-description ((this rudel-obby-user)) + "Provide a speedbar description for THIS." + (let ((connected (oref this :connected)) + (encryption (if (slot-boundp this :encryption) + (oref this :encryption) + nil))) + (format "User %s (%s, %s)" (object-name-string this) + (if connected "Online" "Offline") + (if encryption "Encryption" "Plain"))) + ) + +(defmethod eieio-speedbar-object-buttonname ((this rudel-obby-user)) + "Return a string to use as a speedbar button for THIS." + (rudel-display-string this)) + +(defmethod rudel-display-string ((this rudel-obby-user) + &optional use-images align) + "Return a textual representation of THIS for user interface stuff." + (with-slots (connected color) this + (let ((encryption (and (slot-boundp this :encryption) + (oref this :encryption))) + (name-string (call-next-method))) + (concat + ;; Name bit + (cond + ((numberp align) (format (format "%-%ds" align) name-string)) + ((eq align t) (format "%-12s" name-string)) + (t name-string)) + + ;; Connection status bit + (apply + #'propertize + (if connected "c" "-") + 'help-echo (format (if connected + "%s is connected" + "%s is not connected") + name-string) + 'face (list :background color) + (when use-images + (list 'display (if connected + rudel-icon-connected + rudel-icon-disconnected)))) + + ;; Encryption bit + (apply + #'propertize + (if encryption "e" "-") + 'help-echo (format (if encryption + "%s's connection is encrypted" + "%s's connection is not encrypted") + name-string) + 'face (list :background color) + (when use-images + (list 'display (if encryption + rudel-icon-encrypted + rudel-icon-plaintext))))))) + ) + + +;;; Class rudel-obby-document +;; + +(defclass rudel-obby-document (rudel-document) + ((id :initarg :id + :type integer + :accessor rudel-id + :documentation + "The id of this document. +The id has to be unique only with respect to the other documents +owned by the owner.") + (owner-id :initarg :owner-id + :type integer + :documentation + "") + (suffix :initarg :suffix + :type integer + :documentation + "A counter used to distinguish identically named +documents.")) + "Objects of the class rudel-obby-document represent shared +documents in obby sessions.") + +(defmethod rudel-both-ids ((this rudel-obby-document)) + "Return a list consisting of document and owner id of THIS document." + (with-slots ((doc-id :id) owner-id) this + (list owner-id doc-id))) + +(defmethod rudel-unique-name ((this rudel-obby-document)) + "Generate a unique name for THIS based on the name and the suffix." + (with-slots (suffix) this + (concat (when (next-method-p) + (call-next-method)) + (when (> suffix 1) + (format "<%d>" suffix)))) + ) + +(defmethod eieio-speedbar-description ((this rudel-obby-document)) + "Construct a description for from the name of document object THIS." + (format "Document %s" (object-name-string this))) + +(defmethod eieio-speedbar-object-buttonname ((this rudel-obby-document)) + "Return a string to use as a speedbar button for OBJECT." + (with-slots (subscribed) this + (format "%-12s %s" (object-name-string this) + (if subscribed "s" "-"))) + ) + + +;;; Obby message functions +;; + +(defun rudel-obby-replace-in-string (string replacements) + "Replace elements of REPLACEMENTS in STRING. +REPLACEMENTS is a list of conses whose car is the pattern and +whose cdr is the replacement for the pattern." + (let ((result string)) + (dolist (replacement replacements) + (let ((from (car replacement)) + (to (cdr replacement))) + (setq result (replace-regexp-in-string + from to result nil t)))) + result) + ) + +(defun rudel-obby-escape-string (string) + "Replace meta characters in STRING with their escape sequences." + (rudel-obby-replace-in-string + string + '(("\\\\" . "\\b") ("\n" . "\\n") (":" . "\\d"))) + ) + +(defun rudel-obby-unescape-string (string) + "Replace escaped versions of obby meta characters in STRING with the actual meta characters." + (rudel-obby-replace-in-string + string + '(("\\\\n" . "\n") ("\\\\d" . ":") ("\\\\b" . "\\"))) + ) + +(defun rudel-obby-parse-color (color) + "Parse the obby color string COLOR into an Emacs color." + (let* ((color-numeric (string-to-number color 16)) + (color-string (format "#%04X%04X%04X" + (lsh (logand #xff0000 color-numeric) -08) + (lsh (logand #x00ff00 color-numeric) -00) + (lsh (logand #x0000ff color-numeric) 08)))) + color-string) + ) + +(defun rudel-obby-format-color (color) + "Format the Emacs color COLOR as obby color string." + (multiple-value-bind (red green blue) (color-values color) + (format "%02x%02x%02x" (lsh red -8) (lsh green -8) (lsh blue -8)))) + +(defun rudel-obby-assemble-message (name &rest arguments) + "" + (concat (mapconcat + (lambda (part) + (if (and (not (null part)) (stringp part)) + (rudel-obby-escape-string part) + part)) + (cons name arguments) ":") + "\n") + ) + +(defun rudel-obby-parse-message (message) + "Split MESSAGE at `:' and unescape resulting parts. + +The terminating `\n' should be removed from MESSAGE before +calling this function." + (mapcar #'rudel-obby-unescape-string (split-string message ":"))) + +(defun rudel-obby-send (socket name arguments) + "Send an obby message NAME with arguments ARGUMENTS through SOCKET." + ;; First, assemble the message string. + (let ((message (apply #'rudel-obby-assemble-message + name arguments))) + (if (>= (length message) rudel-obby-long-message-threshold) + ;; For huge messages, chunk the message data and transmit the + ;; chunks + (let ((total (/ (length message) + rudel-obby-long-message-chunk-size)) + (current 0) + (reporter (make-progress-reporter "Sending data " 0.0 1.0))) + (rudel-loop-chunks message chunk rudel-obby-long-message-chunk-size + (progress-reporter-update reporter (/ (float current) total)) + (process-send-string socket chunk) + (incf current)) + (progress-reporter-done reporter)) + ;; Send small messages in one chunk + (process-send-string socket message))) + ) + + +;;; Autoloading +;; + +;;;###autoload +(rudel-add-backend (rudel-backend-get-factory 'protocol) + 'obby 'rudel-obby-backend) + +;;;###autoload +(eval-after-load 'rudel-zeroconf + '(rudel-zeroconf-register-service "_lobby._tcp" 'obby)) + +(provide 'rudel-obby) +;;; rudel-obby.el ends here diff --git a/emacs.d/lisp/rudel/obby/Project.ede b/emacs.d/lisp/rudel/obby/Project.ede new file mode 100644 index 0000000..53d2422 --- /dev/null +++ b/emacs.d/lisp/rudel/obby/Project.ede @@ -0,0 +1,14 @@ +;; Object rudel/obby +;; EDE project file. +(ede-proj-project "rudel/obby" + :name "obby" + :file "Project.ede" + :targets (list + (ede-proj-target-elisp "obby" + :name "obby" + :path "" + :source '("rudel-obby.el" "rudel-obby-util.el" "rudel-obby-client.el" "rudel-obby-server.el" "rudel-obby-errors.el" "rudel-obby-state.el") + :aux-packages '("rudel" "jupiter") + ) + ) + ) diff --git a/emacs.d/lisp/rudel/obby/rudel-obby-client.el b/emacs.d/lisp/rudel/obby/rudel-obby-client.el new file mode 100644 index 0000000..5c192db --- /dev/null +++ b/emacs.d/lisp/rudel/obby/rudel-obby-client.el @@ -0,0 +1,973 @@ +;;; rudel-obby-client.el --- Client functions of the Rudel obby backend +;; +;; Copyright (C) 2008, 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Rudel, obby, backend, client +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; This file contains the client part of the obby backend. + + +;;; History: +;; +;; 0.2 - State machine. +;; +;; 0.1 - Initial revision. + + +;;; Code: +;; + +(require 'eieio) + +(require 'jupiter) + +(require 'rudel-state-machine) +(require 'rudel-operations) +(require 'rudel-chat) + +(require 'rudel-obby-errors) +(require 'rudel-obby-util) +(require 'rudel-obby-state) + + +;;; Class rudel-obby-client-state-new +;; + +(defclass rudel-obby-client-state-new + (rudel-obby-client-connection-state) + () + "Start state of newly established connections.") + +(defmethod rudel-obby/obby_welcome + ((this rudel-obby-client-state-new) version) + "Handle obby 'welcome' message." + ;; Examine announced protocol version. + (with-parsed-arguments ((version number)) + (message "Received Obby welcome message (version %d)" version)) + ;; Start encryption handshake + 'encryption-negotiate) + + +;;; Class rudel-obby-client-state-encryption-negotiate +;; + +(defclass rudel-obby-client-state-encryption-negotiate + (rudel-obby-client-connection-state) + () + "Start state of the encryption handshake.") + +(defmethod rudel-obby/net6_encryption + ((this rudel-obby-client-state-encryption-negotiate) value) + "Handle net6 'encryption' message." + (rudel-send this "net6_encryption_ok") + 'encryption-start) + + +;;; Class rudel-obby-client-connection-encryption-start +;; + +(defclass rudel-obby-client-state-encryption-start + (rudel-obby-client-connection-state) + () + "Second state of the encryption handshake.") + +(defmethod rudel-obby/net6_encryption_begin + ((this rudel-obby-client-state-encryption-start)) + "Handle net6 'encryption_begin' message." + ;; Start TLS encryption for the connection. + (with-slots (connection) this + (with-slots (socket) connection + (when (rudel-process-object socket :supports-tls) + (rudel-tls-start-tls socket) + (sit-for 1)))) + + ;; The connection is now established + 'joining) + +(defmethod rudel-obby/net6_encryption_failed + ((this rudel-obby-client-state-encryption-start)) + "Handle net6 'encryption_failed' message." + ;; The connection is now established; without encryption though + 'joining) + + +;;; Class rudel-obby-client-state-joining +;; + +(defclass rudel-obby-client-state-joining + (rudel-obby-client-connection-state) + () + "First state after the connection has been properly set up.") + +(defmethod rudel-enter ((this rudel-obby-client-state-joining)) + "When entering this state, send a login request." + ;; Send login request with username and color. This can easily fail + ;; (resulting in response 'net6_login_failed') if the username or + ;; color is already taken. + (with-slots (info) (oref this connection) + (let ((username (plist-get info :username)) + (color (plist-get info :color)) + (global-password (plist-get info :global-password)) + (user-password (plist-get info :user-password))) + (apply #'rudel-send + this + "net6_client_login" + username (rudel-obby-format-color color) + (append (when global-password + (list global-password)) + (when (and global-password user-password) + (list user-password)))))) + nil) + +(defmethod rudel-obby/obby_sync_init + ((this rudel-obby-client-state-joining) count) + "Handle obby 'sync_init' message." + ;; Switch to 'synching' state, passing the number of synchronization + ;; items. + (with-parsed-arguments ((count number)) + (list 'session-synching count))) + +(defmethod rudel-obby/net6_login_failed + ((this rudel-obby-client-state-joining) reason) + "Handle net6 'login_failed' message." + (with-parsed-arguments ((reason number)) + (with-slots (connection) this + (let ((error-data + (cond + ;; Invalid username + ((= reason rudel-obby-error-username-invalid) + (cons 'rudel-obby-username-invalid nil)) + ;; Username in use + ((= reason rudel-obby-error-username-in-use) + (cons 'rudel-obby-username-in-use nil)) + ;; Color in use + ((= reason rudel-obby-error-color-in-use) + (cons 'rudel-obby-color-in-use nil)) + ;; Wrong global password + ((= reason rudel-obby-error-wrong-global-password) + (cons 'rudel-obby-wrong-global-password nil)) + ;; Wrong user password + ((= reason rudel-obby-error-wrong-user-password) + (cons 'rudel-obby-wrong-user-password nil)) + ;; Otherwise, signal a generic join error + (t (cons 'rudel-join-error nil))))) + + ;; Switch to 'join-failed' state, pass the error data. + (list 'join-failed error-data)))) + ) + + +;;; Class rudel-obby-client-state-join-failed +;; + +(defclass rudel-obby-client-state-join-failed + (rudel-obby-client-connection-state) + ((error-symbol :initarg :error-symbol + :type symbol + :documentation + "Error symbol describing the reason for the +login failure.") + (error-data :initarg :error-data + :type list + :documentation + "Additional error data describing the login +failure.")) + "State for failed login attempts.") + +(defmethod rudel-enter ((this rudel-obby-client-state-join-failed) + error) + "When the state is entered, store the error data passed in ERROR." + (with-slots (error-symbol error-data) this + (setq error-symbol (car error) + error-data (cdr error))) + nil) + + +;;; Class rudel-obby-client-state idle +;; + +(defclass rudel-obby-client-state-idle + (rudel-obby-client-connection-state + rudel-obby-document-handler) + () + "Default state of the connection.") + +(defmethod rudel-obby/net6_client_join + ((this rudel-obby-client-state-idle) + client-id name encryption user-id color) + "Handle net6 'client_join' message." + (with-parsed-arguments ((client-id number) + (user-id number) + (color color)) + (with-slots (connection) this + (with-slots (session) connection + (let ((user (rudel-find-user session user-id + #'eq #'rudel-id))) + (if user + ;; If we have such a user object, update its state. + (with-slots ((client-id1 client-id) + (color1 color) + connected + (encryption1 encryption)) user + (setq client-id1 client-id + color1 color + connected t + encryption1 (string= encryption "1")) + + ;; Run the change hook of the user object. + (object-run-hook-with-args user 'change-hook)) + ;; Otherwise, create a new user object. + (let ((user (rudel-obby-user + name + :client-id client-id + :user-id user-id + :connected t + :encryption (string= encryption "1") + :color color))) + (rudel-add-user session user)))))) + (message "Client joined: %s %s" name color)) + nil) + +(defmethod rudel-obby/net6_client_part + ((this rudel-obby-client-state-idle) client-id) + "Handle net6 'client_part' message." + ;; Find the user object, associated to the client id. Remove the + ;; client id and change the user's state to disconnected. + (with-parsed-arguments ((client-id number)) + (with-slots (connection) this + (with-slots (session) connection + (let ((user (rudel-find-user session client-id + #'eql #'rudel-client-id))) + (if user + (with-slots (client-id connected) user + ;; Set slot values. + (setq client-id nil + connected nil) + + ;; Run the change hook of the user object. + (object-run-hook-with-args user 'change-hook)) + (display-warning + '(rudel obby) + (format "Cannot find user for client id: %d" + client-id) + :warning)))))) + nil) + +(defmethod rudel-obby/obby_user_colour + ((this rudel-obby-client-state-idle) user-id color) + "Handle obby 'user_colour' message." + (with-parsed-arguments ((user-id number) + (color color)) + ;; Find user object and set color. + (with-slots (connection) this + (with-slots (session) connection + (let ((user (rudel-find-user session user-id + #'= #'rudel-id))) + (with-slots ((name :object-name) (color1 :color)) user + ;; Set color in user object. + (setq color1 color) + + ;; Run the change hook of the user object. + (object-run-hook-with-args user 'change-hook) + + ;; Update overlays. + (rudel-overlay-set-face-attributes + (rudel-overlay-make-face-symbol 'author name) + color1)))))) + nil) + +(defmethod rudel-obby/obby_document_create + ((this rudel-obby-client-state-idle) + owner-id doc-id name suffix encoding) + "Handle obby 'document_create' message." + (with-parsed-arguments ((owner-id number) + (doc-id number) + (suffix number) + (encoding coding-system)) + (with-slots (connection) this + (with-slots (session) connection + (let ((owner (rudel-find-user session owner-id + #'= #'rudel-id))) + (rudel-add-document session (rudel-obby-document + name + :subscribed (list owner) + :id doc-id + :owner-id owner-id + :suffix suffix)))) + (message "New document: %s" name))) + nil) + +(defmethod rudel-obby/obby_document_remove + ((this rudel-obby-client-state-idle) doc-id) + "Handle obby 'document_remove' message." + (with-parsed-arguments ((doc-id document-id)) + (with-slots (connection) this + (with-slots (session) connection + (let ((document (rudel-find-document + session doc-id + #'equal #'rudel-both-ids))) + (if document + (progn + (rudel-remove-document session document) + (with-slots ((name :object-name)) document + (message "Document removed: %s" name))) + (display-warning + '(rudel obby) + (format "Document not found: %s" doc-id) + :warning)))))) + nil) + +(defmethod rudel-obby/obby_document/rename + ((this rudel-obby-client-state-idle) + document user new-name new-suffix) + "Handle obby 'rename' submessage of the 'obby_document' message." + (with-parsed-arguments ((new-suffix number)) + (with-slots ((name :object-name) suffix) document + (setq name new-name + suffix new-suffix))) + nil) + +(defmethod rudel-obby/obby_document/subscribe + ((this rudel-obby-client-state-idle) + document user-id) + "Handle 'subscribe' submessage of obby 'document' message." + (with-parsed-arguments ((user-id number)) + (with-slots (connection) this + (with-slots (session) connection + (let ((user (rudel-find-user session user-id + #'= #'rudel-id))) + (rudel-add-user document user))))) + nil) + +(defmethod rudel-obby/obby_document/unsubscribe + ((this rudel-obby-client-state-idle) + document user-id) + "Handle 'unsubscribe' submessage of obby 'document' message." + (with-parsed-arguments ((user-id number)) + (with-slots (connection) this + (with-slots (session) connection + (let ((user (rudel-find-user session user-id + #'= #'rudel-id))) + (rudel-remove-user document user))))) + nil) + +(defmethod rudel-obby/obby_document/record + ((this rudel-obby-client-state-idle) + document user-id local-revision remote-revision + action &rest arguments) + "Handle 'record' submessage of obby 'document' message." + (with-parsed-arguments ((user-id number) + (local-revision number) + (remote-revision number)) + ;; Locate the user. + (let ((user (with-slots (connection) this + (with-slots (session) connection + (rudel-find-user session user-id + #'= #'rudel-id))))) + (if user + (condition-case error + ;; Try to dispatch + (rudel-dispatch + this "rudel-obby/obby_document/record/" action + (append (list document user local-revision remote-revision) + arguments)) + ;; Warn if we failed to locate or execute the + ;; method. Return nil in this case, so we remain in the + ;; current state. + (rudel-dispatch-error + (progn + (display-warning + '(rudel obby) + (format "%s: no method (%s: %s): `%s:%s'; arguments: %s" + (object-print this) (car error) (cdr error) + "rudel-obby/obby_document/record/" action arguments) + :debug) + nil))) + ;; If we did not find the user, warn. + (progn + (display-warning + '(rudel obby) + (format "User not found: %d" user-id) + :warning) + nil)))) + ) + +(defmethod rudel-obby/obby_document/record/ins + ((this rudel-obby-client-state-idle) + document user local-revision remote-revision + position data) + "Handle 'ins' submessage of 'record' submessage of obby 'document' message." + (with-parsed-arguments ((position number)) + (let ((operation (jupiter-insert + (format "insert-%d-%d" + remote-revision local-revision) + :from position + :data data))) + (with-slots (connection) this + (rudel-remote-operation connection + document user + remote-revision local-revision + operation)))) + nil) + +(defmethod rudel-obby/obby_document/record/del + ((this rudel-obby-client-state-idle) + document user local-revision remote-revision + position length) + "Handle 'del' submessage of 'record' submessage of obby 'document' message." + (with-parsed-arguments ((position number) + (length number)) + (let ((operation (jupiter-delete + (format "delete-%d-%d" + remote-revision local-revision) + :from position + :to (+ position length)))) + (with-slots (connection) this + (rudel-remote-operation connection + document user + remote-revision local-revision + operation)))) + nil) + +(defmethod rudel-obby/obby_document/record/split + ((this rudel-obby-client-state-idle) + document user local-revision remote-revision + &rest operations) + "Handle 'split' submessage of 'record' submessage of obby 'document' message." + (let ((operation (rudel-message->operation + (cons "split" operations) + local-revision remote-revision))) + (with-slots (connection) this + (rudel-remote-operation connection + document user + remote-revision local-revision + operation))) + nil) + +(defmethod rudel-obby/obby_document/record/noop + ((this rudel-obby-client-state-idle) + document user local-revision remote-revision) + "Handle 'noop' submessage of 'record' submessage of obby 'document' message." + (let ((operation (jupiter-nop + (format "nop-%d-%d" + remote-revision local-revision)))) + (with-slots (connection) this + (rudel-remote-operation connection + document user + remote-revision local-revision + operation))) + nil) + +(defmethod rudel-obby/obby_message ((this rudel-obby-client-state-idle) + sender text) + "Handle obby 'message' message" + (with-parsed-arguments ((sender number)) + (with-slots (session) (oref this :connection) + (let ((sender (rudel-find-user session sender #'eq #'rudel-id))) + (rudel-chat-dispatch-message sender text)))) + nil) + + +;;; Class rudel-obby-client-state-session-synching +;; + +(defclass rudel-obby-client-state-session-synching + (rudel-obby-client-connection-state) + ((all-items :initarg :all-items + :type (integer 0) + :documentation + "Total number of synchronization items expected + to receive from the server.") + (remaining-items :initarg :remaining-items + :type (integer 0) + :documentation + "Number of synchronization items not yet + received from the server.") + (have-self :initarg :have-self + :type boolean + :documentation + "Flag that remembers, whether the session has + a 'self' user object.")) + "State used for synching session data.") + +(defmethod rudel-enter ((this rudel-obby-client-state-session-synching) + num-items) + "When entering state, store number of expected items." + (with-slots (all-items remaining-items have-self) this + (setq all-items num-items + remaining-items num-items + have-self nil)) + nil) + +(defmethod rudel-obby/net6_client_join + ((this rudel-obby-client-state-session-synching) + client-id name encryption user-id color) + "Handle net6 'client_join' message." + (with-parsed-arguments ((client-id number) + (user-id number) + (color color)) + (with-slots (connection remaining-items have-self) this + (with-slots (session) connection + ;; Construct user object and add it to the session. + (let ((user (rudel-obby-user + name + :client-id client-id + :user-id user-id + :connected t + :encryption (string= encryption "1") + :color color))) + (rudel-add-user session user) + + ;; The first user object describes the user of this client. + (unless have-self + (with-slots (self) session + (setq self user + have-self t))))) + + ;; Decrease number of not yet received synchronization items. + (decf remaining-items))) + nil) + +(defmethod rudel-obby/obby_sync_usertable_user + ((this rudel-obby-client-state-session-synching) user-id name color) + "Handle obby 'sync_usertable_user' message." + (with-parsed-arguments ((user-id number) + (color color)) + (with-slots (connection remaining-items) this + (with-slots (session) connection + (rudel-add-user session (rudel-obby-user + name + :user-id user-id + :connected nil + :color color))) + + ;; Decrease number of not yet received synchronization items. + (decf remaining-items))) + nil) + +(defmethod rudel-obby/obby_sync_doclist_document + ((this rudel-obby-client-state-session-synching) + owner-id doc-id name suffix encoding &rest subscribed-user-ids) + "Handle obby 'sync_doclist_document' message." + (with-parsed-arguments ((doc-id number) + (owner-id number) + (suffix number) + (encoding coding-system)) + (with-slots (connection remaining-items) this + (with-slots (session) connection + ;; Retrieve the subscribed users + (let ((subscribed-users + (mapcar + (lambda (user-id) + (with-parsed-arguments ((user-id number)) + (rudel-find-user session user-id + #'= #'rudel-id))) + subscribed-user-ids))) + + ;; Make a new document with the list of subscribed users. + (rudel-add-document session (rudel-obby-document + name + :subscribed subscribed-users + :id doc-id + :owner-id owner-id + :suffix suffix)))) + + ;; Decrease number of not yet received synchronization items. + (decf remaining-items))) + nil) + +(defmethod rudel-obby/obby_sync_final + ((this rudel-obby-client-state-session-synching)) + "Handle obby 'sync_final' message." + 'idle) + +(defmethod object-print ((this rudel-obby-client-state-session-synching) + &rest strings) + "Append number of remaining items to string representation." + (with-slots (remaining-items) this + (call-next-method this (format " remaining: %d" remaining-items)))) + + +;;; Class rudel-obby-client-state-subscribing +;; + +(defclass rudel-obby-client-state-subscribing + (rudel-obby-client-connection-state + rudel-obby-document-handler) + ((document :initarg :document + :type rudel-obby-document-child + :documentation + "")) + "") + +(defmethod rudel-enter ((this rudel-obby-client-state-subscribing) + user document) + "When entering this state, send a subscription request to the server." + (with-slots ((document1 :document)) this + (setq document1 document) + + (with-slots ((doc-id :id) owner-id) document1 + (with-slots (user-id) user + (rudel-send this "obby_document" + (format "%x %x" owner-id doc-id) + "subscribe" + (format "%x" user-id))))) + nil) + +(defmethod rudel-obby/obby_document/sync_init + ((this rudel-obby-client-state-subscribing) + document num-bytes) + "Handle obby 'sync_init' message." + (with-parsed-arguments ((num-bytes number)) + (with-slots (documents) this + (if (= num-bytes 0) + 'idle + (list 'document-synching document num-bytes)))) + ) + + +;;; Class rudel-obby-client-state-document-synching +;; + +(defclass rudel-obby-client-state-document-synching + (rudel-obby-client-connection-state + rudel-obby-document-handler) + ((document :initarg :document + :type rudel-obby-document-child + :documentation + "") + (all-bytes :initarg :all-bytes + :type (integer 0) + :documentation + "") + (remaining-bytes :initarg :remaining-bytes + :type (integer 0) + :documentation + "")) + "") + +(defmethod rudel-enter ((this rudel-obby-client-state-document-synching) + document num-bytes) + "" + (with-slots ((document1 :document) all-bytes remaining-bytes) this + (setq document1 document + all-bytes num-bytes + remaining-bytes num-bytes)) + nil) + +(defmethod rudel-obby/obby_document/sync_chunk + ((this rudel-obby-client-state-document-synching) + document data user-id) + "Handle obby 'sync_chunk' message." + (with-parsed-arguments ((user-id number)) + (with-slots (connection remaining-bytes) this + (with-slots (session) connection + (let* ((user (unless (zerop user-id) + (rudel-find-user session user-id + #'= #'rudel-id))) + (operation (rudel-insert-op "bulk-insert" + :from nil + :data data))) + (rudel-remote-operation document user operation))) + + ;; After all bytes are transferred, go back to idle state. + (decf remaining-bytes (string-bytes data)) + (if (= remaining-bytes 0) + 'idle + nil))) + ) + +(defmethod object-print ((this rudel-obby-client-state-document-synching) + &rest strings) + "Append number of remaining items to string representation." + (with-slots (remaining-bytes) this + (call-next-method this (format " remaining: %d" remaining-bytes)))) + + +;;; Class rudel-obby-client-state-they-finalized +;; + +(defclass rudel-obby-client-state-they-finalized + (rudel-obby-client-connection-state) + () + "State used to indicate that the connection was closed by the peer.") + + +;;; Client connection states. +;; + +(defvar rudel-obby-client-connection-states + '((new . rudel-obby-client-state-new) + (encryption-negotiate . rudel-obby-client-state-encryption-negotiate) + (encryption-start . rudel-obby-client-state-encryption-start) + (joining . rudel-obby-client-state-joining) + (join-failed . rudel-obby-client-state-join-failed) + (idle . rudel-obby-client-state-idle) + (session-synching . rudel-obby-client-state-session-synching) + (subscribing . rudel-obby-client-state-subscribing) + (document-synching . rudel-obby-client-state-document-synching) + (they-finalized . rudel-obby-client-state-they-finalized)) + "Name symbols and classes of connection states.") + + +;;; Class rudel-obby-connection +;; + +(defclass rudel-obby-connection (rudel-obby-socket-owner + rudel-connection + rudel-state-machine) + ((info :initarg :info + :type list + :documentation + "Stores connection information for later use.") + (contexts :initarg :contexts + :type hash-table + :documentation + "Contains jupiter context objects for all +documents.")) + "Class rudel-obby-connection ") + +(defmethod initialize-instance ((this rudel-obby-connection) &rest slots) + ;; Initialize slots of THIS + (when (next-method-p) + (call-next-method)) + + ;; Create a new hash-table object to hold jupiter contexts + ;; associated to documents. + (with-slots (contexts) this + (setq contexts (make-hash-table :test #'equal))) + + ;; Register states. + (rudel-register-states this rudel-obby-client-connection-states) + ) + +(defmethod rudel-register-state ((this rudel-obby-connection) + symbol state) + "Register SYMBOL and STATE and set connection slot of STATE." + ;; Associate THIS connection to STATE. + (oset state :connection this) + + ;; Register STATE. + (when (next-method-p) + (call-next-method)) + ) + +(defmethod rudel-disconnect ((this rudel-obby-connection)) + "" + (when (next-method-p) + (call-next-method))) + +(defmethod rudel-close ((this rudel-obby-connection)) + "" + ;; Move the state machine into an error state. + (rudel-switch this 'they-finalized) + + ;; Terminate the session. + (with-slots (session) this + (rudel-end session))) + +(defmethod rudel-find-context ((this rudel-obby-connection) document) + "Return the jupiter context associated to DOCUMENT in THIS connection." + (with-slots (contexts) this + (gethash (oref document :id) contexts))) + +(defmethod rudel-add-context ((this rudel-obby-connection) document) + "Add a jupiter context for DOCUMENT to THIS connection." + (with-slots (contexts) this + (with-slots ((doc-name :object-name) (doc-id :id)) document + (puthash doc-id + (jupiter-context (format "%s" doc-name)) + contexts))) + ) + +(defmethod rudel-remove-context ((this rudel-obby-connection) document) + "Remove the jupiter context associated to DOCUMENT from THIS connection." + (with-slots (contexts) this + (remhash (oref document :id) contexts))) + +(defmethod rudel-message ((this rudel-obby-connection) message) + "Dispatch MESSAGE to the current state of THIS object. +If the state has no suitable method, generate a warning, but do +nothing else." + ;; Dispatch message to state. + (rudel-accept this message)) + +(defmethod rudel-change-color- ((this rudel-obby-connection) color) + "" + (rudel-send this "obby_user_colour" + (rudel-obby-format-color color))) + +(defmethod rudel-publish ((this rudel-obby-connection) document) + "" + ;; Create a new jupiter context for DOCUMENT. + (rudel-add-context this document) + + ;; Announce the new document to the server. + (with-slots ((name :object-name) id buffer) document + (rudel-send this "obby_document_create" + (format "%x" id) + name + "UTF-8" + (with-current-buffer buffer + (buffer-string)))) + ) + +(defmethod rudel-unpublish ((this rudel-obby-connection) document) + "Remove DOCUMENT from the obby session THIS is connected to." + ;; Request removal of DOCUMENT. + (with-slots ((doc-id :id) owner-id) document + (rudel-send this "obby_document_remove" + (format "%x %x" owner-id doc-id))) + + ;; Remove the jupiter context for DOCUMENT. + (rudel-remove-context this document) + ) + +(defmethod rudel-subscribe-to ((this rudel-obby-connection) document) + "" + ;; Create a new jupiter context for DOCUMENT. + (rudel-add-context this document) + + ;; Switch to subscribing state and wait until the state goes back to + ;; idle. + (with-slots (session) this + (with-slots (self) session + (rudel-switch this 'subscribing self document))) + + (lexical-let ((reporter (make-progress-reporter "Subscribing " 0.0 1.0))) + (flet ((display-progress (state) + (cond + ;; Syncing document content, we can provide detailed progress. + ((and (consp state) + (eq (car state) 'document-synching)) + (with-slots (all-bytes remaining-bytes) (cdr state) + (progress-reporter-force-update + reporter + (- 1.0 (/ (float remaining-bytes) (float all-bytes))) + (format "Subscribing (%s) " (car state))))) + + ;; For other states, we just spin. + ((consp state) + (progress-reporter-force-update + reporter 0.5 + (format "Subscribing (%s) " (car state)))) + + ;; Done + (t + (progress-reporter-force-update reporter 1.0 "Subscribing ") + (progress-reporter-done reporter))))) + (rudel-state-wait this '(idle) '(they-finalized) #'display-progress))) + + ;; We receive a notification of our own subscription from the + ;; server. Consequently we do not add SELF to the list of subscribed + ;; users of DOCUMENT. + ) + +(defmethod rudel-unsubscribe-from ((this rudel-obby-connection) document) + "" + ;; Delete the jupiter context for DOCUMENT. + (rudel-remove-context this document) + + ;; Announce the end of our subscription to the server. + (with-slots (session) this + (with-slots (user-id) (oref session :self) + (with-slots ((doc-id :id) owner-id) document + (rudel-send this "obby_document" + (format "%x %x" owner-id doc-id) + "unsubscribe" + (format "%x" user-id))))) + + ;; We receive a notification of the end of our own subscription from + ;; the server. Consequently we do not remove SELF from the list of + ;; subscribed users of DOCUMENT. + ) + +(defmethod rudel-local-insert ((this rudel-obby-connection) + document position data) + "" + (rudel-local-operation + this + document + (jupiter-insert "insert" :from position :data data))) + +(defmethod rudel-local-delete ((this rudel-obby-connection) + document position length) + "" + (rudel-local-operation + this + document + (jupiter-delete "delete" :from position :to (+ position length)))) + +(defmethod rudel-local-operation ((this rudel-obby-connection) + document operation) + "Handle OPERATION performed on DOCUMENT by sending a message through THIS connection." + ;; Convert character positions in OPERATION to byte positions, since + ;; the obby protocol works with byte positions, but Emacs uses + ;; character positions. + (with-slots (buffer) document + (rudel-obby-char->byte operation buffer)) + + ;; Find jupiter context for DOCUMENT. + (let ((context (rudel-find-context this document))) + + ;; Notify the server of the operation. + (with-slots (owner-id (doc-id :id)) document + (with-slots (local-revision remote-revision) context + (apply #'rudel-send + this + "obby_document" + (format "%x %x" owner-id doc-id) + "record" + (format "%x" local-revision) + (format "%x" remote-revision) + (rudel-operation->message operation)))) + + ;; Submit the operation to the jupiter context. + (jupiter-local-operation context operation)) + ) + +(defmethod rudel-remote-operation ((this rudel-obby-connection) + document user + remote-revision local-revision + operation) + "Handle OPERATION received through THIS connection performed by USER on DOCUMENT." + (let* (;; Find jupiter context for DOCUMENT. + (context (rudel-find-context this document)) + ;; And transform the operation. + (transformed (jupiter-remote-operation + context + remote-revision local-revision + operation))) + + ;; Convert byte positions in OPERATION to character positions, + ;; since the obby protocol works with byte positions, but Emacs + ;; uses character positions. + (with-slots (buffer) document + (rudel-obby-byte->char transformed buffer)) ;; TODO operation's responsibility? + + ;; Apply the transformed operation to the document. + (rudel-remote-operation document user transformed)) + ) + +(provide 'rudel-obby-client) +;;; rudel-obby-client.el ends here diff --git a/emacs.d/lisp/rudel/obby/rudel-obby-debug.el b/emacs.d/lisp/rudel/obby/rudel-obby-debug.el new file mode 100644 index 0000000..8f5f168 --- /dev/null +++ b/emacs.d/lisp/rudel/obby/rudel-obby-debug.el @@ -0,0 +1,122 @@ +;;; rudel-obby-debug.el --- Debugging functions for obby backend +;; +;; Copyright (C) 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Rudel, obby, debugging +;; X-RCS: $Id:$ +;; +;; This program 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. +;; +;; This program 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 this program. If not, see . + + +;;; Commentary: +;; +;; Debugging functions for the obby backend. + + +;;; History: +;; +;; 0.1 - Initial revision. + + +;;; Code: +;; + +(require 'eieio) + +(require 'rudel-debug) + +(require 'rudel-obby-util) + + +;;; Variables +;; + +(defvar rudel-obby-debug-old-state nil + "Saves state of state machines across one function call.") + + +;;; Functions +;; + +(defmethod rudel-send :before ((this rudel-obby-socket-owner) + name &rest arguments) + "Print NAME and ARGUMENTS to debug stream." + (let ((message (apply #'rudel-obby-assemble-message + name arguments))) + + (with-slots (socket) this + (rudel-debug-stream-insert + (rudel-debug-stream-name socket) + :sent + (concat (substring message 0 (min (length message) 100)) + (when (> (length message) 100) + "...")) + (append (list name) arguments)))) + ) + +(defmethod rudel-receive :before ((this rudel-obby-socket-owner) data) + "Print DATA to debug stream." + (with-slots (socket) this + (rudel-debug-stream-insert + (rudel-debug-stream-name socket) + :received + (concat (substring data 0 (min (length data) 100)) + (when (> (length data) 100) + "...")))) + ) + +(defmethod rudel-message :before ((this rudel-obby-socket-owner) + message) + "Print DATA to debug stream." + (let ((data (apply #'rudel-obby-assemble-message message))) + + (with-slots (socket) this + (rudel-debug-stream-insert + (rudel-debug-stream-name socket) + :received-processed + (concat (substring data 0 (min (length data) 100)) + (when (> (length data) 100) + "...")) + message) + )) + ) + +(defmethod rudel-switch :before ((this rudel-obby-socket-owner) + state &rest arguments) + "Store name of STATE for later printing." + (with-slots (state) this + (setq rudel-obby-debug-old-state + (if state + (object-name-string state) + "#start"))) + ) + +(defmethod rudel-switch :after ((this rudel-obby-socket-owner) + state &rest arguments) + "Print STATE and ARGUMENTS to debug stream." + (with-slots (socket state) this + (let ((old-state rudel-obby-debug-old-state) + (new-state (object-name-string state))) + (unless (string= old-state new-state) + (rudel-debug-stream-insert + (rudel-debug-stream-name socket) + :special + (if arguments + (format "%s -> %s %s" old-state new-state arguments) + (format "%s -> %s" old-state new-state)))))) + ) + +(provide 'rudel-obby-debug) +;;; rudel-obby-debug.el ends here diff --git a/emacs.d/lisp/rudel/obby/rudel-obby-errors.el b/emacs.d/lisp/rudel/obby/rudel-obby-errors.el new file mode 100644 index 0000000..689d4a8 --- /dev/null +++ b/emacs.d/lisp/rudel/obby/rudel-obby-errors.el @@ -0,0 +1,65 @@ +;;; rudel-obby-errors.el --- Error data used in the obby Rudel backend +;; +;; Copyright (C) 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Rudel, obby, errors +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; This file contains definitions of error conditions and numeric +;; error codes used in the Rudel obby backend. + + +;;; History: +;; +;; 0.1 - Initial revision. + + +;;; Code: +;; + + +;;; Obby protocol error codes +;; + +(defconst rudel-obby-error-username-invalid #x0001 + "Error code for invalid username.") + +(defconst rudel-obby-error-username-in-use #x0002 + "Error code for username already in use.") + +(defconst rudel-obby-error-color-in-use #x0100 + "Error code for color already in use.") + +(defconst rudel-obby-error-wrong-global-password #x0101 + "Error code for wrong global password.") + +(defconst rudel-obby-error-wrong-user-password #x0102 + "Error code for wrong user password.") + +(defconst rudel-obby-error-protocol-version-mismatch #x0103 + "Error code for protocol version mismatch.") + +(defconst rudel-obby-error-not-encrypted #x0104 + "Error code for not encrypted.") + +(provide 'rudel-obby-errors) +;;; rudel-obby-errors.el ends here diff --git a/emacs.d/lisp/rudel/obby/rudel-obby-server.el b/emacs.d/lisp/rudel/obby/rudel-obby-server.el new file mode 100644 index 0000000..5bf1158 --- /dev/null +++ b/emacs.d/lisp/rudel/obby/rudel-obby-server.el @@ -0,0 +1,798 @@ +;;; rudel-obby-server.el --- Server component of the Rudel obby backend +;; +;; Copyright (C) 2008, 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Rudel, obby, backend, server +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; This file contains the server part of the obby backend for Rudel. +;; +;; It is implemented using one state machine (class +;; `rudel-obby-client') for each client connection. These state +;; machines have the following states: +;; +;; + new `rudel-obby-server-state-new' +;; + encryption-negotiate `rudel-obby-server-state-encryption-negotiate' +;; + before-join `rudel-obby-server-state-before-join' +;; + idle `rudel-obby-server-state-idle' + + +;;; History: +;; +;; 0.2 - State machine. +;; +;; 0.1 - Initial revision. + + +;;; Code: +;; + +(eval-when-compile + (require 'cl)) + +(require 'eieio) + +(require 'jupiter) + +(require 'rudel-state-machine) + +(require 'rudel-obby-errors) +(require 'rudel-obby-util) +(require 'rudel-obby-state) + + +;;; Class rudel-obby-server-state-new +;; + +(defclass rudel-obby-server-state-new + (rudel-obby-server-connection-state) + () + "State in which new connections start out.") + +(defmethod rudel-enter ((this rudel-obby-server-state-new)) + "Sends welcome messages to the client and starts the session +timeout timer." + ;; Send greeting sequence to the client. + (rudel-send this + "obby_welcome" + (number-to-string rudel-obby-protocol-version)) + + ;; Switch to encryption negotiation state. + 'encryption-negotiate) + + +;;; Class rudel-obby-server-state-encryption-negotiate +;; + +(defclass rudel-obby-server-state-encryption-negotiate + (rudel-obby-server-connection-state) + () + "Encryption negotiation state.") + +(defmethod rudel-enter ((this rudel-obby-server-state-encryption-negotiate)) + "Send net6 'encryption' message requesting to not enable encryption." + (rudel-send this "net6_encryption" "0") + nil) + +(defmethod rudel-obby/net6_encryption_ok + ((this rudel-obby-server-state-encryption-negotiate)) + "Handle net6 'encryption_ok' message. +Even if the client requests an encrypted connection, we cancel +the negotiation." + (rudel-send this "net6_encryption_failed") + 'before-join) + +(defmethod rudel-obby/net6_encryption_failed + ((this rudel-obby-server-state-encryption-negotiate)) + "Handle net6 'encryption_failed' message. +No action has to be taken, since the client simply proceeds after +failed encryption negotiation." + 'before-join) + + +;;; Class rudel-obby-server-state-before-join +;; + +(defclass rudel-obby-server-state-before-join + (rudel-obby-server-connection-state) + () + "Waiting for client request joining the session.") + +(defmethod rudel-obby/net6_client_login + ((this rudel-obby-server-state-before-join) username color + &optional global-password user-password) + "Handle net6 'client_login' message." + (with-parsed-arguments ((color color)) + (with-slots (server + (client-id :id) + user + encryption) (oref this :connection) + ;; Make sure USERNAME and COLOR are valid. + (let ((error (rudel-check-username-and-color + server username color))) + (if error + ;; If USERNAME or COLOR are invalid, send the error code + ;; to the client and stay in the current state. + (progn + (rudel-send this + "net6_login_failed" + (format "%x" error)) + nil) + + ;; Create a user object for this client and add it to the + ;; server. + (setq user (rudel-make-user + server + username client-id color encryption)) + (rudel-add-user server user) + + ;; Broadcast the join event to all clients (including the + ;; new one). + (with-slots ((name :object-name) color (user-id :user-id)) user + (rudel-broadcast this (list 'exclude (oref this :connection)) + "net6_client_join" + (format "%x" client-id) + name + "0" + (format "%x" user-id) + (rudel-obby-format-color color))) + + ;; Get the new client up to date: + ;; - transmit user list + ;; - connected users + ;; - disconnected users + ;; - transmit document list + (with-slots (users clients documents) server + ;; Send number of synchronization items: sum of numbers of + ;; offline users and documents. + (let ((number-of-items (+ (length users) (length documents)))) + (rudel-send this + "obby_sync_init" + (format "%x" number-of-items))) + + ;; Transmit list of connected users. + (dolist (client clients) + (with-slots ((client-id :id) user) client + (when user + (with-slots ((name :object-name) + color + (user-id :user-id)) user + (rudel-send this + "net6_client_join" + (format "%x" client-id) + name + "0" + (format "%x" user-id) + (rudel-obby-format-color color)))))) + + ;; Transmit list of disconnected users. + (let ((offline-users (remove-if #'rudel-connected users))) + (dolist (user offline-users) + (with-slots ((name :object-name) user-id color) user + (rudel-send this + "obby_sync_usertable_user" + (format "%x" user-id) + name + (rudel-obby-format-color color))))) + + ;; Transmit document list + (dolist (document documents) + (with-slots ((name :object-name) + (doc-id :id) + owner-id + suffix + subscribed) document + (apply #'rudel-send + this + "obby_sync_doclist_document" + (format "%x" owner-id) + (format "%x" doc-id) + name + (format "%x" suffix) + "UTF-8" + (mapcar + (lambda (user1) ;; TODO we could use `user' here, but there is a bug in cl + (format "%x" (rudel-id user1))) + subscribed))))) + + (rudel-send this "obby_sync_final") + 'idle)))) + ) + + +;;; Class rudel-obby-server-state-idle +;; + +(defclass rudel-obby-server-state-idle + (rudel-obby-server-connection-state) + () + "Idle state of a server connection. + +The connection enters this state when all setup work is finished, +the client has joined the session and no operation is in +progress. In this state, the connection waits for new messages +from the client that initiate operations. Simple (which means +stateless in this case) operations are performed without leaving +the idle state.") + +(defmethod rudel-obby/obby_user_colour + ((this rudel-obby-server-state-idle) color-) + "Handle obby 'user_colour' message. +This method is called when the connected user requests a change +of her color to COLOR." + (with-parsed-arguments ((color- color)) + (with-slots (user) (oref this :connection) + (with-slots (color (user-id :user-id)) user + ;; Set color slot value. + (setq color color-) + + ;; Run change hook. + (object-run-hook-with-args user 'change-hook) + + (rudel-broadcast this (list 'exclude (oref this :connection)) + "obby_user_colour" + (format "%x" user-id) + (rudel-obby-format-color color))))) + nil) + +(defmethod rudel-obby/obby_document_create + ((this rudel-obby-server-state-idle) + doc-id name encoding content) + "Handle obby 'document_create' message." + (with-parsed-arguments ((doc-id number) + (encoding coding-system)) + (with-slots (user server) (oref this :connection) + (with-slots ((user-id :user-id)) user + ;; Create a (hidden) buffer for the new document. + (let* ((buffer (get-buffer-create + (generate-new-buffer-name + (concat " *" name "*")))) + ;; Create the new document object + (document (rudel-obby-document + name + :buffer buffer + :subscribed (list user) + :id doc-id + :owner-id user-id + :suffix 1))) + + ;; Initialize the buffer's content + (with-current-buffer buffer + (insert content)) + + (with-slots (suffix) document + ;; Determine an appropriate suffix to provide an unique + ;; name for the new document. + (while (rudel-find-document server + (if (= suffix 1) + name + (format "%s<%d>" name suffix)) + #'string= #'rudel-unique-name) + (incf suffix)) + + ;; Add the document to the server's document list + (rudel-add-document server document) + + ;; Maybe notify the creating client of the changed suffix. + (unless (= suffix 1) + (rudel-send this + "obby_document" + (format "%x %x" user-id doc-id) + "rename" + (format "%x" user-id) + name + (format "%x" suffix))) + + ;; Notify other clients of the new document + (rudel-broadcast this (list 'exclude (oref this :connection)) + "obby_document_create" + (format "%x" user-id) + (format "%x" doc-id) + name + (format "%x" suffix) + (upcase (symbol-name encoding)))) + + ;; Add a jupiter context for (THIS DOCUMENT). + (rudel-add-context server (oref this :connection) document)))) + nil) + ) + +(defmethod rudel-obby/obby_document + ((this rudel-obby-server-state-idle) doc-id action &rest arguments) + "Handle obby 'document' messages." + (with-parsed-arguments ((doc-id document-id)) + ;; Locate the document based on owner id and document id + (let ((document (with-slots (server) (oref this :connection) + (rudel-find-document server doc-id + #'equal #'rudel-both-ids)))) + (rudel-obby-dispatch this action + (append (list document) arguments) + "rudel-obby/obby_document/"))) + ) + +(defmethod rudel-obby/obby_document/subscribe + ((this rudel-obby-server-state-idle) document user-id) + "Handle 'subscribe' submessage of obby 'document' message." + (with-parsed-arguments ((user-id number)) + (let ((user (with-slots (server) (oref this :connection) + (rudel-find-user server user-id + #'= #'rudel-id)))) + (with-slots (owner-id (doc-id :id) subscribed buffer) document + + ;; Track subscription, handle duplicate subscription requests. + (when (memq user subscribed) + (error "User `%s' already subscribed to document `%s'" + (object-name user) (object-name document))) + (rudel-add-user document user) + + ;; Synchronize the buffer content to the client. + (with-current-buffer buffer + ;; Send overall buffer size + (rudel-send this + "obby_document" + (format "%x %x" owner-id doc-id) + "sync_init" + (format "%x" (1- (position-bytes (point-max))))) + + ;; Send buffer chunks with author ids + (dolist (chunk (rudel-chunks document)) + (multiple-value-bind (from to author) chunk + (let ((string (buffer-substring (+ from 1) (+ to 1)))) + (rudel-send this + "obby_document" + (format "%x %x" owner-id doc-id) + "sync_chunk" + string + (format "%x" + (if author + (oref author :user-id) + 0))))))) + + ;; Notify clients of the new subscription (including our own + ;; client, who requested the subscription). + (with-slots ((user-id :user-id)) user + (rudel-broadcast this nil + "obby_document" + (format "%x %x" owner-id doc-id) + "subscribe" + (format "%x" user-id))))) + + ;; Add a jupiter context for (THIS document). + (with-slots (server) (oref this :connection) + (rudel-add-context server (oref this :connection) document)) + nil) + ) + +(defmethod rudel-obby/obby_document/unsubscribe + ((this rudel-obby-server-state-idle) document user-id) + "Handle 'unsubscribe' submessage of 'obby_document' message." + (with-parsed-arguments ((user-id number)) + (let ((user (with-slots (server) (oref this :connection) + (rudel-find-user server user-id + #'= #'rudel-id)))) + (with-slots (owner-id (doc-id :id) subscribed) document + + ;; Track subscription, handle invalid unsubscribe requests + (unless (memq user subscribed) + (error "User `%s' not subscribed to document `%s'" + (object-name user) (object-name document))) + (rudel-remove-user document user) + + ;; Notify clients of the canceled subscription (including our + ;; own client, who requested being unsubscribed). + (with-slots ((user-id :user-id)) user + (rudel-broadcast this nil + "obby_document" + (format "%x %x" owner-id doc-id) + "unsubscribe" + (format "%x" user-id)))) + + ;; Remove jupiter context for (THIS DOCUMENT). + (with-slots (server) (oref this :connection) + (rudel-remove-context server (oref this :connection) document))) + nil) + ) + +(defmethod rudel-obby/obby_document/record + ((this rudel-obby-server-state-idle) + document local-revision remote-revision action &rest arguments) + "Handle 'record' submessages of 'obby_document' message." + (with-parsed-arguments ((local-revision number) + (remote-revision number)) + ;; Dispatch to specialized operation handlers. + (rudel-obby-dispatch + this action + (append (list document local-revision remote-revision) + arguments) + "rudel-obby/obby_document/record/")) + ) + +(defmethod rudel-obby/obby_document/record/ins + ((this rudel-obby-server-state-idle) + document local-revision remote-revision position data) + "Handle 'ins' submessage of 'record' submessages of 'obby_document' message." + (with-parsed-arguments ((position number)) + ;; Construct the operation object and process it. + (rudel-remote-operation + (oref this :connection) document + remote-revision local-revision + (jupiter-insert + (format "insert-%d-%d" + remote-revision local-revision) + :from position + :data data)) + nil) + ) + +(defmethod rudel-obby/obby_document/record/del + ((this rudel-obby-server-state-idle) + document local-revision remote-revision position length) + "Handle 'del' submessage of 'record' submessages of 'obby_document' message." + (with-parsed-arguments ((position number) + (length number)) + ;; Construct the operation object and process it. + (rudel-remote-operation + (oref this :connection) document + remote-revision local-revision + (jupiter-delete + (format "delete-%d-%d" + remote-revision local-revision) + :from position + :to (+ position length))) + nil) + ) + + +;;; Client connection states. +;; + +(defvar rudel-obby-server-connection-states + '((new . rudel-obby-server-state-new) + (encryption-negotiate . rudel-obby-server-state-encryption-negotiate) + (before-join . rudel-obby-server-state-before-join) + (idle . rudel-obby-server-state-idle)) + "Name symbols and classes of connection states.") + + +;;; Class rudel-obby-client +;; + +(defclass rudel-obby-client (rudel-obby-socket-owner + rudel-state-machine) + ((server :initarg :server + :type rudel-obby-server + :documentation + "") + (id :initarg :id + :type integer + :accessor rudel-id + :documentation + "") + (user :initarg :user + :type (or rudel-obby-user null) + :initform nil + :documentation + "") + (encryption :initarg :encryption + :type boolean + :documentation + "")) + "Each object of this class represents one client, that is +connected to the server. This object handles all direct +communication with the client, while broadcast messages are +handled by the server.") + +(defmethod initialize-instance ((this rudel-obby-client) &rest slots) + "Initialize slots of THIS and register state machine states." + ;; Initialize slots of THIS + (when (next-method-p) + (call-next-method)) + + ;; Register states. + (rudel-register-states this rudel-obby-server-connection-states) + ) + +(defmethod rudel-register-state ((this rudel-obby-client) symbol state) + "Register SYMBOL and STATE and set connection slot of STATE." + ;; Associate THIS connection to STATE. + (oset state :connection this) + + ;; Register STATE. + (call-next-method)) + +(defmethod rudel-end ((this rudel-obby-client)) + "" + (rudel-disconnect this)) + +(defmethod rudel-close ((this rudel-obby-client)) + "" + (with-slots (server) this + (rudel-remove-client server this))) + +(defmethod rudel-message ((this rudel-obby-client) message) + "Dispatch MESSAGE to the active state of THIS state machine." + ;; Dispatch message to state + (rudel-accept this message)) + +(defmethod rudel-broadcast ((this rudel-obby-client) + receivers name &rest arguments) + "Broadcast message NAME with arguments ARGUMENTS to RECEIVERS." + (with-slots (server) this + (apply #'rudel-broadcast server receivers name arguments))) + +(defmethod rudel-remote-operation ((this rudel-obby-client) + document + local-revision remote-revision + operation) + "Execute and relay OPERATION on DOCUMENT." + (with-slots (server user) this + ;; Transform OPERATION and find clients that need to receive + ;; notifications. + (let* ((context (rudel-find-context server this document)) + (transformed (jupiter-remote-operation + context + local-revision remote-revision + operation)) + (receivers (rudel-subscribed-clients-not-self + this document))) + + ;; Relay change notification to other clients. We use + ;; TRANSFORMED before the byte -> char conversion which is what + ;; the receivers expect. + (with-slots (user-id) user + (with-slots (owner-id (doc-id :id)) document + ;; Construct and send messages to all receivers individually + ;; since the contents of the messages depends on the state + ;; of the jupiter context associated the respective + ;; receiver. + (dolist (receiver receivers) + + ;; Find the jupiter context for RECEIVER and use its + ;; revision information. + (let ((context (rudel-find-context server receiver document))) + ;; Construct and send one message. + (with-slots (local-revision remote-revision) context + (apply #'rudel-send + receiver + "obby_document" + (format "%x %x" owner-id doc-id) + "record" + (format "%x" user-id) + (format "%x" local-revision) + (format "%x" remote-revision) + (rudel-operation->message transformed))) + + ;; Submit the operation to the jupiter context. + (jupiter-local-operation context transformed))))) + + ;; Incorporate change into DOCUMENT (the server-side + ;; document). We have to convert bytes -> chars before we can do + ;; this. + (with-slots (buffer) document + (rudel-obby-byte->char transformed buffer)) + + (rudel-remote-operation document user transformed))) + ) + +(defmethod rudel-subscribed-clients-not-self ((this rudel-obby-client) + document) + "Return a list of clients subscribed to DOCUMENT excluding THIS." + (with-slots (clients) (oref this :server) + (with-slots (subscribed) document + (remove-if + (lambda (client) + (with-slots (user) client + (or (eq client this) + (not (memq user subscribed))))) + clients))) + ) + + +;;; Class rudel-obby-server +;; + +(defclass rudel-obby-server (rudel-server-session + rudel-socket-owner) + ((clients :initarg :clients + :type list + :initform nil + :documentation + "") + (next-client-id :initarg :next-client-id + :type integer + :initform 1 + :documentation + "") + (next-user-id :initarg :next-user-id + :type integer + :initform 1 + :documentation + "") + (contexts :initarg :contexts + :type hash-table + :documentation + "")) + "Class rudel-obby-server ") + +(defmethod initialize-instance ((this rudel-obby-server) &rest slots) + "" + (when (next-method-p) + (call-next-method)) + + (with-slots (contexts) this + (setq contexts (make-hash-table :test 'equal)))) + +(defmethod rudel-end ((this rudel-obby-server)) + "" + (rudel-disconnect this)) + +(defmethod rudel-broadcast ((this rudel-obby-server) + receivers name &rest arguments) + "Send a message of type NAME with arguments ARGUMENTS to RECEIVERS. + +RECEIVERS can be a object derived from rudel-obby-client, a list +of such objects or a list with car 'exclude and cdr a list of +such objects derived from rudel-obby-client." + ;; Construct list of receivers. + (let ((receiver-list + (cond + ;; If RECEIVERS is nil, the message should be broadcast to + ;; all clients. + ((null receivers) (oref this :clients)) + ;; If RECEIVERS is a (non-empty) list of rudel-obby-client + ;; (or derived) objects, treat it as a list of receivers. + ((and (listp receivers) + (rudel-obby-client-child-p (car receivers))) + receivers) + ;; If RECEIVERS is a (non-empty) list with cdr equal to + ;; 'exclude treat it as a list of receivers to exclude. + ((and (listp receivers) + (eq (car receivers) 'exclude)) + (with-slots (clients) this + (set-difference clients (cdr receivers) + :key #'rudel-id))) + ;; If RECEIVERS is a single rudel-obby-client (or derived) + ;; object, send the message to that client. + ((rudel-obby-client-child-p receivers) + (list receivers)) + ;; + (t (signal 'wrong-type-argument (type-of receivers)))))) + + ;; Send message to receivers. + (dolist (receiver receiver-list) + (apply #'rudel-send receiver name arguments))) + ) + +(defmethod rudel-make-user ((this rudel-obby-server) + name client-id color encryption) + "" + (with-slots (next-user-id) this + (let ((user (rudel-obby-user name + :color color + :client-id client-id + :user-id next-user-id + :connected t + :encryption encryption))) + (incf next-user-id) + user)) + ) + +(defmethod rudel-check-username-and-color ((this rudel-obby-server) + username color) + "Check whether USERNAME and COLOR are valid. +USERNAME must not be empty and must not be used by another +user. COLOR has to be sufficiently different from used colors." + (cond + ;; The empty user name is not allowed + ((string= username "") + rudel-obby-error-username-invalid) + + ;; Make sure the user name is not already in use. + ((rudel-find-user this username + #'string= #'object-name-string) + rudel-obby-error-username-in-use) + + ;; Make sure the color is sufficiently dissimilar to all used + ;; colors. + ((rudel-find-user this color + (lambda (left right) + (< (color-distance left right) 20000)) ;; TODO constant + #'rudel-color) + rudel-obby-error-color-in-use)) + ) + +(defmethod rudel-add-client ((this rudel-obby-server) + client-socket) + "" + (with-slots (next-client-id clients) this + (let ((client (rudel-obby-client (process-name client-socket) + :server this + :socket client-socket + :id next-client-id + :encryption nil))) + (push client clients)) + (incf next-client-id)) + ) + +(defmethod rudel-remove-client ((this rudel-obby-server) + client) + "" + (with-slots ((client-id :id) user) client + ;; Broadcast the part event to all remaining clients. + (rudel-broadcast this (list 'exclude client) + "net6_client_part" + (format "%x" client-id)) + + ;; If the client has an associated user object, set the status of + ;; the user object to offline. + (when user + ;; Set slot value. + (with-slots (connected) user + (setq connected nil)) + + ;; Run change hook. + (object-run-hook-with-args user 'change-hook))) + + (object-remove-from-list this :clients client) + ) + +(defmethod rudel-find-context ((this rudel-obby-server) client document) + "Return the jupiter context associated to (CLIENT DOCUMENT) in THIS." + (with-slots (contexts) this + (gethash (rudel-obby-context-key client document) contexts))) + +(defmethod rudel-add-context ((this rudel-obby-server) client document) + "Add a jupiter context for (CLIENT DOCUMENT) to THIS." + (with-slots (contexts) this + (with-slots ((client-id :id)) client + (with-slots ((doc-name :object-name)) document + (puthash + (rudel-obby-context-key client document) + (jupiter-context (format "%d-%s" client-id doc-name)) + contexts)))) + ) + +(defmethod rudel-remove-context ((this rudel-obby-server) client document) + "Remove the jupiter context associated to (CLIENT DOCUMENT) from THIS." + (with-slots (contexts) this + (remhash + (rudel-obby-context-key client document) + contexts))) + +(defun rudel-obby-context-key (client document) + "Generate hash key based on CLIENT and DOCUMENT." + (with-slots ((client-id :id)) client + (with-slots ((doc-id :id)) document + (list client-id doc-id)))) + +(defmethod object-print ((this rudel-obby-server) &rest strings) + "Print THIS with number of clients." + (with-slots (clients) this + (apply #'call-next-method + this + (format " clients: %d" + (length clients)) + strings)) + ) + +(provide 'rudel-obby-server) +;;; rudel-obby-server.el ends here diff --git a/emacs.d/lisp/rudel/obby/rudel-obby-state.el b/emacs.d/lisp/rudel/obby/rudel-obby-state.el new file mode 100644 index 0000000..a190967 --- /dev/null +++ b/emacs.d/lisp/rudel/obby/rudel-obby-state.el @@ -0,0 +1,169 @@ +;;; rudel-obby-state.el --- Base class for states used in the obby backend +;; +;; Copyright (C) 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Rudel, obby, state machine +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; This file contains a base class for finite state machine states +;; used in the obby backend. + + +;;; History: +;; +;; 0.1 - Initial revision + + +;;; Code: +;; + +(require 'eieio) + +(require 'rudel-util) +(require 'rudel-state-machine) + +(require 'rudel-obby-util) + + +;;; Class rudel-obby-state +;; + +(defclass rudel-obby-state (rudel-state) + ((connection :initarg :connection + :type rudel-obby-socket-owner + :documentation + "Connection object that uses the state.")) + "Base class for state classes used in the obby backend." + :abstract t) + +(defmethod rudel-enter ((this rudel-obby-state)) + "Default behavior is doing nothing when entering a state." + nil) + +(defmethod rudel-leave ((this rudel-obby-state)) + "Default behavior is doing nothing when leaving a state.") + +(defmethod rudel-accept ((this rudel-obby-state) message) + "Dispatch to appropriate handler based on MESSAGE. +Display a warning if no such handler is found." + ;; Try to dispatch to the correct message handler. If there is none, + ;; warn. + (let ((name (car message)) + (arguments (cdr message))) + (condition-case error + ;; Try to dispatch + (rudel-dispatch this "rudel-obby/" name arguments) + ;; Warn if we failed to locate or execute the method. Return nil + ;; in this case, so we remain in the current state. + (rudel-dispatch-error + (progn + (display-warning + '(rudel obby) + (format "%s: no method (%s: %s): `%s/%s'; arguments: %s" + (object-print this) (car error) (cdr error) + "rudel-obby" name arguments) + :debug) + nil)))) + ) + +(defmethod rudel-send ((this rudel-obby-state) &rest args) + "Send ARGS through the connection associated with THIS." + (with-slots (connection) this + (apply #'rudel-send connection args))) + + +;;; Class rudel-obby-client-connection-state +;; + +(defclass rudel-obby-client-connection-state (rudel-obby-state) + () + "Base class for state classes used by obby client connections." + :abstract t) + +(defmethod rudel-obby/net6_ping ((this rudel-obby-client-connection-state)) + "Handle net6 'ping' message." + (rudel-send this "net6_pong") + nil) + + +;;; Class rudel-obby-server-connection-state +;; + +(defclass rudel-obby-server-connection-state (rudel-obby-state) + () + "Base class for server connection states." + :abstract t) + +(defmethod rudel-broadcast ((this rudel-obby-server-connection-state) + receivers name &rest arguments) + "Broadcast message NAME with arguments ARGUMENTS to RECEIVERS." + (with-slots (connection) this + (apply #'rudel-broadcast connection receivers name arguments))) + + +;;; Class rudel-obby-document-handler +;; + +(defclass rudel-obby-document-handler () + () + "Mixin class that provides ability to process submessages of + obby 'document' messages.") + +(defmethod rudel-obby/obby_document + ((this rudel-obby-document-handler) doc-id action &rest arguments) + "Handle obby 'document' message family." + ;; Try to dispatch to the correct message handler. If there is none, + ;; warn. + (with-parsed-arguments ((doc-id document-id)) + ;; Locate the document based on owner id and document id. + (let ((document (with-slots (connection) this + (with-slots (session) connection + (rudel-find-document session doc-id + #'equal #'rudel-both-ids))))) + (if document + (condition-case error + ;; Try to dispatch + (rudel-dispatch this "rudel-obby/obby_document/" action + (cons document arguments)) + ;; Warn if we failed to locate or execute the + ;; method. Return nil in this case, so we remain in the + ;; current state. + (rudel-dispatch-error + (progn + (display-warning + '(rudel obby) + (format "%s: no method (%s: %s): `%s:%s'; arguments: %s" + (object-print this) (car error) (cdr error) + "rudel-obby/obby_document/" action arguments) + :debug) + nil))) + ;; If we did not find the document, warn. + (progn + (display-warning + '(rudel obby) + (format "Document not found: %s" doc-id) + :debug) + nil)))) + ) + +(provide 'rudel-obby-state) +;;; rudel-obby-state.el ends here diff --git a/emacs.d/lisp/rudel/obby/rudel-obby-util.el b/emacs.d/lisp/rudel/obby/rudel-obby-util.el new file mode 100644 index 0000000..faefe70 --- /dev/null +++ b/emacs.d/lisp/rudel/obby/rudel-obby-util.el @@ -0,0 +1,314 @@ +;;; rudel-obby-util.el --- Miscellaneous functions for the Rudel obby backend +;; +;; Copyright (C) 2008, 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Rudel, obby, backend, miscellaneous +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; + + +;;; History: +;; +;; 0.1 - Initial revision. + + +;;; Code: +;; + +(eval-when-compile + (require 'cl)) + +(require 'eieio) + +(require 'rudel) +(require 'rudel-util) + +(require 'jupiter) + + +;;; Class rudel-obby-socket-owner +;; + +(defclass rudel-obby-socket-owner (rudel-socket-owner) + ((buffer :initarg :buffer + :type (or null string) + :initform nil + :documentation + "Stores message fragments until complete messages can +be assembled.")) + "This class adds functions for sending and receiving obby +messages to the base class rudel-socket-owner.") + +(defmethod rudel-send ((this rudel-obby-socket-owner) + name &rest arguments) + "Send obby message NAME with arguments ARGUMENTS through the socket associated to THIS." + (with-slots (socket) this + (rudel-obby-send socket name arguments))) + +(defmethod rudel-receive ((this rudel-obby-socket-owner) data) + "Reassemble lines in DATA received on the socket associated with THIS and call message handler." + ;; Assemble fragmented lines. + (with-slots (buffer) this + (rudel-assemble-line-fragments data buffer)) + + ;; Process all available lines. + (rudel-loop-lines data line + ;; `rudel-message' has to dispatch message to an appropriate + ;; handler. + (let ((message (rudel-obby-parse-message line))) + (rudel-message this message))) + ) + +(defgeneric rudel-message ((this rudel-obby-socket-owner) message) + "Called when a message arrives. +Should be implemented in derived classes.") + + +;;; Message serialization +;; + +(defgeneric rudel-operation->message ((this jupiter-operation)) + "Generate a list obby message components from THIS operation.") + +(defmethod rudel-operation->message ((this jupiter-insert)) + "Serialize THIS insert operation." + (with-slots (from data) this + (list "ins" (format "%x" from) data))) + +(defmethod rudel-operation->message ((this jupiter-delete)) + "Serialize THIS delete operation." + (with-slots (from length) this + (list "del" (format "%x" from) (format "%x" length)))) + +(defmethod rudel-operation->message ((this jupiter-compound)) + "Serialize THIS compound operation." + (with-slots (children) this + (apply #'append + (list "split" ) + (mapcar #'rudel-operation->message children)))) + +(defmethod rudel-operation->message ((this jupiter-nop)) + "Serialize THIS nop operation." + (list "nop")) + +(defun rudel-message->operation (message local-revision remote-revision) + "Construct an operation object from MESSAGE and LOCAL-REVISION and REMOTE-REVISION. +LOCAL-REVISION and REMOTE-REVISION are only used in the +construction of the name of the new operation. " + (let ((type (car message))) + (cond + + ;; Insert operation + ((string= type "ins") + (let ((position-numeric (string-to-number (nth 1 message) 16)) + (data (nth 2 message))) + (jupiter-insert + (format "insert-%d-%d" + remote-revision local-revision) + :from position-numeric + :data data))) + + ;; Delete operation + ((string= type "del") + (let ((position-numeric (string-to-number (nth 1 message) 16)) + (length-numeric (string-to-number (nth 2 message) 16))) + (jupiter-delete + (format "delete-%d-%d" + remote-revision local-revision) + :from position-numeric + :to (+ position-numeric length-numeric)))) + + ;; Compound operation + ((string= type "split") + (let* ((rest (cdr message)) + (offset (position-if + (lambda (item) + (member* item '("ins" "del" "nop") + :test #'string=)) + rest + :start 1)) + (first (subseq rest 0 offset)) + (second (subseq rest offset))) + (jupiter-compound + (format "compound-%d-%d" + remote-revision local-revision) + :children + (list (rudel-message->operation + first local-revision remote-revision) + (rudel-message->operation + second local-revision remote-revision))))) + + ;; No operation + ((string= type "nop") + (jupiter-nop + (format "nop-%d-%d" + remote-revision local-revision))) + + ;; Unknown operation type + (t (error "Unknown document record type: `%s'" type)))) + ) + + +;;; Character <-> byte position conversion +;; + +(defgeneric rudel-obby-char->byte ((this jupiter-operation) buffer) + "Convert character positions and lengths in THIS to bytes.") + +(defmethod rudel-obby-char->byte ((this jupiter-insert) buffer) + "Convert character positions and lengths in THIS insert to bytes." + (with-slots (from) this + (with-current-buffer buffer + (setq from (- (position-bytes (+ from 1)) 1))))) + +(defmethod rudel-obby-char->byte ((this jupiter-delete) buffer) + "Convert character positions and lengths in THIS delete to bytes." + (with-slots (from to length) this + (let ((old-from (+ from 1)) + (old-to (+ to 1))) + (with-current-buffer buffer + (destructuring-bind (change-from change-to string) + rudel-buffer-change-workaround-data + (setq from (- (position-bytes old-from) 1) + length (string-bytes + (substring string + (- old-from change-from) + (- old-to change-from)))))))) + ) + +(defmethod rudel-obby-char->byte ((this jupiter-compound) buffer) + "Convert character positions and lengths in THIS compound to bytes.." + (with-slots (children) this + (mapc + (lambda (child) + (rudel-obby-char->byte child buffer)) + children)) + ) + +(defmethod rudel-obby-char->byte ((this jupiter-nop) buffer) + "Nothing to convert if THIS is a nop.") + +(defgeneric rudel-obby-byte->char ((this jupiter-operation) buffer) + "Convert byte positions and lengths in THIS to character positions.") + +(defmethod rudel-obby-byte->char ((this jupiter-insert) buffer) + "Convert byte positions and lengths in THIS insert to character positions." + (with-slots (from) this + (with-current-buffer buffer + (setq from (- (byte-to-position (+ from 1)) 1))))) + +(defmethod rudel-obby-byte->char ((this jupiter-delete) buffer) + "Convert byte positions and lengths in THIS delete to character positions." + (with-slots (from to length) this + (let ((old-from from) + (old-length length)) + (with-current-buffer buffer + (setq from (- (byte-to-position (+ old-from 1)) 1) + to (- (byte-to-position (+ old-from old-length 1)) 1))))) + ) + +(defmethod rudel-obby-byte->char ((this jupiter-compound) buffer) + "Convert byte positions and lengths in THIS compound to character positions." + (with-slots (children) this + (mapc + (lambda (child) + (rudel-obby-byte->char child buffer)) + children)) + ) + +(defmethod rudel-obby-byte->char ((this jupiter-nop) buffer) + "Nothing to convert if THIS is a nop.") + + +;;; Miscellaneous functions +;; + +(defun rudel-obby-dispatch (object name arguments &optional prefix) + "Call method starting with PREFIX and ending in NAME of OBJECT with ARGUMENTS. +When PREFIX is not specified, \"rudel-obby/\" is used." + ;; Default prefix. + (unless prefix + (setq prefix "rudel-obby/")) + + ;; Construct a matching symbol. + (let ((method (intern-soft (concat prefix name)))) + ;; If we found a suitable method, run it; Otherwise warn and do + ;; nothing. + (unless (and method + (condition-case error + ;; Try to call METHOD. If successful, always + ;; return t. + (progn + (apply method object arguments) + t) + ;; Warn only when the condition is + ;; 'no-method-definition' and refers to METHOD, + ;; otherwise continue unwinding. + (no-method-definition + (if (eq method (cadr error)) + nil + (signal (car error) (cdr error)))))) + (display-warning + '(rudel obby) + (format "%s: in context `%s': no method: `%s'; arguments: %s" + (object-name-string object) prefix name arguments) + :debug))) + ) + +(defmacro with-parsed-arguments (specs &rest forms) + "Execute FORMS with variable bindings according to SPECS. +SPECS is structured as follows: +SPECS ::= (BINDING*) +BINDING ::= (VAR TYPE) +VAR is a symbol and TYPE is one of number, color, document-id and +coding-system." + (declare (indent 1) + (debug (listp &rest form))) + (let ((bindings + (mapcar + (lambda (spec) + (destructuring-bind (var type) spec + (list var + (case type + ;; Number + (number + `(string-to-number ,var 16)) + ;; Color + (color + `(rudel-obby-parse-color ,var)) + ;; Document Id + (document-id + `(mapcar + (lambda (string) + (string-to-number string 16)) + (split-string ,var " " t))) + ;; Coding System + (coding-system + `(rudel-get-coding-system (downcase ,var))))))) + specs))) + `(let (,@bindings) + ,@forms)) + ) + +(provide 'rudel-obby-util) +;;; rudel-obby-util.el ends here diff --git a/emacs.d/lisp/rudel/obby/rudel-obby.el b/emacs.d/lisp/rudel/obby/rudel-obby.el new file mode 100644 index 0000000..a09c0d3 --- /dev/null +++ b/emacs.d/lisp/rudel/obby/rudel-obby.el @@ -0,0 +1,488 @@ +;;; rudel-obby.el --- An obby backend for Rudel +;; +;; Copyright (C) 2008, 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Rudel, obby, backend, implementation +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; This file contains a Rudel protocol backend, which implements the +;; obby protocol (used by the Gobby collaborative editor until version +;; 0.5). + + +;;; History: +;; +;; 0.2 - Refactored client and server to employ state machine. +;; +;; 0.1 - Initial revision. + + +;;; Code: +;; + +(eval-when-compile + (require 'cl)) + +(require 'eieio) + +(require 'rudel) +(require 'rudel-backend) +(require 'rudel-protocol) +(require 'rudel-util) +(require 'rudel-icons) +(require 'rudel-compat) ;; for `read-color' replacement + + +;;; Constants +;; + +(defconst rudel-obby-version '(0 2) + "Version of the obby backend for Rudel.") + +(defconst rudel-obby-protocol-version 8 + "Version of the obby protocol this library understands.") + +(defvar rudel-obby-long-message-threshold 32768 + "Threshold for message size, above which messages are sent in +multiple chunks.") + +(defvar rudel-obby-long-message-chunk-size 16384 + "Chunk size used, when chunking long messages.") + + +;;; Class rudel-obby-backend +;; + +;;;###autoload +(defclass rudel-obby-backend (rudel-protocol-backend) + ((capabilities :initform '(join host + change-color + track-subscriptions))) + "Main class of the Rudel obby backend. Creates obby client +connections and creates obby servers.") + +(defmethod initialize-instance ((this rudel-obby-backend) &rest slots) + "Initialize slots of THIS with SLOTS." + (when (next-method-p) + (call-next-method)) + + (oset this :version rudel-obby-version)) + +(defmethod rudel-ask-connect-info ((this rudel-obby-backend) &optional info) + "Ask user for the information required to connect to an obby server." + ;; Read server host and port. + (let ((host (or (and info (plist-get info :host)) + (read-string "Server: "))) + (port (or (and info (plist-get info :port)) + (read-number "Port: " 6522))) + ;; Read desired username and color + (username (or (and info (plist-get info :username)) + (read-string "Username: " user-login-name))) + (color (or (and info (plist-get info :color)) + (read-color "Color: " t))) + (encryption (if (and info (member :encryption info)) + (plist-get info :encryption) + (y-or-n-p "Use encryption? "))) + (global-password (if (and info (member :global-password info)) + (plist-get info :global-password) + (read-string "Global password: " ""))) + (user-password (if (and info (member :user-password info)) + (plist-get info :user-password) + (read-string "User password: " "")))) + (append (list :host host + :port port + :username username + :color color + :encryption encryption + :global-password (unless (string= global-password "") + global-password) + :user-password (unless (string= user-password "") + user-password)) + info)) + ) + +(defmethod rudel-connect ((this rudel-obby-backend) info) + "Connect to an obby server using the information INFO. +Return the connection object." + ;; Before we start, load the client functionality. + (require 'rudel-obby-client) + + ;; Create the network process + (let* ((session (plist-get info :session)) + (host (plist-get info :host)) + (port (plist-get info :port)) + (encryption (plist-get info :encryption)) + ;; Create the network process + (socket (funcall + (if encryption + (progn + (require 'rudel-tls) + #'rudel-tls-make-process) + #'make-network-process) + :name host + :host host + :service port + ;; Install connection filter to redirect data to + ;; the connection object + :filter #'rudel-filter-dispatch + ;; Install connection sentinel to redirect state + ;; changes to the connection object + :sentinel #'rudel-sentinel-dispatch + ;; Do not start receiving immediately since the + ;; filter function is not yet setup properly. + :stop t)) + (connection (rudel-obby-connection + host + :session session + :socket socket + :info info))) + + ;; Now start receiving and wait until the basic session setup is + ;; complete. + (continue-process socket) + + ;; Wait for the connection to reach one of the states idle, + ;; join-failed and they-finalized. + (condition-case error + (lexical-let ((reporter (make-progress-reporter "Joining "))) + (flet ((display-progress (state) + (cond + ;; For all states, just spin. + ((consp state) + (progress-reporter-force-update + reporter nil (format "Joining (%s)" (car state)))) + + ;; Done + (t + (progress-reporter-force-update reporter nil "Joining ") + (progress-reporter-done reporter))))) + + (rudel-state-wait connection + '(idle) '(join-failed they-finalized) + #'display-progress))) + + (rudel-entered-error-state + (destructuring-bind (symbol . state) (cdr error) + (if (eq (rudel-find-state connection 'join-failed) state) + (with-slots (error-symbol error-data) state + (signal 'rudel-join-error + (append (list error-symbol) error-data))) + (signal 'rudel-join-error nil))))) + + ;; The connection is now usable; return it. + connection) + ) + +(defmethod rudel-ask-host-info ((this rudel-obby-backend)) + "Ask user for information required to host an obby session." + (let ((port (read-number "Port: " 6522))) + (list :port port))) + +(defmethod rudel-host ((this rudel-obby-backend) info) + "Host an obby session using the information INFO. +Return the created server." + ;; Before we start, we load the server functionality. + (require 'rudel-obby-server) + + ;; Create the network process. + (let* ((port (plist-get info :port)) + ;; Make a server socket + (socket (make-network-process + :name "obby-server" + :host "0.0.0.0" + :service port + :server t + :filter #'rudel-filter-dispatch + :sentinel #'rudel-sentinel-dispatch + ;; + :log + (lambda (server-process client-process message) + (let ((server (rudel-process-object server-process))) + (rudel-add-client server client-process))))) + ;; Construct server object. + (server (rudel-obby-server "obby-server" + :backend this + :socket socket))) + + ;; Return the constructed server. + server) + ) + +(defmethod rudel-make-document ((this rudel-obby-backend) + name session) + "Make a new document in SESSION named NAME. +Return the new document." + ;; Find an unused document id and create a document with that id. + (let ((id (rudel-available-document-id this session))) + (with-slots (user-id) (oref session :self) + (rudel-obby-document name + :session session + :id id + :owner-id user-id + :suffix 1))) + ) + +(defmethod rudel-available-document-id ((this rudel-obby-backend) + session) + "Return a document id, which is not in use in SESSION." + ;; Look through some candidates until an unused id is hit. + (let* ((used-ids (with-slots (documents) session + (mapcar 'rudel-id documents))) + (test-ids (number-sequence 0 (length used-ids)))) + (car (sort (set-difference test-ids used-ids) '<))) + ) + + +;;; Class rudel-obby-user +;; + +(defclass rudel-obby-user (rudel-user) + ((client-id :initarg :client-id + :type (or null integer) ;; We allow nil instead of making + :accessor rudel-client-id ;; the slot unbound, to be able to + :initform nil ;; search with test `rudel-client-id + :documentation ;; without headaches + "Id of the client connection, which the user used to log in. +The value is an integer, if the user is connected, and nil +otherwise.") + (user-id :initarg :user-id + :type integer + :accessor rudel-id + :documentation + "") + (connected :initarg :connected + :type boolean + :accessor rudel-connected + :documentation + "") + (encryption :initarg :encryption ;; TODO maybe we should use unbound when the user is not connected + :type boolean + :documentation + "")) + "Class rudel-obby-user ") + +(defmethod eieio-speedbar-description ((this rudel-obby-user)) + "Provide a speedbar description for THIS." + (let ((connected (oref this :connected)) + (encryption (if (slot-boundp this :encryption) + (oref this :encryption) + nil))) + (format "User %s (%s, %s)" (object-name-string this) + (if connected "Online" "Offline") + (if encryption "Encryption" "Plain"))) + ) + +(defmethod eieio-speedbar-object-buttonname ((this rudel-obby-user)) + "Return a string to use as a speedbar button for THIS." + (rudel-display-string this)) + +(defmethod rudel-display-string ((this rudel-obby-user) + &optional use-images align) + "Return a textual representation of THIS for user interface stuff." + (with-slots (connected color) this + (let ((encryption (and (slot-boundp this :encryption) + (oref this :encryption))) + (name-string (call-next-method))) + (concat + ;; Name bit + (cond + ((numberp align) (format (format "%-%ds" align) name-string)) + ((eq align t) (format "%-12s" name-string)) + (t name-string)) + + ;; Connection status bit + (apply + #'propertize + (if connected "c" "-") + 'help-echo (format (if connected + "%s is connected" + "%s is not connected") + name-string) + 'face (list :background color) + (when use-images + (list 'display (if connected + rudel-icon-connected + rudel-icon-disconnected)))) + + ;; Encryption bit + (apply + #'propertize + (if encryption "e" "-") + 'help-echo (format (if encryption + "%s's connection is encrypted" + "%s's connection is not encrypted") + name-string) + 'face (list :background color) + (when use-images + (list 'display (if encryption + rudel-icon-encrypted + rudel-icon-plaintext))))))) + ) + + +;;; Class rudel-obby-document +;; + +(defclass rudel-obby-document (rudel-document) + ((id :initarg :id + :type integer + :accessor rudel-id + :documentation + "The id of this document. +The id has to be unique only with respect to the other documents +owned by the owner.") + (owner-id :initarg :owner-id + :type integer + :documentation + "") + (suffix :initarg :suffix + :type integer + :documentation + "A counter used to distinguish identically named +documents.")) + "Objects of the class rudel-obby-document represent shared +documents in obby sessions.") + +(defmethod rudel-both-ids ((this rudel-obby-document)) + "Return a list consisting of document and owner id of THIS document." + (with-slots ((doc-id :id) owner-id) this + (list owner-id doc-id))) + +(defmethod rudel-unique-name ((this rudel-obby-document)) + "Generate a unique name for THIS based on the name and the suffix." + (with-slots (suffix) this + (concat (when (next-method-p) + (call-next-method)) + (when (> suffix 1) + (format "<%d>" suffix)))) + ) + +(defmethod eieio-speedbar-description ((this rudel-obby-document)) + "Construct a description for from the name of document object THIS." + (format "Document %s" (object-name-string this))) + +(defmethod eieio-speedbar-object-buttonname ((this rudel-obby-document)) + "Return a string to use as a speedbar button for OBJECT." + (with-slots (subscribed) this + (format "%-12s %s" (object-name-string this) + (if subscribed "s" "-"))) + ) + + +;;; Obby message functions +;; + +(defun rudel-obby-replace-in-string (string replacements) + "Replace elements of REPLACEMENTS in STRING. +REPLACEMENTS is a list of conses whose car is the pattern and +whose cdr is the replacement for the pattern." + (let ((result string)) + (dolist (replacement replacements) + (let ((from (car replacement)) + (to (cdr replacement))) + (setq result (replace-regexp-in-string + from to result nil t)))) + result) + ) + +(defun rudel-obby-escape-string (string) + "Replace meta characters in STRING with their escape sequences." + (rudel-obby-replace-in-string + string + '(("\\\\" . "\\b") ("\n" . "\\n") (":" . "\\d"))) + ) + +(defun rudel-obby-unescape-string (string) + "Replace escaped versions of obby meta characters in STRING with the actual meta characters." + (rudel-obby-replace-in-string + string + '(("\\\\n" . "\n") ("\\\\d" . ":") ("\\\\b" . "\\"))) + ) + +(defun rudel-obby-parse-color (color) + "Parse the obby color string COLOR into an Emacs color." + (let* ((color-numeric (string-to-number color 16)) + (color-string (format "#%04X%04X%04X" + (lsh (logand #xff0000 color-numeric) -08) + (lsh (logand #x00ff00 color-numeric) -00) + (lsh (logand #x0000ff color-numeric) 08)))) + color-string) + ) + +(defun rudel-obby-format-color (color) + "Format the Emacs color COLOR as obby color string." + (multiple-value-bind (red green blue) (color-values color) + (format "%02x%02x%02x" (lsh red -8) (lsh green -8) (lsh blue -8)))) + +(defun rudel-obby-assemble-message (name &rest arguments) + "" + (concat (mapconcat + (lambda (part) + (if (and (not (null part)) (stringp part)) + (rudel-obby-escape-string part) + part)) + (cons name arguments) ":") + "\n") + ) + +(defun rudel-obby-parse-message (message) + "Split MESSAGE at `:' and unescape resulting parts. + +The terminating `\n' should be removed from MESSAGE before +calling this function." + (mapcar #'rudel-obby-unescape-string (split-string message ":"))) + +(defun rudel-obby-send (socket name arguments) + "Send an obby message NAME with arguments ARGUMENTS through SOCKET." + ;; First, assemble the message string. + (let ((message (apply #'rudel-obby-assemble-message + name arguments))) + (if (>= (length message) rudel-obby-long-message-threshold) + ;; For huge messages, chunk the message data and transmit the + ;; chunks + (let ((total (/ (length message) + rudel-obby-long-message-chunk-size)) + (current 0) + (reporter (make-progress-reporter "Sending data " 0.0 1.0))) + (rudel-loop-chunks message chunk rudel-obby-long-message-chunk-size + (progress-reporter-update reporter (/ (float current) total)) + (process-send-string socket chunk) + (incf current)) + (progress-reporter-done reporter)) + ;; Send small messages in one chunk + (process-send-string socket message))) + ) + + +;;; Autoloading +;; + +;;;###autoload +(rudel-add-backend (rudel-backend-get-factory 'protocol) + 'obby 'rudel-obby-backend) + +;;;###autoload +(eval-after-load 'rudel-zeroconf + '(rudel-zeroconf-register-service "_lobby._tcp" 'obby)) + +(provide 'rudel-obby) +;;; rudel-obby.el ends here diff --git a/emacs.d/lisp/rudel/rudel-backend.el b/emacs.d/lisp/rudel/rudel-backend.el new file mode 100644 index 0000000..79cfef6 --- /dev/null +++ b/emacs.d/lisp/rudel/rudel-backend.el @@ -0,0 +1,305 @@ +;;; rudel-backend.el --- A generic backend management mechanism for Rudel +;; +;; Copyright (C) 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Rudel, backend, factory +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; This file contains a generic mechanism that handles registration, +;; query and instantiation of Rudel backends for any number of +;; functional categories. +;; +;; The class and collaboration design is as follows: for each +;; category, which is identified by a symbol, there is a factory +;; object (an instance of `rudel-backend-factory') that is responsible +;; for creating backend objects of the category. Examples of +;; categories are 'transport', 'protocol' and 'session-initiation'. +;; In addition to creating backend object, factories also allow +;; querying backends based on desired capabilities and load backend +;; implementations only when required. + + +;;; History: +;; +;; 0.1 - Initial revision + + +;;; Code: +;; + +(eval-when-compile + (require 'cl)) + +(require 'eieio) + + +;;; Class rudel-backend +;; + +(defclass rudel-backend () + ((version :initarg :version + :type list + :documentation + "A list of the form (MAJOR MINOR [MICRO +WHATEVER*]) describing the version of the backend.") + (capabilities :initarg :capabilities + :type list + :initform nil + :documentation + "A list of symbols, or lists whose car is a +symbol, that each describe one capability of the backend.")) + "Base class for backend classes." + :abstract t) + +(defmethod rudel-capable-of-p ((this rudel-backend) capability) + "Return t if the backend THIS is capable of CAPABILITY." + (with-slots (capabilities) this + (member capability capabilities))) + + +;;; Class rudel-backend-factory +;; + +(defclass rudel-backend-factory () + ((backends :initarg :backends + :type hash-table + :documentation + "Mapping of symbolic names to classes (prior to +instantiation) or objects (after instantiation) for all backends +known to the factory object.") + (factories :type hash-table + :allocation :class + :documentation + "Mapping of backend categories to responsible +factory objects.")) + "Factory class that holds an object for each known backend +category. Objects manage backend implementation for one backend +category each.") +(oset-default rudel-backend-factory factories + (make-hash-table :test #'eq)) + +(defmethod initialize-instance ((this rudel-backend-factory) &rest slots) + "Initialize slots of THIS with SLOTS." + (when (next-method-p) + (call-next-method)) + (oset this :backends (make-hash-table :test #'eq))) + +;;;###autoload +(defmethod rudel-get-factory :static ((this rudel-backend-factory) + category) + "Return the factory responsible for CATEGORY. +If there is no responsible factory, create one and return it." + (with-slots (factories) this + (or (gethash category factories) + (puthash category (rudel-backend-factory category) factories))) + ) + +;;;###autoload +(defmethod rudel-add-backend ((this rudel-backend-factory) + name class &optional replace) + "Add factory class CLASS with name NAME to THIS. +if REPLACE is non-nil, replace a registered implementation of the +same name." + (with-slots (backends) this + (when (or (not (gethash name backends)) + replace) + (puthash name class backends)))) + +(defmethod rudel-get-backend ((this rudel-backend-factory) name) + "Return backend object for name NAME or nil if there is none. +The returned backend is of the form (NAME . OBJECT). + +Backends are loaded, if necessary." + ;; Load all available backends + (rudel-load-backends this) + + ;; Find the backend and return it. + (with-slots (backends) this + (let ((backend (gethash name backends))) + (when backend + (cons name backend)))) + ) + +(defmethod rudel-all-backends ((this rudel-backend-factory)) + "Return a list of all backends registered with THIS. +Each list element is of the form (NAME . CLASS-OR-OBJECT)." + (let ((backend-list)) + (with-slots (backends) this + (maphash (lambda (name class) + (push (cons name class) backend-list)) + backends)) + backend-list) + ) + +(defmethod rudel-suitable-backends ((this rudel-backend-factory) predicate) + "Return a list of backends which satisfy PREDICATE. +Each list element is of the form (NAME . OBJECT). +Backends are loaded, if necessary." + ;; Load all available backends + (rudel-load-backends this) + + ;; Retrieve and return all backends, possibly filtering the list + ;; using PREDICATE. + (if predicate + (remove-if-not + (lambda (cell) + (and (object-p (cdr cell)) + (funcall predicate (cdr cell)))) + (rudel-all-backends this)) + (rudel-all-backends this)) + ) + +(defmethod rudel-load-backends ((this rudel-backend-factory)) + "Load backends in THIS factory if necessary. +Loading errors are not reported explicitly, but can be detected +by checking for backends that still are classes rather than +objects." + ;; Map lambda that loads unloaded backends over all backends. Store + ;; objects back after loading. + (with-slots (backends) this + (maphash + (lambda (name class) + (unless (object-p class) + (condition-case error + (puthash name (make-instance + class (symbol-name name)) backends) + (error (display-warning + '(rudel backend) + (format "Could not load backend `%s': %s" + name + (error-message-string error)) + :warning))))) + backends)) + ) + + +;;; High-level frontend functions +;; + +(defsubst rudel-backend-cons-p (cell) + "Check whether CELL is a cons of a backend name and object." + (and (consp cell) + (symbolp (car cell)) + (object-p (cdr cell)))) + +;;;###autoload +(defun rudel-backend-get (category name) + "A shortcut for getting backend NAME of category CATEGORY. +The returned backend is of the form (NAME . OBJECT)." + (rudel-get-backend (rudel-backend-get-factory category) name)) + +;;;###autoload +(defun rudel-backend-get-factory (category) + "A shortcut for getting the factory object for CATEGORY." + (rudel-get-factory rudel-backend-factory category)) + +(defun rudel-backend-suitable-backends (category predicate) + "Return backends from category CATEGORY that satisfy PREDICATE. +Each list element is of the form (NAME . OBJECT)." + (rudel-suitable-backends + (rudel-backend-get-factory category) + predicate)) + +(defun rudel-backend-choose (category &optional predicate) + "Choose a backend from CATEGORY satisfying PREDICATE automatically or by asking the user. +The returned backend is of the form (NAME . CLASS-OR-OBJECT)." + (let ((backends (rudel-backend-suitable-backends + category predicate))) + (unless backends + (error "No backends available")) + + (if (= (length backends) 1) + ;; If there is only one backend, we can choose that one right + ;; away displaying a message to avoid confusing the user. + (let ((backend (nth 0 backends))) + (message "Using backend `%s'" (symbol-name (car backend))) + (sit-for 0.5) + backend) + + ;; When we have more than one backend, we have to ask the user, + ;; which one she wants. + (require 'rudel-interactive) + (rudel-read-backend backends nil 'object))) + ) + + +;;; User interaction functions +;; + +(defun rudel-backend-dump (&optional load) + "Create display information about backends in a buffer. +If LOAD is non-nil, load all backends before display. This makes +available information available for the backends" + (interactive "p") + (save-excursion + ;; Setup a new buffer. + (set-buffer (get-buffer-create "*Rudel Backends*")) + (erase-buffer) + (set-window-buffer nil (current-buffer)) + (maphash + (lambda (category factory) + ;; Load backends if requested. + (unless (zerop load) + (rudel-load-backends factory)) + + ;; Insert header for this category. + (insert (propertize + (format "Category %s\n" category) + 'face 'bold)) + (insert (apply #'format + " %-20s %-6s %-7s %s\n" + (mapcar + (lambda (header) + (propertize header 'face 'italic)) + '("name" "loaded" "version" "capabilities")))) + + ;; Insert all backends provided by this factory. + (dolist (backend (rudel-all-backends factory)) + (insert (format " %-20s %-6s %-7s (%s)\n" + (propertize + (symbol-name (car backend)) + 'face 'font-lock-type-face) + (propertize + (prin1-to-string (object-p (cdr backend))) + 'face 'font-lock-variable-name-face) + (propertize + (if (object-p (cdr backend)) + (mapconcat #'prin1-to-string + (oref (cdr backend) :version) + ".") + "?") + 'face 'font-lock-constant-face) + (propertize + (if (object-p (cdr backend)) + (mapconcat #'prin1-to-string + (oref (cdr backend) :capabilities) + " ") + "?") + 'face 'font-lock-constant-face)))) + + ;; One empty line between backend categories. + (insert "\n")) + (oref rudel-backend-factory factories))) + ) + +(provide 'rudel-backend) +;;; rudel-backend.el ends here diff --git a/emacs.d/lisp/rudel/rudel-chat.el b/emacs.d/lisp/rudel/rudel-chat.el new file mode 100644 index 0000000..c7e992f --- /dev/null +++ b/emacs.d/lisp/rudel/rudel-chat.el @@ -0,0 +1,103 @@ +;;; rudel-chat.el --- Handling of chat messages +;; +;; Copyright (C) 2008, 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Rudel, chat, message +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; This file contains some functions that deal with incoming chat +;; messages. Backends that support receiving chat message should +;; dispatch them using `rudel-chat-dispatch-message'. Chat messages +;; will be processed in a customizable way from there. + + +;;; History: +;; +;; 0.1 - Initial revision. + + +;;; Code: +;; + + +;;; Customization +;; + +(defcustom rudel-chat-handler-function #'rudel-chat-handle-buffer + "A function that is called when chat messages arrive." + :group 'rudel + :type '(choice (const :tag "Display messages in the echo area" + rudel-chat-handle-message) + (const :tag "Log messages into a buffer" + rudel-chat-handle-buffer) + (function :tag "Other function")) + ) + + +;;; Variables and constants +;; + +(defconst rudel-chat-buffer-name "*rudel-chat-log*" + "Name of the buffer into which received chat message should be +inserted.") + + +;;; Interface functions +;; + +(defun rudel-chat-dispatch-message (sender message) + "Dispatch SENDER and MESSAGE to customizable handler function." + (funcall rudel-chat-handler-function sender message)) + + +;;; Handler functions +;; + +(defun rudel-chat-handle-message (sender text) + "Display SENDER and MESSAGE in the echo area." + (message "%s says: %s" + (rudel-chat-format-sender sender) + text)) + +(defun rudel-chat-handle-buffer (sender text) + "Insert SENDER and MESSAGE in a buffer." + (let ((buffer (or (get-buffer rudel-chat-buffer-name) + (pop-to-buffer rudel-chat-buffer-name)))) + (with-current-buffer buffer + (goto-char (point-min)) + (insert (format "%s: %s\n" + (rudel-chat-format-sender sender) + text)))) + ) + + +;;; Miscellaneous functions +;; + +(defun rudel-chat-format-sender (user) + "Format USER handling nil values." + (if user + (object-name-string user) + "")) + +(provide 'rudel-chat) +;;; rudel-chat.el ends here diff --git a/emacs.d/lisp/rudel/rudel-compat.el b/emacs.d/lisp/rudel/rudel-compat.el new file mode 100644 index 0000000..630496c --- /dev/null +++ b/emacs.d/lisp/rudel/rudel-compat.el @@ -0,0 +1,158 @@ +;;; rudel-compat.el --- Compatibility code for Rudel +;; +;; Copyright (C) 2009 Jan Moringen +;; Copyright (C) 2009 Phil Hagelberg +;; +;; Author: Jan Moringen +;; Phil Hagelberg +;; Keywords: rudel, compatibility +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; This file contains compatibility code required to make Rudel work +;; with different versions of Emacs. + + +;;; History: +;; +;; 0.1 - Initial revision + + +;;; Code: +;; + +(unless (fboundp 'read-color) + (defun read-color (prompt &rest ignored) + "Poor man's read color without completion. +You have to take care to only enter valid color names." + (read-string prompt))) + + +;;; Spinner Progress Reporter +;; + +(unless (functionp 'progress-reporter-spin) + (defvar progress-spinner-values ["-" "\\" "|" "/"]) + + (defsubst progress-reporter-update (reporter &optional value) + "Report progress of an operation in the echo area. + +The first parameter, REPORTER, should be the result of a call to +`make-progress-reporter'. For reporters for which the max value +is known, the second argument determines the actual progress of +operation; it must be between MIN-VALUE and MAX-VALUE as passed +to `make-progress-reporter'. + +However, if the change since last echo area update is too small +or not enough time has passed, then do nothing (see +`make-progress-reporter' for details). + +In this case, this function is very inexpensive, you need not +care how often you call it." + (if (progress-reporter-spinner-p reporter) + (progress-reporter-spin reporter) + (when (>= value (car reporter)) + (progress-reporter-do-update reporter value)))) + + (defun make-progress-reporter (message &optional min-value max-value + current-value min-change min-time) + "Return progress reporter object to be used with `progress-reporter-update'. + +MESSAGE is shown in the echo area. When at least 1% of operation +is complete, the exact percentage will be appended to the +MESSAGE. When you call `progress-reporter-done', word \"done\" +is printed after the MESSAGE. You can change MESSAGE of an +existing progress reporter with `progress-reporter-force-update'. + +If provided, MIN-VALUE and MAX-VALUE designate starting (0% +complete) and final (100% complete) states of operation. The +latter should be larger; if this is not the case, then simply +negate all values. Optional CURRENT-VALUE specifies the progress +by the moment you call this function. You should omit it or set +it to nil in most cases since it defaults to MIN-VALUE. + +Optional MIN-CHANGE determines the minimal change in percents to +report (default is 1%.) Optional MIN-TIME specifies the minimal +time before echo area updates (default is 0.2 seconds.) If +`float-time' function is not present, then time is not tracked +at all. If OS is not capable of measuring fractions of seconds, +then this parameter is effectively rounded up. + +If MIN-VALUE and MAX-VALUE are unknown, they may be omitted to +return a \"pulsing\" progress reporter." + (unless min-time + (setq min-time 0.2)) + (let ((reporter + (cons min-value ;; Force a call to `message' now + (vector (if (and (fboundp 'float-time) + (>= min-time 0.02)) + (float-time) nil) + (or min-value 0) + max-value + message + (if min-change (max (min min-change 50) 1) 1) + min-time)))) + (progress-reporter-update reporter (or current-value min-value)) + reporter)) + + (defun progress-reporter-force-update (reporter &optional value new-message) + "Report progress of an operation in the echo area unconditionally. + +First two parameters are the same as for +`progress-reporter-update'. Optional NEW-MESSAGE allows you to +change the displayed message." + (let ((parameters (cdr reporter))) + (when new-message + (aset parameters 3 new-message)) + (when (aref parameters 0) + (aset parameters 0 (float-time))) + (if (progress-reporter-spinner-p reporter) + (progress-reporter-spin reporter) + (progress-reporter-do-update reporter value)))) + + (defun progress-reporter-spinner-p (reporter) + "Return t if REPORTER has an unknown max value." + (null (aref (cdr reporter) 2))) + + (defun progress-reporter-spin (reporter) + "Advance indicator of spinning REPORTER." + (let* ((parameters (cdr reporter)) + (index (+ (aref parameters 1) 1))) + (aset parameters 1 index) + (let ((message-log-max nil)) ; No logging + (message "%s %s" + (aref progress-spinner-values (mod index 4)) + (aref parameters 3)))))) + +(unless (functionp 'string-match-p) + (defsubst string-match-p (regexp string &optional start) + "Same as `string-match' except this function does not change the match data" + (let ((inhibit-changing-match-data t)) + (string-match regexp string start)))) + +(defun rudel-get-coding-system (name) + (if (functionp 'coding-system-from-name) + (coding-system-from-name name) + ;; May need to try a little harder here for Emacs 22 depending on + ;; what kind of encoding names are given us. + (intern name))) + +(provide 'rudel-compat) +;;; rudel-compat.el ends here diff --git a/emacs.d/lisp/rudel/rudel-compile.el b/emacs.d/lisp/rudel/rudel-compile.el new file mode 100644 index 0000000..aeba765 --- /dev/null +++ b/emacs.d/lisp/rudel/rudel-compile.el @@ -0,0 +1,46 @@ +;;; rudel-compile.el --- Byte-compile Rudel +;; +;; Copyright (C) 2009 Phil Hagelberg +;; +;; Author: Phil Hagelberg +;; Keywords: Rudel, compile +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; Press M-x eval-buffer to byte-compile Rudel. + + +;;; History: +;; +;; 0.1 - Initial revision + + +;;; Code: +;; + +(let ((rudel-dir (file-name-directory + (or (buffer-file-name) load-file-name)))) + ;; Adjust load path for compilation. + (dolist (dir '("." "jupiter" "obby" "zeroconf")) + (let ((d (concat rudel-dir "/" dir))) + (add-to-list 'load-path d))) + + ;; Byte compile everything. + (byte-recompile-directory rudel-dir 0)) diff --git a/emacs.d/lisp/rudel/rudel-debug.el b/emacs.d/lisp/rudel/rudel-debug.el new file mode 100644 index 0000000..747bf68 --- /dev/null +++ b/emacs.d/lisp/rudel/rudel-debug.el @@ -0,0 +1,215 @@ +;;; rudel-debug.el --- Debugging functions for Rudel +;; +;; Copyright (C) 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Rudel, debugging +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 rudel. If not, see . + + +;;; Commentary: +;; +;; Debugging functions for Rudel. + + +;;; History: +;; +;; 0.1 - Initial revision. + + +;;; Code: +;; + +(require 'data-debug) +(require 'eieio-datadebug) + +(require 'rudel-util) + + +;;; Customization +;; + +(defgroup rudel-debug nil + "Customization options related to Rudel's debugging functions." + :group 'rudel) + +(defface rudel-debug-sent-data-face + '((default (:background "orange"))) + "Face used for sent data." + :group 'rudel-debug) + +(defface rudel-debug-received-data-face + '((default (:background "light sky blue"))) + "Face used for received (but not yet processed) data." + :group 'rudel-debug) + +(defface rudel-debug-received-processed-data-face + '((default (:background "DeepSkyBlue1"))) + "Face used for received data after processing." + :group 'rudel-debug) + +(defface rudel-debug-state-face + '((default (:background "light gray"))) + "Face used when indicating state changes." + :group 'rudel-debug) + +(defface rudel-debug-special-face + '((default (:background "light sea green"))) + "Face used for additional information." + :group 'rudel-debug) + +(defvar rudel-debug-tag-faces + '((:sent . (rudel-debug-sent-data-face "< ")) + (:received . (rudel-debug-received-data-face "> ")) + (:received-processed . (rudel-debug-received-processed-data-face ">> ")) + (:state . (rudel-debug-state-face "| ")) + (:special . (rudel-debug-special-face "; "))) + "Associate tag to faces and prefixes.") + + +;;; Data debug functions +;; + +(defun rudel-adebug-session () + "Analyze current session in data debug buffer." + (interactive) + + ;; Make sure we have a session. + (unless rudel-current-session + (error "No active Rudel session")) + + (with-current-buffer (data-debug-new-buffer "RUDEL-SESSION") + (data-debug-insert-thing rudel-current-session "# " ""))) + +(defun rudel-adebug-server (server) + "Analyze server in data debug buffer." + (interactive) + + (with-current-buffer (data-debug-new-buffer "RUDEL-SERVER") + (data-debug-insert-thing server "# " ""))) + + +;;; Advice stuff +;; + +(defadvice rudel-join-session (after rudel-debug last activate) + "Run data-debug inspection on newly created session objects." + (require 'rudel-debug) + (rudel-adebug-session)) + +(defadvice rudel-host-session (after rudel-debug last activate) + "Run data-debug inspection on newly created server objects." + (require 'rudel-debug) + (rudel-adebug-server ad-return-value)) + + +;;; Network functions +;; + +(defun rudel-suspend-session-socket () + "Suspend the socket associated to the current session." + (interactive) + + ;; Make sure we have a session. + (unless rudel-current-session + (error "No active Rudel session")) + + (with-slots (connection) rudel-current-session + (with-slots (socket) connection + (stop-process socket)))) + +(defun rudel-resume-session-socket () + "Resume the socket associated to the current session." + (interactive) + + ;; Make sure we have a session. + (unless rudel-current-session + (error "No active Rudel session")) + + (with-slots (connection) rudel-current-session + (with-slots (socket) connection + (continue-process socket)))) + + +;;; Reset functions +;; + +(defun rudel-kill-processes () + "TODO" + (interactive) + (mapc #'delete-process (process-list))) + +(defun rudel-reset () + "TODO" + (interactive) + (dolist (buffer (buffer-list)) + (with-current-buffer buffer + (when rudel-buffer-document + (setq rudel-buffer-document nil)))) + (rudel-kill-processes) + (setq rudel-current-session nil)) + + +;;; Socket debugging +;; + +(defmethod rudel-state-change :before ((this rudel-socket-owner) + state message) + "Print STATE and MESSAGE to debug stream." + (with-slots (socket) this + (rudel-debug-stream-insert + (rudel-debug-stream-name socket) + :state + (format "connection state changed to %s: \"%s\"" + (upcase (symbol-name state)) + ;; MESSAGE ends with a newline; remove it + (substring message 0 -1)))) + ) + + +;;; Utility functions +;; + +(defun rudel-debug-stream-name (socket) + "Return debug stream name for SOCKET." + (process-name socket)) + +(defun rudel-debug-stream-insert (stream tag data &optional object) + "Insert DATA and possibly OBJECT into stream using TAG as style." + (let* ((buffer-name (format "*%s stream*" stream)) + (buffer (or (get-buffer buffer-name) + (data-debug-new-buffer buffer-name))) + (appearance (cdr (assoc tag rudel-debug-tag-faces))) + (face (when appearance + (or (nth 0 appearance) + 'default))) + (prefix (or (nth 1 appearance) + ""))) + (save-excursion + (set-buffer buffer) + (goto-char 0) + (insert prefix + (propertize data 'face face) + (if (string= (substring data -1) "\n") + "" "\n")) + (when object + (data-debug-insert-thing object prefix "")))) + ) + +(provide 'rudel-debug) +;;; rudel-debug.el ends here diff --git a/emacs.d/lisp/rudel/rudel-errors.el b/emacs.d/lisp/rudel/rudel-errors.el new file mode 100644 index 0000000..9811511 --- /dev/null +++ b/emacs.d/lisp/rudel/rudel-errors.el @@ -0,0 +1,66 @@ +;;; rudel-errors.el --- Error data used in Rudel +;; +;; Copyright (C) 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Rudel, errors, conditions +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; The following condition hierarchy is defined: +;; +;; error +;; +- rudel-error +;; +- rudel-join-error +;; +- rudel-host-error + + +;;; History: +;; +;; 0.1 - Initial revision + + +;;; Code: +;; + +;; rudel-join-error + +(intern "rudel-join-error") + +(put 'rudel-join-error 'error-conditions + '(error + rudel-error rudel-join-error)) + +(put 'rudel-join-error 'error-message + "Could not join session") + +;; rudel-host-error + +(intern "rudel-host-error") + +(put 'rudel-host-error 'error-conditions + '(error + rudel-error rudel-host-error)) + +(put 'rudel-host-error 'error-message + "Could not host session") + +(provide 'rudel-errors) +;;; rudel-errors.el ends here diff --git a/emacs.d/lisp/rudel/rudel-hooks.el b/emacs.d/lisp/rudel/rudel-hooks.el new file mode 100644 index 0000000..0cc9a3e --- /dev/null +++ b/emacs.d/lisp/rudel/rudel-hooks.el @@ -0,0 +1,252 @@ +;;; rudel-hooks.el --- Hooks for Rudel events +;; +;; Copyright (C) 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Rudel, hook +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; This file contains all global hooks (as opposed to hooks provided +;; by individual objects) provided by Rudel. + + +;;; History: +;; +;; 0.1 - Initial revision. + + +;;; Code: +;; + +(require 'eieio) + + +;;; Hook variables +;; + +(defvar rudel-session-start-hook nil + "This hook is run when a new session is started. +The only argument is the session object.") + +(defvar rudel-session-end-hook nil + "This hook is run when a session ends. +The only argument is the session object.") + +(defvar rudel-session-add-user-hook nil + "This hook is run when a user is added to a session. +The arguments are the session and the user.") + +(defvar rudel-session-remove-user-hook nil + "This hook is run when a user is removed from a session. +The arguments are the session and the user.") + +(defvar rudel-session-add-document-hook nil + "This hook is run when a document is added to a session. +The arguments are the session and the document.") + +(defvar rudel-session-remove-document-hook nil + "This hook is run when a document is removed from a session. +The arguments are the session and the document.") + + +(defvar rudel-user-change-hook nil + "This hooks is run when a user object changes. +The only argument is the user object.") + + +(defvar rudel-document-attach-hook nil + "This hook is run when a document is attached to a buffer. +The arguments are the document and the buffer.") + +(defvar rudel-document-detach-hook nil + "This hook is run when document is detached from its buffer. +The arguments are the document and the buffer.") + + +;;; Handlers +;; + +(defun rudel-hooks--session-start (session) + "Watch SESSION for added/removed users and documents." + ;; Install handlers for the hooks of the session. + (with-slots (users documents) session + + ;; Watch for session end. + (object-add-hook session 'end-hook + #'rudel-hooks--session-end) + + ;; Watch all users in the session. + (dolist (user users) + (rudel-hooks--session-add-user session user)) + + ;; Watch session for added/removed users. + (object-add-hook + session 'add-user-hook + #'rudel-hooks--session-add-user) + (object-add-hook + session 'remove-user-hook + #'rudel-hooks--session-remove-user) + + ;; Watch all documents in the session. + (dolist (document documents) + (rudel-hooks--session-add-document session document)) + + ;; Watch session for added/removed documents. + (object-add-hook + session 'add-document-hook + #'rudel-hooks--session-add-document) + (object-add-hook + session 'remove-document-hook + #'rudel-hooks--session-remove-document)) + ) + +(defun rudel-hooks--session-end (session) + "Stop watching SESSION for added/removed users and documents." + ;; Remove handlers from the session. + (with-slots (users documents) session + + ;; Stop watching for session end. + (object-remove-hook session 'end-hook + #'rudel-hooks--session-end) + + ;; Stop watching all users in the session. + (dolist (user users) + (rudel-hooks--session-remove-user session user)) + + ;; Stop watching session for added/removed users. + (object-remove-hook + session 'add-user-hook + #'rudel-hooks--session-add-user) + (object-remove-hook + session 'remove-user-hook + #'rudel-hooks--session-remove-user) + + ;; Stop watching all documents in the session. + (dolist (document documents) + (rudel-hooks--session-remove-document session document)) + + ;; Stop watching session for added/removed documents. + (object-remove-hook + session 'add-document-hook + #'rudel-hooks--session-add-document) + (object-remove-hook + session 'remove-document-hook + #'rudel-hooks--session-remove-document)) + + ;; Run the hook. + (run-hook-with-args 'rudel-session-end-hook session) + ) + +(defun rudel-hooks--session-add-user (session user) + "Watch USER for changes and run `rudel-session-add-user-hook'." + ;; Watch USER. + (object-add-hook user 'change-hook #'rudel-hooks--user-change) + + ;; Run the hook. + (run-hook-with-args 'rudel-session-add-user-hook session user)) + +(defun rudel-hooks--session-remove-user (session user) + "Stop watching USER and run `rudel-session-remove-user-hook'" + ;; Stop watching USER. + (object-remove-hook user 'change-hook #'rudel-hooks--user-change) + + ;; Run the hook. + (run-hook-with-args 'rudel-session-remove-user-hook + session user)) + +(defun rudel-hooks--session-add-document (session document) + "Watch DOCUMENT and run `rudel-session-add-document-hook'." + ;; Watch document for attach/detach. + (object-add-hook document 'attach-hook + #'rudel-hooks--document-attach) + (object-add-hook document 'detach-hook + #'rudel-hooks--document-detach) + + ;; Run the hook. + (run-hook-with-args 'rudel-session-add-document-hook + session document) + ) + +(defun rudel-hooks--session-remove-document (session document) + "Stop watching DOCUMENT and run `rudel-session-remove-document-hook'." + ;; Stop watching DOCUMENT for attach/detach. + (object-remove-hook + document 'attach-hook #'rudel-hooks--document-attach) + (object-remove-hook + document 'detach-hook #'rudel-hooks--document-detach) + + ;; Run the hook. + (run-hook-with-args 'rudel-session-remove-document-hook + session document) + ) + + +(defun rudel-hooks--user-change (user) + "Run `rudel-user-change-hook' with argument USER." + (run-hook-with-args 'rudel-user-change-hook user)) + + +(defun rudel-hooks--document-attach (document buffer) + "Run `rudel-document-attach-hook' with arguments DOCUMENT and BUFFER." + (run-hook-with-args 'rudel-document-attach-hook + document buffer)) + +(defun rudel-hooks--document-detach (document buffer) + "Run `rudel-document-detach-hook' with arguments DOCUMENT and BUFFER." + (run-hook-with-args 'rudel-document-detach-hook + document buffer)) + + +;;; Initialization +;; + +(defun rudel-hooks--install-handlers () + "Install handlers for session start/end." + ;; Install handlers for already started sessions. + (when (boundp 'rudel-current-session) + (mapc + #'rudel-hooks--session-start + (when rudel-current-session + (list rudel-current-session)))) + + ;; Watch for session start/end. + (add-hook 'rudel-session-start-hook + #'rudel-hooks--session-start) + ) + +(defun rudel-hooks--uninstall-handlers () + "Uninstall handlers for session start/end." + ;; Stop watching session start/end. + (remove-hook 'rudel-session-start-hook + #'rudel-hooks--session-start) + + ;; Uninstall handlers for already started sessions. + (when (boundp 'rudel-current-session) + (mapc + #'rudel-hooks--session-end + (when rudel-current-session + (list rudel-current-session)))) + ) + +(rudel-hooks--install-handlers) + +(provide 'rudel-hooks) +;;; rudel-hooks.el ends here diff --git a/emacs.d/lisp/rudel/rudel-icons.el b/emacs.d/lisp/rudel/rudel-icons.el new file mode 100644 index 0000000..9bb35b5 --- /dev/null +++ b/emacs.d/lisp/rudel/rudel-icons.el @@ -0,0 +1,90 @@ +;;; rudel-icons.el --- Icons used by Rudel +;; +;; Copyright (C) 2008, 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: rudel, icons +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; This file loads all icons used in Rudel. + + +;;; History: +;; +;; 0.1 - Initial revision. + + +;;; Code: +;; + +(require 'image) + + +;;; Image constants +;; + +(defconst rudel-icons-image-formats '(svg png) + "Image formats to try (in that order) when loading Rudel icons.") + +(defconst rudel-icons-directory + (file-name-as-directory + (concat (file-name-directory + (locate-library "rudel-icons.el")) + "icons")) + "Directory that holds Rudel icon files.") + + +;;; Helper macro +;; + +(defmacro rudel-defimage (name &optional docstring) + "Load image from Rudel icon directory and define image named NAME. +Optional argument DOCSTRING is the documentation string to +associate with the image." + (declare (doc-string 2)) + (let ((icon (intern (format "rudel-icon-%s" name))) + (specs (mapcar + (lambda (type) + `(:type ,type + :ascent center + :mask heuristic + :file ,(concat rudel-icons-directory + name "." (symbol-name type)))) + rudel-icons-image-formats))) + `(defimage ,icon + (,@specs) + ,(or docstring + (format "%s icon." (capitalize name))))) + ) + + +;;; Image definitions +;; + +(rudel-defimage "person") +(rudel-defimage "document") +(rudel-defimage "connected") +(rudel-defimage "disconnected") +(rudel-defimage "plaintext") +(rudel-defimage "encrypted") + +(provide 'rudel-icons) +;;; rudel-icons.el ends here diff --git a/emacs.d/lisp/rudel/rudel-interactive.el b/emacs.d/lisp/rudel/rudel-interactive.el new file mode 100644 index 0000000..5dda752 --- /dev/null +++ b/emacs.d/lisp/rudel/rudel-interactive.el @@ -0,0 +1,181 @@ +;;; rudel-interactive.el --- User interaction functions for Rudel. +;; +;; Copyright (C) 2008, 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Rudel, user, interface, interaction +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; Functions for user interactions commonly used in Rudel components. + + +;;; History: +;; +;; 0.1 - Initial revision. + + +;;; Code: +;; + +(require 'rudel-compat) ;; for `read-color' replacement + + +;;; Function for reading Rudel objects from the user. +;; + +(defun rudel-read-backend (backends &optional prompt return) + "Read a backend name from BACKENDS and return that name or the actual backend depending on RETURN. +If RETURN is 'object, return the backend object which is of the +form (NAME . CLASS-OR-OBJECT); Otherwise return the name as +string." + (unless prompt + (setq prompt "Backend: ")) + (let* ((backend-names (mapcar (lambda (cell) + (symbol-name (car cell))) + backends)) + (backend-name (completing-read prompt backend-names nil t))) + (cond + ((eq return 'object) + (assoc (intern backend-name) backends)) + (t backend-name))) + ) + +(defun rudel-read-session (sessions &optional prompt return) + "Read a session name from SESSIONS and return that name or the session info depending on RETURN. +If PROMPT is non-nil use as prompt string. +If RETURN is 'object, return the session object; Otherwise return +the name as string." + (unless prompt + (setq prompt "Session: ")) + ;; For presentation and identification of sessions, use the :name + ;; property. + (flet ((to-string (session) + (if (rudel-backend-cons-p session) + (symbol-name (car session)) + (plist-get session :name)))) + ;; Read a session by name, then return that name or the + ;; corresponding session info. + (let ((session-name (completing-read prompt + (mapcar #'to-string sessions) + nil t))) + (cond + ((eq return 'object) + (find session-name sessions + :key #'to-string :test #'string=)) + (t session-name)))) + ) + +(defun rudel-read-user-name () + "Read a username. +The default is taken from `rudel-default-username'." + (read-string "Username: " rudel-default-username)) + +(defun rudel-read-user-color () + "Read a color." + (read-color "Color: " t)) + +(defun rudel-read-user (&optional users prompt return) + "Read a user name from USERS and return that name or the actual user depending on RETURN. +If USERS is nil, use the user list of `rudel-current-session'. +If RETURN. is 'object, return the user object; Otherwise return +the name as string." + ;; If no user list is provided, the user list of the current session + ;; is used. + (unless users + (if rudel-current-session + (setq users (oref rudel-current-session :users)) + (error "No user list and no active Rudel session"))) + (unless prompt + (setq prompt "User: ")) + ;; Construct a list of user name, read a name with completion and + ;; return a user name of object. + (let* ((user-names (mapcar 'object-name-string users)) + (user-name (completing-read prompt user-names nil t))) + (cond + ((eq return 'object) + (find user-name users + :test 'string= :key 'object-name-string)) + (t user-name))) + ) + +(defun rudel-read-document (&optional documents prompt return) + "Read a document name from DOCUMENTS and return that name or the actual document depending on RETURN. +If RETURN. is 'object, return the backend object; Otherwise +return the name as string." + (unless documents + (if rudel-current-session + (setq documents (oref rudel-current-session :documents)) + (error "No document list and no active Rudel session"))) + (unless documents + (error "No documents")) ; TODO error is a bit harsh + (unless prompt + (setq prompt "Document: ")) + + ;; Construct list of names, read one name and return that name or + ;; the named object. + (let* ((document-names (mapcar #'rudel-unique-name documents)) + (document-name (completing-read prompt document-names nil t))) + (cond + ((eq return 'object) + (find document-name documents + :test #'string= :key #'rudel-unique-name)) + (t document-name))) + ) + + +;;; Buffer allocation functions +;; + +(defun rudel-allocate-buffer-clear-existing (name) + "When the requested buffer NAME exists, clear its contents and use it." + (let ((buffer (get-buffer name))) + (if buffer + (progn + ;; Ask the user whether it is OK to erase the contents of + ;; the buffer. + (unless (yes-or-no-p (format + "Buffer `%s' already exists; Erase contents? " + name)) + (error "Buffer `%s' already exists" name)) ;; TODO throw or signal; not error + ;; When the buffer is attached to a different document, ask + ;; whether it is OK to detach the buffer. + (let ((document (rudel-buffer-document buffer))) + (unless (or (not document) + (yes-or-no-p (format + "Buffer `%s' is attached to the document `%s'; Detach? " + name + (rudel-unique-name document)))) + (error "Buffer `%s' already attached to a document" name))) + ;; Delete buffer contents; maybe detach buffer first. + (when (rudel-buffer-has-document-p buffer) + (rudel-unpublish-buffer buffer)) + (with-current-buffer buffer + (erase-buffer))) + (setq buffer (get-buffer-create name))) + buffer) + ) + +(defun rudel-allocate-buffer-make-unique (name) + "When the requested buffer NAME exists, create another buffer." + (get-buffer-create (generate-new-buffer-name name))) + +(provide 'rudel-interactive) +;;; rudel-interactive.el ends here diff --git a/emacs.d/lisp/rudel/rudel-loaddefs.el b/emacs.d/lisp/rudel/rudel-loaddefs.el new file mode 100644 index 0000000..9de8708 --- /dev/null +++ b/emacs.d/lisp/rudel/rudel-loaddefs.el @@ -0,0 +1,23 @@ +;;; rudel-loaddefs.el + +(autoload 'rudel-join-session "rudel" "Start a collaborative Rudel session" t) +(autoload 'rudel-host-session "rudel" "Host a collaborative Rudel session" t) +(autoload 'rudel-speedbar "rudel-speedbar" + "Show connected users and documents for the Rudel session in speedbar" t) +(autoload 'global-rudel-minor-mode "rudel-mode" + "Bindings for rudel session-level commands" t) + +(global-set-key (kbd "C-c c j") 'rudel-join-session) + +(setq rudel-dir (file-name-directory (or (buffer-file-name) load-file-name))) + +(dolist (dir '("." "jupiter" "obby" "zeroconf")) + (add-to-list 'load-path (concat rudel-dir "/" dir))) + +(eval-after-load 'rudel + '(progn (global-rudel-minor-mode) + (require 'rudel-obby) + (when (require 'zeroconf nil t) + (require 'rudel-zeroconf)))) + +(provide 'rudel-loaddefs) \ No newline at end of file diff --git a/emacs.d/lisp/rudel/rudel-mode.el b/emacs.d/lisp/rudel/rudel-mode.el new file mode 100644 index 0000000..182188a --- /dev/null +++ b/emacs.d/lisp/rudel/rudel-mode.el @@ -0,0 +1,621 @@ +;;; rudel-mode.el --- Global and buffer-local Rudel minor modes +;; +;; Copyright (C) 2008, 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: rudel, mode +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; This file contains the following global and buffer-local Rudel +;; minor modes: +;; +;; + `rudel-header-subscriptions-minor-mode': Display subscribed users +;; and their respective status in the header line +;; + `rudel-mode-line-publish-state-minor-mode': Display publication +;; state of buffers in their mode lines +;; + `global-rudel-minor-mode': Installs a keymap and a Rudel menu + + +;;; History: +;; +;; 0.4 - Display buffer publication state in mode line. +;; +;; 0.3 - Display subscriptions in header line. +;; +;; 0.2 - Use define-minor-mode. +;; +;; 0.1 - Initial revision. + + +;;; Code: +;; + +(require 'easy-mmode) +(require 'easymenu) + +(require 'rudel) +(require 'rudel-hooks) + + +;;; Customization Options +;; + +(defcustom rudel-header-subscriptions-use-images t + "Use images when displaying subscribed users in header-line." + :group 'rudel + :type 'boolean + :set (lambda (symbol value) + (set-default symbol value) + (when (featurep 'rudel-mode) + (rudel-header-subscriptions--options-changed)))) + +(defcustom rudel-header-subscriptions-separator " " + "String used to separate indicator strings of subscribed users." + :group 'rudel + :type 'string + :set (lambda (symbol value) + (set-default symbol value) + (when (featurep 'rudel-mode) + (rudel-header-subscriptions--options-changed)))) + +(defcustom rudel-mode-line-publish-state-unpublished-string "-" + "String used to indicate not published state in the mode line." + :group 'rudel + :type 'string + :set (lambda (symbol value) + (set-default symbol value) + (when (featurep 'rudel-mode) + (rudel-mode-line-publish-state--options-changed)))) + +(defcustom rudel-mode-line-publish-state-published-string "P" + "String used to indicate published state in the mode line." + :group 'rudel + :type 'string + :set (lambda (symbol value) + (set-default symbol value) + (when (featurep 'rudel-mode) + (rudel-mode-line-publish-state--options-changed)))) + +(dolist (v '(rudel-header-subscriptions-use-images + rudel-header-subscriptions-separator + rudel-mode-line-publish-state-unpublished-string + rudel-mode-line-publish-state-published-string)) + (put v 'save-local-variable t)) + + +;;; Header line subscriptions helper functions +;; + +(defun rudel-header-subscriptions--make-format (document) + "Return a Lisp object usable as `header-line-format' generated from DOCUMENT." + (with-slots (subscribed) document + (mapconcat + (lambda (user) + (rudel-display-string + user rudel-header-subscriptions-use-images)) + subscribed rudel-header-subscriptions-separator))) + +(defun rudel-header-subscriptions--update-from-document (document) + "Update header-line of the buffer attached to DOCUMENT." + (with-slots (buffer) document + (when buffer + (with-current-buffer buffer + (setq header-line-format + (rudel-header-subscriptions--make-format document)) + (force-mode-line-update))))) + +(defun rudel-header-subscriptions--update-from-buffer () + "Update header-line of the current buffer from associated document." + (setq header-line-format + (when (rudel-buffer-document) + (rudel-header-subscriptions--make-format + (rudel-buffer-document)))) + (force-mode-line-update)) + +(defun rudel-header-subscriptions--options-changed () + "Update headers in buffers that have header subscriptions mode enabled." + (dolist (buffer (buffer-list)) + (with-current-buffer buffer + (when rudel-header-subscriptions-minor-mode + (rudel-header-subscriptions--update-from-buffer))))) + + +;;; Header line indication of users' status and activities +;; + +(defun rudel-header-subscriptions--user-change (document user) + "Update header line after USER changed." + ;; Update the header line to reflect the changes to USER. + (rudel-header-subscriptions--update-from-document document)) + +(defun rudel-header-subscriptions--add-user (document user) + "Start monitoring USER and update header line." + ;; Monitor USER. + (lexical-let ((document document)) + (object-add-hook user 'change-hook + (lambda (user) + (rudel-header-subscriptions--user-change + document user)))) + + ;; Update the header line once to get the user added. + (rudel-header-subscriptions--update-from-document document) + ) + +(defun rudel-header-subscriptions--remove-user (document user) + "Stop monitoring USER and update header line." + ;; TODO Stop monitoring USER. + ;; (object-remove-hook user 'change-hook + + ;; Update the header line once to get the user removed. + (rudel-header-subscriptions--update-from-document document) + ) + +;;;###autoload +(define-minor-mode rudel-header-subscriptions-minor-mode + "Toggle Rudel header subscriptions minor mode. + +This mode displays users subscribed to the document associated +with the buffer in the header-line. Depending on the kind of +session, additional information like connection status, +encryption or activity indication may be displayed with each +user. + +If ARG is null, toggle Rudel header subscriptions mode. +If ARG is a number greater than zero, turn on Rudel header +subscriptions mode; otherwise, turn it off." + :init-value nil + :group 'rudel + (cond + ;; Emacs session is not interactive + (noninteractive + (setq rudel-header-subscriptions-minor-mode nil)) + + ;; Mode is being enabled and the buffer has an attached document. + ((and rudel-header-subscriptions-minor-mode + (rudel-buffer-document)) + (let ((document (rudel-buffer-document))) + + ;; Monitor all users that already are subscribed to the + ;; document. + (with-slots (subscribed) document + (dolist (user subscribed) + (rudel-header-subscriptions--add-user document user))) + + ;; Monitor future (un)subscribe events. + (object-add-hook document 'subscribe-hook + #'rudel-header-subscriptions--add-user) + (object-add-hook document 'unsubscribe-hook + #'rudel-header-subscriptions--remove-user)) + + ;; Update header line. + (rudel-header-subscriptions--update-from-buffer)) + + ;; Mode is being disabled, but the buffer has an attached document. + ((and (not rudel-header-subscriptions-minor-mode) + (rudel-buffer-document)) + (let ((document (rudel-buffer-document))) + + ;; Stop monitoring all users that are subscribed to the + ;; document. + (with-slots (subscribed) document + (dolist (user subscribed) + (rudel-header-subscriptions--remove-user document user))) + + ;; Stop monitoring (un)subscribe events. + (object-remove-hook document 'subscribe-hook + #'rudel-header-subscriptions--add-user) + (object-remove-hook document 'unsubscribe-hook + #'rudel-header-subscriptions--remove-user)) + + ;; Reset header line to default format. + (setq header-line-format default-header-line-format) + (force-mode-line-update)) ;; TODO remove all handlers + + ;; No buffer document + (t + ;; Ensure the mode is disabled. + (setq rudel-header-subscriptions-minor-mode nil) + + ;; Reset header line to default format. + (setq header-line-format default-header-line-format) + (force-mode-line-update))) + ) + + +;;; Global header subscriptions mode +;; + +;; Tracking stuff for the global mode + +(defun rudel-header-subscriptions--attach (document buffer) + "Activate header subscriptions mode for BUFFER." + (with-current-buffer buffer + (rudel-header-subscriptions-minor-mode 1))) + +(defun rudel-header-subscriptions--detach (document buffer) + "Deactivate header subscriptions mode for BUFFER." + (with-current-buffer buffer + (rudel-header-subscriptions-minor-mode 0))) + +(defun rudel-header-subscriptions--add-document (session document) + "Watch DOCUMENT for attach/detach events." + ;; When document is attached to a buffer, turn the mode on. + (with-slots (buffer) document + (when buffer + (rudel-header-subscriptions--attach document buffer))) + + ;; Watch document for attaching and detaching. + (object-add-hook + document 'attach-hook #'rudel-header-subscriptions--attach) + (object-add-hook + document 'detach-hook #'rudel-header-subscriptions--detach)) + +(defun rudel-header-subscriptions--remove-document (session document) + "Stop watching DOCUMENT for attach/detach events." + ;; When document is attached to a buffer, turn the mode off. + (with-slots (buffer) document + (when buffer + (rudel-header-subscriptions--detach document buffer))) + + ;; Stop watching document for attaching and detaching. + (object-remove-hook + document 'attach-hook #'rudel-header-subscriptions--attach) + (object-remove-hook + document 'detach-hook #'rudel-header-subscriptions--detach)) + +(defun rudel-header-subscriptions--session-start (session) + "Watch SESSION documents and watch for added/removed documents." + ;; Watch all documents in the session. + (with-slots (documents) session + (dolist (document documents) + (rudel-header-subscriptions--add-document session document))) + + ;; Watch session for added/removed documents. + (object-add-hook + session 'add-document-hook + #'rudel-header-subscriptions--add-document) + (object-add-hook + session 'remove-document-hook + #'rudel-header-subscriptions--remove-document) + ) + +(defun rudel-header-subscriptions--session-end (session) + "Stop watching SESSION for added/removed documents." + ;; Stop watching all documents in the session. + (with-slots (documents) session + (dolist (document documents) + (rudel-header-subscriptions--remove-document session document))) + + ;; Stop watching session for added/removed documents. + (object-remove-hook + session 'add-document-hook + #'rudel-header-subscriptions--add-document) + (object-remove-hook + session 'remove-document-hook + #'rudel-header-subscriptions--remove-document) + ) + +;;;###autoload +(define-globalized-minor-mode global-rudel-header-subscriptions-mode + rudel-header-subscriptions-minor-mode + rudel-header-subscriptions-minor-mode + :group 'rudel) + +(defadvice global-rudel-header-subscriptions-mode + (around track-subscriptions activate) + "Start/stop tracking subscriptions when the mode is (de)activated." + (let ((value ad-do-it)) + (if value + + ;; Add handlers to session start and end hooks and run the + ;; start handler on already started sessions. + (progn + + ;; Go through all existing sessions. + (mapc #'rudel-header-subscriptions--session-start + (when rudel-current-session + (list rudel-current-session))) + + ;; Watch for new/ended sessions. + (add-hook 'rudel-session-start-hook + #'rudel-header-subscriptions--session-start) + (add-hook 'rudel-session-end-hook + #'rudel-header-subscriptions--session-end)) + + ;; Remove handlers from session start and end hooks and run the + ;; end handler on active sessions. + (mapc #'rudel-header-subscriptions--session-end + (when rudel-current-session + (list rudel-current-session))) + + (remove-hook 'rudel-session-start-hook + #'rudel-header-subscriptions--session-start) + (remove-hook 'rudel-session-end-hook + #'rudel-header-subscriptions--session-end))) + ) + + +;;; Mode line indication of buffer state +;; + +(defvar rudel-mode-line-publish-state-string + (propertize + "-" + 'mouse-face 'mode-line-highlight + 'help-echo "Buffer is not published") + "Contains a mode line fragment indicating the publication state +of the buffer.") +(make-variable-buffer-local 'rudel-mode-line-publish-state-string) +(put 'rudel-mode-line-publish-state-string 'risky-local-variable t) + +(defun rudel-mode-line-publish-state--add-indicator-to-mode-line () + "Add Rudel publish state indicator to mode line." + (let* ((new-format (copy-list mode-line-format)) + (format-rest (nthcdr + (position 'mode-line-modified mode-line-format) + new-format)) + (format-rest-cdr (cdr format-rest))) + (setcdr format-rest (cons 'rudel-mode-line-publish-state-string + format-rest-cdr)) + (setq mode-line-format new-format)) + (force-mode-line-update)) + +(defun rudel-mode-line-publish-state--remove-indicator-from-mode-line () + "Remove Rudel publish state indicator from mode line." + (let ((format-rest (nthcdr + (position 'mode-line-remote mode-line-format) + mode-line-format))) + ;; Only change the mode line if our indicator is present. + (when (eq (second format-rest) 'rudel-mode-line-publish-state-string) + (setcdr format-rest (cddr format-rest)) + (force-mode-line-update)))) + +(defun rudel-mode-line-publish-state--update-string () + "Update variable `rudel-mode-line-publish-state-string'." + ;; Update `rudel-mode-line-publish-state-string' with appropriate + ;; propertized indicator string. + (setq rudel-mode-line-publish-state-string + (cond + ((rudel-buffer-document) + (propertize + rudel-mode-line-publish-state-published-string + 'mouse-face 'mode-line-highlight + 'help-echo "Buffer is published")) + (t + (propertize + rudel-mode-line-publish-state-unpublished-string + 'mouse-face 'mode-line-highlight + 'help-echo "Buffer is not published")))) + + ;; Update the mode line. + (force-mode-line-update) + ) + +(defun rudel-mode-line-publish-state--document-attach (document buffer) + "Handle attaching of DOCUMENT to BUFFER. +When `rudel-mode-line-publish-state-minor-mode' is enabled in +BUFFER, update the state string." + ;; Only act when BUFFER has the minor mode enabled. + (with-current-buffer buffer + (when rudel-mode-line-publish-state-minor-mode + ;; Update the mode line. + (rudel-mode-line-publish-state--update-string) + + ;; Watch for detaching of DOCUMENT from BUFFER. + (object-add-hook + document 'detach-hook + #'rudel-mode-line-publish-state--document-detach))) + ) + +(defun rudel-mode-line-publish-state--document-detach (document buffer) + "Handle detaching of DOCUMENT from BUFFER." + ;; Update the mode line of BUFFER. + (with-current-buffer buffer + (rudel-mode-line-publish-state--update-string)) + + ;; Stop watching for detaching of DOCUMENT from BUFFER. It (or a + ;; different document) has to attach again first, before the next + ;; detaching can occur. + (object-remove-hook + document 'detach-hook + #'rudel-mode-line-publish-state--document-detach) + ) + +(defun rudel-mode-line-publish-state--options-changed () + "Update mode lines in buffers that have mode line publish state mode enabled." + (dolist (buffer (buffer-list)) + (with-current-buffer buffer + (when rudel-mode-line-publish-state-minor-mode + (rudel-mode-line-publish-state--update-string))))) + +;;;###autoload +(define-minor-mode rudel-mode-line-publish-state-minor-mode + "Toggle Rudel mode line publish state minor mode. + +This mode displays an indicator of the buffer's state with +respect to an associated Rudel document in the mode line. If the +buffer has an attached document, the string \"P\" is displayed +after the remote file indicator. Otherwise, the string \"-\" is +displayed. + +If ARG is null, toggle Rudel mode line publish state minor mode. +If ARG is a number greater than zero, turn on Rudel minor mode +line publish state mode; otherwise, turn it off." + :init-value nil + :group 'rudel + (cond + ;; Emacs session is not interactive + (noninteractive + (setq rudel-mode-line-publish-state-minor-mode nil)) + + ;; Mode is enabled + (rudel-mode-line-publish-state-minor-mode + ;; Extend and update the mode line, no matter whether the buffer + ;; has a document or not. + (rudel-mode-line-publish-state--add-indicator-to-mode-line) + (rudel-mode-line-publish-state--update-string) + + ;; Watch document, if available or attach events, when a document + ;; is not available. + (let ((document (rudel-buffer-document))) + (if document + ;; Handle detaching of the document from the buffer. + (object-add-hook + document 'detach-hook + #'rudel-mode-line-publish-state--document-detach) + ;; Handle attaching of documents to buffers. We use the global + ;; hook here, installing the handler twice is prevented by + ;; `add-hook'. + (add-hook 'rudel-document-attach-hook + #'rudel-mode-line-publish-state--document-attach)))) + + ;; Mode is disabled + (t + ;; Maybe stop watching for the document detaching from the buffer. + (let ((document (rudel-buffer-document))) + (when document + (object-remove-hook + document 'detach-hook + #'rudel-mode-line-publish-state--document-detach))) + + ;; Remove the indicator from the mode line. + (rudel-mode-line-publish-state--remove-indicator-from-mode-line))) + ) + + +;;; Global mode line publish state mode +;; + +;;;###autoload +(define-globalized-minor-mode global-rudel-mode-line-publish-state-mode + rudel-mode-line-publish-state-minor-mode + rudel-mode-line-publish-state-minor-mode + :group 'rudel) + + +;;; Global Rudel mode, menu and keymap +;; + +(defvar rudel-minor-keymap + (let ((map (make-sparse-keymap)) + (sub-map (make-sparse-keymap))) + ;; Define sub keymap + (define-key sub-map "j" #'rudel-join-session) + (define-key sub-map "h" #'rudel-host-session) + (define-key sub-map "e" #'rudel-end-session) + + (define-key sub-map "c" #'rudel-change-color) + + (define-key sub-map "p" #'rudel-publish-buffer) + (define-key sub-map "u" #'rudel-unpublish-buffer) + (define-key sub-map "s" #'rudel-subscribe) + + ;; Bind the sub keymap into map + (define-key map "\C-cc" sub-map) + map) + "Keymap used in Rudel minor mode.") + +(when rudel-minor-keymap + (easy-menu-define + rudel-minor-menu rudel-minor-keymap "Rudel Minor Mode Menu" + '("Rudel" + [ "Join Session" rudel-join-session + (not rudel-current-session) ] + [ "Leave Session" rudel-end-session + rudel-current-session ] + "---" + [ "Host a Session" rudel-host-session + t ] + "---" + [ "Change Color" rudel-change-color + (and rudel-current-session + (rudel-capable-of-p + (oref rudel-current-session :backend) + 'change-color)) ] ; TODO bulky + "---" + [ "Publish current Buffer" rudel-publish-buffer + (and rudel-current-session + (not (rudel-buffer-has-document-p))) ] + [ "Unpublish current Buffer" rudel-unpublish-buffer + (rudel-buffer-has-document-p) ] + [ "Subscribe to Document" rudel-subscribe + rudel-current-session ] + "---" + [ "Rudel Overview" rudel-speedbar + t ] + "---" + ( "Options" + [ "Highlight Contributions in Authors' Colors" + (lambda () + (interactive) + (setq rudel-overlay-author-display + (not rudel-overlay-author-display)) + (rudel-overlay-options-changed)) + :style toggle + :selected rudel-overlay-author-display ] + ( "Show subscribed Users" + [ "In this Buffer" + rudel-header-subscriptions-minor-mode + :style toggle + :selected rudel-header-subscriptions-minor-mode ] + [ "Globally" + global-rudel-header-subscriptions-mode + :style toggle + :selected global-rudel-header-subscriptions-mode ] ) + ( "Show Status in mode line" + [ "In this Buffer" + rudel-mode-line-publish-state-minor-mode + :style toggle + :selected rudel-mode-line-publish-state-minor-mode ] + [ "Globally" + global-rudel-mode-line-publish-state-mode + :style toggle + :selected global-rudel-mode-line-publish-state-mode ] ) ) ) + ) + ) + +;;;###autoload +(define-minor-mode global-rudel-minor-mode + "Toggle global Rudel minor mode (No modeline indicator). + +If ARG is null, toggle global Rudel mode. +If ARG is a number greater than zero, turn on global Rudel mode; +otherwise, turn it off." + :init-value nil + :keymap rudel-minor-keymap + :global t + :group 'rudel + (cond + ;; Emacs session is not interactive + (noninteractive + (setq global-rudel-minor-mode nil)) + + ;; Mode is enabled + (global-rudel-minor-mode + ) + + ;; Mode is disabled + (t + )) + ) + +(provide 'rudel-mode) +;;; rudel-mode.el ends here diff --git a/emacs.d/lisp/rudel/rudel-operations.el b/emacs.d/lisp/rudel/rudel-operations.el new file mode 100644 index 0000000..3637303 --- /dev/null +++ b/emacs.d/lisp/rudel/rudel-operations.el @@ -0,0 +1,133 @@ +;;; rudel-operations.el --- Rudel domain operations +;; +;; Copyright (C) 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Rudel, operations +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; This file contains classes representing operations like insertions +;; and deletions, that are import for Rudel's domain of collaborative +;; editing. + + +;;; History: +;; +;; 0.1 - Initial revision + + +;;; Code: +;; + +(require 'eieio) + + +;;; Class rudel-operation +;; + +(defclass rudel-operation () + () + "Abstract base class for operations." + :abstract t) + +(defgeneric rudel-apply ((this rudel-operation) object) + "Apply the change represented by THIS to OBJECT.") + + +;;; Class rudel-insert-op +;; + +;; TODO should be rudel-insert but there is a method of the same name +(defclass rudel-insert-op (rudel-operation) + ((from :initarg :from + :type (or null (integer 0)) + :documentation + "Start of the region affected by this operation or +nil. nil means end of buffer") + (data :initarg :data + :type string + :documentation + "The inserted string.")) + "Objects of this class represent insertion operations.") + +(defmethod rudel-apply ((this rudel-insert-op) object) + "Apply THIS to OBJECT by inserting the associated data." + (with-slots (from data) this + (rudel-insert object from data))) + +(defmethod slot-missing ((this rudel-insert-op) + slot-name operation &optional new-value) + "Simulate read-only slots :length and :to." + (cond + ;; Slot :length + ((and (or (eq slot-name :length) + (eq slot-name 'length)) + (eq operation 'oref)) + (with-slots (data) this + (length data))) + ;; Slot :to + ((and (or (eq slot-name :to) + (eq slot-name 'to)) + (eq operation 'oref)) + (with-slots (from length) this + (+ from length))) + ;; Call next method + (t (call-next-method))) + ) + + +;;; Class rudel-delete-op +;; + +;; TODO should be rudel-delete but there is a method of the same name +(defclass rudel-delete-op (rudel-operation) + ((from :initarg :from + :type (integer 0) + :documentation + "Start of the region affected by this operation.") + (to :initarg :to + :type (integer 0) + :documentation + "End of the region affected by this operation.")) + "Objects of this class represent deletion operations.") + +(defmethod rudel-apply ((this rudel-delete-op) object) + "Apply THIS to OBJECT by deleting the associated region." + (with-slots (from length) this + (rudel-delete object from length))) + +(defmethod slot-missing ((this rudel-delete-op) + slot-name operation &optional new-value) + "Simulate slot :length" + (cond + ;; Slot :length + ((or (eq slot-name :length) + (eq slot-name 'length)) + (with-slots (from to) this + (if (eq operation 'oref) + (- to from) + (setq to (+ from new-value))))) + ;; Call next method + (t (call-next-method))) + ) + +(provide 'rudel-operations) +;;; rudel-operations.el ends here diff --git a/emacs.d/lisp/rudel/rudel-operators.el b/emacs.d/lisp/rudel/rudel-operators.el new file mode 100644 index 0000000..e51982e --- /dev/null +++ b/emacs.d/lisp/rudel/rudel-operators.el @@ -0,0 +1,172 @@ +;;; rudel-operators.el --- Sets of modification operators for Rudel objects +;; +;; Copyright (C) 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Rudel, operators +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; Collections of operations on specific objects are collected into +;; classes. Current there are +;; +;; - rudel-document-operators: realize operations on document objects +;; +;; - rudel-connection-operators: realize operations on connection +;; objects +;; +;; - rudel-overlay-operators: realize operations by altering the +;; overlays of buffer objects +;; +;; - rudel-hook-operators: realize operations by calling hooks + + +;;; History: +;; +;; 0.1 - Initial revision. + + +;;; Code: +;; + +(require 'eieio) + +(require 'rudel-overlay) + + +;;; Class rudel-document-operators +;; + +(defclass rudel-document-operators () + ((document :initarg :document + :type rudel-document-child + :documentation + "Document to which modification operators are +applied.")) + "Provides operation methods which modify an associated document.") + +(defmethod rudel-insert ((this rudel-document-operators) position data) + "Insert DATA at POSITION into the document attached to THIS." + (with-slots (document) this + (rudel-insert document position data))) + +(defmethod rudel-delete ((this rudel-document-operators) position length) + "Delete a region of LENGTH characters at POSITION from the document attached to THIS." + (with-slots (document) this + (rudel-delete document position length))) + + +;;; Class rudel-connection-operators +;; + +(defclass rudel-connection-operators () + ((connection :initarg :connection + :type rudel-connection-child + :documentation + "Connection on which the operations are +performed.") + (document :initarg :document + :type rudel-document-child + :documentation + "Document object to which operations refer.")) + "Provides operation methods which affect an associated +connection.") + +(defmethod rudel-insert ((this rudel-connection-operators) position data) + "Notify the connection associated to THIS of the insertion of DATA at POSITION." + (with-slots (connection document) this + (rudel-local-insert connection document position data))) + +(defmethod rudel-delete ((this rudel-connection-operators) position length) + "Notify the connection associated to THIS of a deletion of LENGTH at POSITION." + (with-slots (connection document) this + (rudel-local-delete connection document position length))) + + +;;; Class rudel-overlay-operators +;; + +(defclass rudel-overlay-operators () + ((document :initarg :document + :type rudel-document-child + :documentation + "The document to the overlays of which the +operations are applied") + (user :initarg :user + :type rudel-user-child + :documentation + "The user object associated to operations.")) + "Provides operation methods which affect the overlays of a +buffer.") + +(defmethod rudel-insert ((this rudel-overlay-operators) position data) + "Update the overlays associated to THIS to incorporate an insertion of DATA at POSITION." + (with-slots (document user) this + (with-slots (buffer) document + + ;; Since we inserted something, (point-max) is at least the + ;; length of the insertion + 1. So we can safely subtract the + ;; length of the insertion and 1. + (unless position + (with-current-buffer buffer + (setq position (- (point-max) (length data) 1)))) + + + (rudel-update-author-overlay-after-insert + buffer (+ position 1) (length data) user))) + ) + +(defmethod rudel-delete ((this rudel-overlay-operators) position length) + "Update the overlays associated to THIS to incorporate a deletion of LENGTH at POSITION." + (with-slots (document user) this + (with-slots (buffer) document + (rudel-update-author-overlay-after-delete + buffer (+ position 1) length user)))) + + +;;; Class rudel-hook-operators +;; + +(defclass rudel-hook-operators () + ((document :initarg :document + :type rudel-document-child + :documentation + "The document object to which operations refer.") + (user :initarg :user + :type rudel-user-child + :documentation + "The user object associated to operations.")) + "Provides operation methods which cause corresponding hooks to +be called.") + +(defmethod rudel-insert ((this rudel-hook-operators) position data) + "Call insert hook associated to THIS with POSITION and DATA." + (with-slots (document user) this + (with-slots (buffer) document + (run-hook-with-args 'rudel-insert-hook buffer user position data)))) + +(defmethod rudel-delete ((this rudel-hook-operators) position length) + "Call delete hook associated to THIS with POSITION and LENGTH." + (with-slots (document user) this + (with-slots (buffer) document + (run-hook-with-args 'rudel-delete-hook buffer user position length)))) + +(provide 'rudel-operators) +;;; rudel-operators.el ends here diff --git a/emacs.d/lisp/rudel/rudel-overlay.el b/emacs.d/lisp/rudel/rudel-overlay.el new file mode 100644 index 0000000..093afdf --- /dev/null +++ b/emacs.d/lisp/rudel/rudel-overlay.el @@ -0,0 +1,275 @@ +;;; rudel-overlay.el --- Overlay functions for Rudel +;; +;; Copyright (C) 2008, 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: rudel, overlay +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; + + +;;; History: +;; +;; 0.1 - Initial revision. + + +;;; Code: +;; + +(require 'custom) + +(eval-when-compile + (require 'cl)) + + +;;; Rudel overlay faces +;; + +(defcustom rudel-overlay-author-display t + "Indicate authorship by setting text color to user color." + :group 'rudel + :type 'boolean + :set (lambda (symbol value) + (set-default symbol value) + (when (featurep 'rudel-overlay) + (rudel-overlay-options-changed)))) + +(put 'rudel-overlay-author-display 'safe-local-variable t) + +(defface rudel-author-overlay-face + '((default (:background "black"))) + "*Face used to highlight contributions according to their authors. +Attributes involving color are not applied literally. Instead the +color is replaced with the color associated with the respective +author." + :group 'rudel) + + +;;; General overlay functions +;; + +(defun rudel-overlay-p (overlay) + "Non-nil if OVERLAY is a Rudel overlay." + (overlay-get overlay :rudel)) + +(defun rudel-overlay-length (overlay) + "Distance between end and start of OVERLAY." + (- (overlay-end overlay) (overlay-start overlay))) + +(defun rudel-overlay-user (overlay) + "User object associated to OVERLAY." + (overlay-get overlay :user)) + +(defun rudel-overlays (&optional predicate) + "Return a list of Rudel-related overlays or overlays satisfying PREDICATE. +If PREDICATE is non-nil returned overlays satisfy PREDICATES; +Otherwise all Rudel-related overlays are returned." + (unless predicate + (setq predicate #'rudel-overlay-p)) + + (let* ((overlay-lists (overlay-lists)) + (overlays (append (car overlay-lists) + (cdr overlay-lists)))) + (remove-if-not predicate overlays)) + ) + +(defun rudel-overlays-at (position &optional predicate) + "Return a list of Rudel-related overlays at POSITION. +If PREDICATE is non-nil returned overlays satisfy PREDICATES; +Otherwise all Rudel-related overlays are returned." + (unless predicate + (setq predicate #'rudel-overlay-p)) + (remove-if-not predicate (overlays-at position))) + +(defun rudel-overlays-in (start end &optional predicate) + "Return a list of Rudel-related overlays in the range START to END. +If PREDICATE is non-nil returned overlays satisfy PREDICATES; +Otherwise all Rudel-related overlays are returned." + (unless predicate + (setq predicate #'rudel-overlay-p)) + (remove-if-not predicate (overlays-in start end))) + +(defun rudel-overlays-remove-all () + "Remove all Rudel overlays from the current buffer." + (mapc #'delete-overlay (rudel-overlays))) + + +;;; Author overlay +;; + +(defun rudel-author-overlay-p (overlay) + "Predicate for author overlays." + (eq (overlay-get overlay :rudel) 'author)) + +(defun rudel-author-overlays () + "Return the list of author overlays in the current buffer." + (rudel-overlays #'rudel-author-overlay-p)) + +(defun rudel-author-overlay-at (position &optional author) + "" + (let ((overlays (rudel-overlays-at + position #'rudel-author-overlay-p))) + ;; There can only be one rudel overlay at any given position + (when overlays + (when (or (not author) + (eq (rudel-overlay-user (car overlays)) author)) + (car overlays)))) + ) + +(defun rudel-author-overlays-in (start end &optional author) + "" + (rudel-overlays-in + start end + (lambda (overlay) + (and (rudel-overlay-p overlay) + (or (not author) + (eq (rudel-overlay-user overlay) author))))) + ) + +(defun rudel-make-author-overlay (buffer from to author) + "Make and return an overlay for the range FROM to TO in BUFFER suitable for contributions by AUTHOR. +AUTHOR has to be an object of type rudel-user-child." + (let ((overlay (make-overlay from to buffer t))) + (rudel-overlay-author-set-properties overlay author) + overlay)) + +(defun rudel-overlay-author-set-properties (overlay author) + "Set properties of OVERLAY according to slots of AUTHOR. +AUTHOR has to be an object of type rudel-user-child." + (with-slots ((name :object-name) color) author + (overlay-put overlay :rudel 'author) + (overlay-put overlay :user author) + (overlay-put overlay 'face (when rudel-overlay-author-display + (rudel-overlay-make-face + (rudel-overlay-make-face-symbol + 'author name) + 'rudel-author-overlay-face + color))) + (overlay-put overlay 'help-echo (when rudel-overlay-author-display + (format "Written by %s" name)))) + ) + +(defun rudel-overlay-author-update (overlay) + "Update properties of OVERLAY from its attached user object." + (let ((author (rudel-overlay-user overlay))) + (rudel-overlay-author-set-properties overlay author))) + + +;;; Update functions for author overlays +;; + +(defun rudel-update-author-overlay-after-insert (buffer position length author) + "Update author overlays in BUFFER to incorporate an insertion of length LENGTH at POSITION by AUTHOR. +POSITION refers to an Emacs buffer position. +AUTHOR has to be an object of type rudel-author-child." + (when author + (with-current-buffer buffer + (let* ((end (+ position length)) + (before (when (> position 1) + (rudel-author-overlay-at (- position 1) author))) + (at (rudel-author-overlay-at position)) + (after (when (< end (point-max)) + (rudel-author-overlay-at (+ end 1) author)))) + (cond + ;; If there is an overlay, we have to split it unless the + ;; author is AUTHOR or we are on its boundary. + (at + (unless (eq (rudel-overlay-user at) author) + (let* ((on-start (= (overlay-start at) position)) + (on-end (= (- (overlay-end at) 1) position)) + (before (unless on-start + (if on-end at (copy-overlay at)))) + (after (unless on-end at))) + (when before + (move-overlay before (overlay-start before) position)) + (when after + (move-overlay after end (overlay-end after))) + (rudel-make-author-overlay buffer position end author)))) + ;; There is no overlay under the insert, but there are + ;; overlays of the same author immediately before and after + ;; the insert. We merge these two into one large overlay + ;; including the insert. + ((and before after) + (let ((end (overlay-end after))) + (delete-overlay after) + (move-overlay before (overlay-start before) end))) + ;; If there is an overlay of the same author before the + ;; insert, we extend it. + (before + (move-overlay before (overlay-start before) end)) + ;; If there is an overlay of the same author after the + ;; insert, we extend it. + (after + (move-overlay after position (overlay-end after))) + ;; If there are no overlays at all, we create a suitable one. + (t + (rudel-make-author-overlay buffer position end author)))))) + ) + +(defun rudel-update-author-overlay-after-delete (buffer position length author) + "Update author overlays in BUFFER to incorporate a deletion of length LENGTH at POSITION by AUTHOR. +POSITION refers to an Emacs buffer position. +AUTHOR has to be an object of type rudel-author-child." + (with-current-buffer buffer + (mapc + (lambda (overlay) + (when (zerop (rudel-overlay-length overlay)) + (delete-overlay overlay))) + (rudel-author-overlays-in position position))) + ) + + +;;; Miscellaneous functions +;; + +(defun rudel-overlay-make-face-symbol (category name) + "Allocate a symbol for a face for CATEGORY and NAME." + (intern (format "rudel-%s-overlay-%s-face" + (if (stringp category) + category + (symbol-name category)) + name))) + +(defun rudel-overlay-make-face (face template color) + "Copy TEMPLATE to FACE and replace color attributes with COLOR. +TEMPLATE has to be a face. FACE can be nil or a face. In the +latter case, FACE is returned unmodified." + (unless (facep face) + (make-face face) + (copy-face template face) + (rudel-overlay-set-face-attributes face color)) + face) + +(defun rudel-overlay-set-face-attributes (face color) + "Set color-related attributes of FACE with respect to COLOR." + (when (facep face) + (dolist (property '(:foreground :background :underline :overline)) + (unless (eq (face-attribute face property) 'unspecified) + (set-face-attribute face nil property color))))) + +(defun rudel-overlay-options-changed () + "Update Rudel overlays after a change of customization options." + (dolist (buffer (buffer-list)) + (with-current-buffer buffer + (mapc #'rudel-overlay-author-update (rudel-overlays))))) + +(provide 'rudel-overlay) +;;; rudel-overlay.el ends here diff --git a/emacs.d/lisp/rudel/rudel-protocol.el b/emacs.d/lisp/rudel/rudel-protocol.el new file mode 100644 index 0000000..c60718c --- /dev/null +++ b/emacs.d/lisp/rudel/rudel-protocol.el @@ -0,0 +1,83 @@ +;;; rudel-protocol.el --- Interface implemented by Rudel protocol backends +;; +;; Copyright (C) 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Rudel, backend, protocol +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; This file contains the interface for Rudel protocol backends. This +;; interface consists of the following functions: +;; +;; + joining sessions +;; + `rudel-ask-join-info' +;; + `rudel-join' +;; + hosting sessions +;; + `rudel-ask-host-info' +;; + `rudel-host' +;; + factory functions +;; + `rudel-make-document' + + +;;; History: +;; +;; 0.1 - Initial revision + + +;;; Code: +;; + +(require 'eieio) + +(require 'rudel-backend) + + +;;; Class rudel-protocol-backend +;; + +(defclass rudel-protocol-backend (rudel-backend) + () + "Interface implemented by protocol backends." + :abstract t) + +(defgeneric rudel-ask-join-info ((this rudel-protocol-backend)) + "Retrieve information for joining a session from user. +Return a property list that contains the collected information.") + +(defgeneric rudel-join ((this rudel-protocol-backend) info) + "Create a new connection according to the data in the property list INFO. +Implementations can rely on the fact that the property :session +contains the `rudel-session' object to which the new connection +will be associated.") + +(defgeneric rudel-ask-host-info ((this rudel-protocol-backend)) + "Retrieve information for hosting a session from user. +Return a property list that contains the collected information.") + +(defgeneric rudel-host ((this rudel-protocol-backend) info) + "Create a new session according to the property list INFO.") + +(defgeneric rudel-make-document ((this rudel-protocol-backend) + name session) + "Create a new document object named NAME for SESSION.") + +(provide 'rudel-protocol) +;;; rudel-protocol.el ends here diff --git a/emacs.d/lisp/rudel/rudel-session-initiation.el b/emacs.d/lisp/rudel/rudel-session-initiation.el new file mode 100644 index 0000000..395ab07 --- /dev/null +++ b/emacs.d/lisp/rudel/rudel-session-initiation.el @@ -0,0 +1,352 @@ +;;; rudel-session-initiation.el --- Session discovery and advertising functions +;; +;; Copyright (C) 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Rudel, session, initiation, service, discovery, advertising +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; Interfaces for session initiation and discovery. +;; +;; The central interface is +;; `rudel-session-initiation-backend'. Backends implementing this +;; interface can provide methods to discover sessions, to advertise +;; sessions, or both. +;; +;; The client programming interface consists of a priority which is +;; one of: +;; +;; + `primary' +;; + `fallback' +;; +;; and the following functions: +;; +;; + `rudel-session-initiation-discover' +;; + `rudel-session-initiation-advertise' +;; + `rudel-session-initiation-withdraw' + + +;;; History: +;; +;; 0.1 - Initial revision + + +;;; Code: +;; + +(eval-when-compile + (require 'cl)) + +(require 'eieio) + +(require 'rudel-backend) + + +;;; Customization options +;; + +(defcustom rudel-configured-sessions nil + "List of configured sessions. + +Each session is described as a plist (a list of keys and values +see Info node `(elisp)Property Lists'). Keys are specified using +keywords and look like this: :host, :username, :color. Values are +mostly strings, but symbols and numbers are possible as well. + +The following keys are required for any session: + +* :name (string) +* :backend (string or symbol) + +Other keys are optional and depend on the selected +backend. Required keys for which no value is specified will be +prompted for when selecting the session. The values of the :name +properties have to be distinct for all configured sessions. + +Additional keys required by most backends: + +* :host (string) +* :port (number) +* :username (string) +* :color (string) + +Here is a complete example of customized values for the obby +backend: + +* :name \"sonian\" +* :backend obby +* :host \"sobby\" +* :port 6522 +* :encryption t +* :username \"phil\" +* :color \"white\" +* :global-password \"\" (this means \"no password\") +* :user-password \"\" + +The programmatic equivalent looks like this: + +(add-to-list + 'rudel-configured-sessions + (list :name \"myserver\" + :backend 'obby + :host \"my.sobby-server.net\" + :username user-login-name + ;; Use M-x list-colors-display to see color choices. + :color \"white\" + :encryption t + :port 6522 + ;; empty string means no password + :global-password \"\" + :user-password \"\"))" + :group 'rudel + :type '(repeat :tag "Connections" + (plist :tag "Connection" + :options ((:name string) + (:backend symbol) + (:username string) + (:color color)))) + ) + + +;;; Variables and constants +;; + +(defvar rudel-session-discovered-hook nil + "This hook is run when collaboration sessions are discovered.") + +(defvar rudel-session-vanished-hook nil + "This hook is run when previously discovered collaboration +session disappear.") + + +;;; Class rudel-session-initiation-backend +;; + +(defclass rudel-session-initiation-backend (rudel-backend) + ((priority :initarg :priority + :type symbol + :accessor rudel-priority + :documentation + "Priority of the session initiation method +implemented by this backend. Has to be either 'primary or +'fallback")) + "Interface implemented by session initiation backends." + :abstract t) + +(defgeneric rudel-discover ((this rudel-session-initiation-backend)) + "Return a list of discovered sessions. +Each list element is a connect info property list. See +`rudel-join-session' for a description of the format of this +list. + +The presence of an implementation of this generic function should +be indicated by the presence of the 'discover' capability.") + +(defgeneric rudel-advertise ((this rudel-session-initiation-backend) info) + "Advertise session described by INFO. +INFO is a connect info property list. See `rudel-host-session' +for a description of the format of this list. + +The presence of an implementation of this generic function should +be indicated by the presence of the 'advertise' capability.") + + +;;; Client programming interface functions. +;; + +(defun rudel-session-initiation-suitable-backends (capability) + "Return primary and fallback backends that have CAPABILITY. +The returned list is of the form (PRIMARY FALLBACK), where +PRIMARY and FALLBACK are lists of backends of the respective +priority." + (let* (;; Select all backends, which can discover sessions + (suitable-backends (rudel-backend-suitable-backends + 'session-initiation + (lambda (backend) + (rudel-capable-of-p backend capability)))) + ;; Select primary backends + (primary-backends (remove* + 'fallback suitable-backends + :key (lambda (backend) + (rudel-priority (cdr backend))))) + ;; Select fallback backends + (fallback-backends (remove* + 'primary suitable-backends + :key (lambda (backend) + (rudel-priority (cdr backend)))))) + (list primary-backends fallback-backends)) + ) + +(defun rudel-session-initiation-discover (&optional backend-name) + "Return a list of session using BACKEND-NAME when non-nil. +BACKEND-NAME is a symbol. When it is non-nil, only the specified +backend is used to discover session. + +The returned list is of the form (INFO-1 ... INFO-N FALLBACK-1 +... FALLBACK-M) where INFO-I are connect info property lists (see +`rudel-join-session') and FALLBACK-I are conses of the form (NAME +. CLASS-OR-OBJECT) that specify fallback backends." + (multiple-value-bind (primary-backends fallback-backends) + (rudel-session-initiation-suitable-backends 'discover) + ;; Retrieve session list from primary backend and fall back to + ;; fallback backends if the list is empty. + (if backend-name + (let ((backend (find backend-name fallback-backends :key #'car))) + (rudel-discover (cdr backend))) + (let ((primary-results + (remove-if #'null + (apply #'append + (mapcar #'rudel-discover + (mapcar #'cdr primary-backends)))))) + (append primary-results fallback-backends)))) + ) + +(defun rudel-session-initiation-advertise (info) + "Advertise the session described by INFO. +INFO is a connect info property list. See `rudel-host-session' +for a description of the format of this list. + +Primary backends are tried first. If none succeeds, fallback +backends are tried. + +The result is non-nil if at least one backend was able to +advertise the session." + (multiple-value-bind (primary-backends fallback-backends) + (rudel-session-initiation-suitable-backends 'advertise) + (or ;; Try to advertise the session using primary backends. + (some (mapcar (lambda (backend) + (rudel-advertise backend info)) + (mapcar #'cdr primary-backends))) + ;; When the primary backends fail, try to advertise the + ;; session using fallback backends + (some (mapcar (lambda (backend) + (rudel-advertise backend info)) + (mapcar #'cdr fallback-backends))))) + ) + + +;;; Class rudel-ask-protocol-backend +;; + +(defconst rudel-ask-protocol-version '(0 1) + "Version of the ask-protocol backend for Rudel.") + +;;;###autoload +(defclass rudel-ask-protocol-backend (rudel-session-initiation-backend) + ((capabilities :initform (discover)) + (priority :initform fallback)) + "This fallback backend can \"discover\" sessions by letting the +user select a suitable backend and asking for connect information +required by the chosen backend.") + +(defmethod initialize-instance ((this rudel-ask-protocol-backend) + &rest slots) + "Set backend version." + (when (next-method-p) + (call-next-method)) + + (oset this :version rudel-ask-protocol-version)) + +(defmethod rudel-discover ((this rudel-ask-protocol-backend)) + "\"Discover\" sessions by asking the user about the backend to use and the connect info." + (let ((backend (rudel-backend-choose + 'protocol + (lambda (backend) + (rudel-capable-of-p backend 'join))))) + (list (append (list :name "asked" + :backend backend) + (rudel-ask-connect-info (cdr backend))))) + ) + +;;;###autoload +(rudel-add-backend (rudel-backend-get-factory 'session-initiation) + 'ask-protocol 'rudel-ask-protocol-backend) + + +;;; Class rudel-configured-sessions-backend +;; + +(defconst rudel-configured-sessions-version '(0 1) + "Version of the configured-sessions backend for Rudel.") + +;;;###autoload +(defclass rudel-configured-sessions-backend + (rudel-session-initiation-backend) + ((capabilities :initform (discover)) + (priority :initform primary)) + "This fallback backend can \"discover\" sessions the user has +configured using customization.") + +(defmethod initialize-instance ((this rudel-configured-sessions-backend) + &rest slots) + "Set backend version." + (when (next-method-p) + (call-next-method)) + + (oset this :version rudel-configured-sessions-version)) + +(defmethod rudel-discover ((this rudel-configured-sessions-backend)) + "\"Discover\" sessions the has configured." + ;; Iterate over all configured sessions in order to make + ;; adjustments. + (mapcar #'rudel-session-initiation-adjust-info + rudel-configured-sessions)) + +;;;###autoload +(rudel-add-backend (rudel-backend-get-factory 'session-initiation) + 'configured-sessions 'rudel-configured-sessions-backend) + + +;;; Miscellaneous functions +;; + +(defun rudel-session-initiation-adjust-info (info) + "Resolve arguments that need resolving in INFO." + ;; Start with a new, empty property list. + (let ((adjusted-info) + (key (car info)) + (value (cadr info)) + (rest info)) + ;; Iterate over all properties in INFO. + (while rest + (setq rest (cddr rest)) + (cond + ;; Resolve backend arguments. + ((eq key :backend) + (let ((backend (rudel-backend-get 'protocol + (if (stringp value) + (intern value) + value)))) + (push backend adjusted-info) + (push key adjusted-info))) + ;; Keep other arguments unmodified. + (t + (push value adjusted-info) + (push key adjusted-info))) + ;; Advance to next key value pair. + (setq key (car rest) + value (cadr rest))) + ;; Return the transformed session information. + adjusted-info) + ) + +(provide 'rudel-session-initiation) +;;; rudel-session-initiation.el ends here diff --git a/emacs.d/lisp/rudel/rudel-speedbar.el b/emacs.d/lisp/rudel/rudel-speedbar.el new file mode 100644 index 0000000..8c2caa8 --- /dev/null +++ b/emacs.d/lisp/rudel/rudel-speedbar.el @@ -0,0 +1,214 @@ +;;; rudel-speedbar.el --- Speedbar rendering of Rudel objects +;; +;; Copyright (C) 2008, 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: rudel, collaboration, speedbar +;; X-RCS: $Id: rudel-speedbar.el,v 1.32 2008/10/10 21:47:28 zappo Exp $ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + +;;; Commentary: +;; +;; This implements rendering of Rudel objects in speedbar. + + +;;; History: +;; +;; 0.1 - Initial revision. + + +;;; Code: +;; + +(require 'speedbar) +(require 'eieio-speedbar) + + +;;; Class rudel-user methods +;; + +(defmethod eieio-speedbar-description ((this rudel-user)) + "Provide a speedbar description for OBJ." + (format "User %s" (object-name-string this))) + +(defmethod eieio-speedbar-object-buttonname ((this rudel-user)) + "Return a string to use as a speedbar button for OBJECT." + (format "%s" (object-name-string this))) + + +;;; Class rudel-document methods +;; + +(defmethod eieio-speedbar-description ((this rudel-document)) + "Construct a description for from the name of document object THIS." + (format "Document %s" (object-name-string this))) + +(defmethod eieio-speedbar-object-buttonname ((this rudel-document)) + "Return a string to use as a speedbar button for OBJECT." + (rudel-unique-name this)) + +;;; Speedbar support mode +;; +(defun rudel-speedbar-make-map () + "Make the generic object based speedbar keymap." + (speedbar-make-specialized-keymap)) + +(defvar rudel-speedbar-key-map + (rudel-speedbar-make-map) + "A Generic object based speedbar display keymap.") + +(defvar rudel-speedbar-menu + '([ "Compile" rudel-speedbar-compile-line t]) + "Menu part in easymenu format used in speedbar while browsing objects.") + +(defun rudel-speedbar-toplevel-buttons (dir) + "Return a list of objects to display in speedbar. +Argument DIR is the directory from which to derive the list of objects." + (when rudel-current-session + (with-slots (users documents) rudel-current-session + (append users documents)))) + +(eieio-speedbar-create 'rudel-speedbar-make-map + 'rudel-speedbar-key-map + 'rudel-speedbar-menu + "Collaboration Session" + 'rudel-speedbar-toplevel-buttons) + +;;;###autoload +(defun rudel-speedbar () + "Show connected users and available documents of Rudel session in speedbar." + (interactive) + (speedbar-frame-mode 1) + (speedbar-change-initial-expansion-list "Collaboration Session") + (speedbar-get-focus)) + +;;; Speedbar Project Methods +;; +;; (defun rudel-find-nearest-file-line () +;; "Go backwards until we find a file." +;; (save-excursion +;; (beginning-of-line) +;; (looking-at "^\\([0-9]+\\):") +;; (let ((depth (string-to-number (match-string 1)))) +;; (while (not (re-search-forward "[]] [^ ]" +;; (save-excursion (end-of-line) +;; (point)) +;; t)) +;; (re-search-backward (format "^%d:" (1- depth))) +;; (setq depth (1- depth))) +;; (speedbar-line-token)))) +;; +;; (defmethod eieio-speedbar-derive-line-path ((obj rudel-session) &optional depth) +;; "Return the path to OBJ. +;; Optional DEPTH is the depth we start at." +;; "session" ;(file-name-directory (oref obj file)) +;; ) +;; +;; (defmethod eieio-speedbar-derive-line-path ((obj rudel-session) &optional depth) +;; "Return the path to OBJ. +;; Optional DEPTH is the depth we start at." +;; (let ((proj (rudel-target-parent obj))) +;; ;; Check the type of line we are currently on. +;; ;; If we are on a child, we need a file name too. +;; (save-excursion +;; (let ((lt (speedbar-line-token))) +;; (if (or (eieio-object-p lt) (stringp lt)) +;; (eieio-speedbar-derive-line-path proj) +;; ;; a child element is a token. Do some work to get a filename too. +;; (concat (eieio-speedbar-derive-line-path proj) +;; (rudel-find-nearest-file-line))))))) +;; +;; (defmethod eieio-speedbar-description ((obj rudel-session)) +;; "Provide a speedbar description for OBJ." +;; "bla") ;(rudel-description obj)) +;; +;; +;; (defmethod eieio-speedbar-object-buttonname ((object rudel-project)) +;; "Return a string to use as a speedbar button for OBJECT." +;; (if (rudel-parent-project object) +;; (rudel-name object) +;; (concat (rudel-name object) " " (oref object version)))) +;; +;; +;; (defmethod eieio-speedbar-object-children ((this rudel-project)) +;; "Return the list of speedbar display children for THIS." +;; (condition-case nil +;; (with-slots (subproj targets) this +;; (append subproj targets)) +;; (error nil))) +;; +;; +;; ;;; Generic file management for TARGETS +;; ;; +;; (defun rudel-file-find (text token indent) +;; "Find the file TEXT at path TOKEN. +;; INDENT is the current indentation level." +;; (speedbar-find-file-in-frame +;; (expand-file-name token (speedbar-line-directory indent))) +;; (speedbar-maybee-jump-to-attached-frame)) +;; +;; (defun rudel-create-tag-buttons (filename indent) +;; "Create the tag buttons associated with FILENAME at INDENT." +;; (let* ((lst (speedbar-fetch-dynamic-tags filename))) +;; ;; if no list, then remove expando button +;; (if (not lst) +;; (speedbar-change-expand-button-char ??) +;; (speedbar-with-writable +;; ;; We must do 1- because indent was already incremented. +;; (speedbar-insert-generic-list (1- indent) +;; lst +;; 'rudel-tag-expand +;; 'rudel-tag-find))))) +;; +;; (defun rudel-tag-expand (text token indent) +;; "Expand a tag sublist. Imenu will return sub-lists of specialized tag types. +;; Etags does not support this feature. TEXT will be the button +;; string. TOKEN will be the list, and INDENT is the current indentation +;; level." +;; (cond ((string-match "+" text) ;we have to expand this file +;; (speedbar-change-expand-button-char ?-) +;; (speedbar-with-writable +;; (save-excursion +;; (end-of-line) (forward-char 1) +;; (speedbar-insert-generic-list indent token +;; 'rudel-tag-expand +;; 'rudel-tag-find)))) +;; ((string-match "-" text) ;we have to contract this node +;; (speedbar-change-expand-button-char ?+) +;; (speedbar-delete-subblock indent)) +;; (t (error "Ooops... not sure what to do"))) +;; (speedbar-center-buffer-smartly)) +;; +;; (defun rudel-tag-find (text token indent) +;; "For the tag TEXT in a file TOKEN, goto that position. +;; INDENT is the current indentation level." +;; (let ((file (rudel-find-nearest-file-line))) +;; (speedbar-find-file-in-frame file) +;; (save-excursion (speedbar-stealthy-updates)) +;; ;; Reset the timer with a new timeout when cliking a file +;; ;; in case the user was navigating directories, we can cancel +;; ;; that other timer. +;; ; (speedbar-set-timer speedbar-update-speed) +;; (goto-char token) +;; (run-hooks 'speedbar-visiting-tag-hook) +;; ;;(recenter) +;; (speedbar-maybee-jump-to-attached-frame) +;; )) + +(provide 'rudel-speedbar) + +;;; rudel-speedbar.el ends here diff --git a/emacs.d/lisp/rudel/rudel-state-machine.el b/emacs.d/lisp/rudel/rudel-state-machine.el new file mode 100644 index 0000000..d96bcc7 --- /dev/null +++ b/emacs.d/lisp/rudel/rudel-state-machine.el @@ -0,0 +1,331 @@ +;;; rudel-state-machine.el --- A simple state machine for Rudel +;; +;; Copyright (C) 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Rudel, FSM +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; This is a simple implementation of a finite state machine +;; (FSM). The is modeled by rudel-state-machine class, objects of +;; which contain state objects of classes derived from +;; rudel-state. There are no explicit transition rules, since states +;; specify their successors. + + +;;; History: +;; +;; 0.1 - Initial revision + + +;;; Code: +;; + +(eval-when-compile + (require 'cl)) + +(require 'eieio) + +(require 'rudel-errors) +(require 'rudel-compat) ;; for pulsing progress reporter + + +;;; Errors related to the state machine +;; + +;; rudel-state-error + +(intern "rudel-state-error") + +(put 'rudel-state-error 'error-conditions + '(error + rudel-error rudel-state-error)) + +(put 'rudel-state-error 'error-message + "Invalid state or state transition") + +;; rudel-invalid-successor-state + +(intern "rudel-invalid-successor-state") + +(put 'rudel-invalid-successor-state 'error-conditions + '(error + rudel-error rudel-state-error rudel-invalid-successor-state)) + +(put 'rudel-invalid-successor-state 'error-message + "Invalid successor state in state transition") + +;; rudel-entered-error-state + +(intern "rudel-entered-error-state") + +(put 'rudel-entered-error-state 'error-conditions + '(error + rudel-error rudel-state-error rudel-entered-error-state)) + +(put 'rudel-entered-error-state 'error-message + "Transition to error state") + +;; rudel-no-start-state + +(intern "rudel-no-start-state") + +(put 'rudel-no-start-state 'error-conditions + '(error + rudel-error rudel-state-error rudel-no-start-state)) + +(put 'rudel-no-start-state 'error-message + "No start state specified for state machine") + + +;;; Class rudel-state +;; + +(defclass rudel-state () + () + "A state that can be used in state machines." + :abstract t) + +(defgeneric rudel-accept ((this rudel-state) &rest arguments) + "Executed when the machine receives an event while in state THIS.") + +(defgeneric rudel-enter ((this rudel-state) &rest arguments) + "Executed when the machine switches to state THIS.") + +(defgeneric rudel-leave ((this rudel-state)) + "Executed when the machine leaves state THIS.") + + +;;; Class rudel-state-machine +;; + +(defclass rudel-state-machine () + ((states :initarg :states + :type list ;; alist + :initform nil + :documentation + "A list (NAME . STATE) conses where NAME is a symbol +and STATE is an object of a class derived from rudel-state.") + (state :initarg :state + :type rudel-state-child + :documentation + "The current state of the machine.")) + "A finite state machine.") + +(defmethod initialize-instance :after ((this rudel-state-machine) + &rest slots) + "Set current state of THIS to a proper initial value. +If a start state is specified in the arguments to the +constructor, that state is used. If there is no such state, the +list of states is search for a state named start. If that fails +as well, the first state in the state list is used." + (with-slots (states) this + ;; Find a suitable start state and switch to it. + (let ((start (or (plist-get slots :start) + (car (assoc 'start states)) + (when (length states) + (car (nth 0 states)))))) + (unless start + (signal 'rudel-no-start-state nil)) + ;; Make start state the current state and call send an enter + ;; message. + (let ((start (cdr (assoc start states)))) + (oset this :state start) + (rudel--switch-to-return-value + this start (rudel-enter start))))) + ) + +(defmethod rudel-find-state ((this rudel-state-machine) name) + "Return state object for symbol NAME." + (with-slots (states) this + (cdr (assoc name states)))) + +(defmethod rudel-register-state ((this rudel-state-machine) name state) + "Register STATE and its NAME with THIS state machine." + (object-add-to-list this :states (cons name state) t)) + +(defmethod rudel-register-states ((this rudel-state-machine) states) + "Register STATES with THIS state machine. +STATES is a list of cons cells whose car is a symbol - the name +of the state - and whose cdr is a class." + (dolist (symbol-and-state states) + (destructuring-bind (name . class) symbol-and-state + (rudel-register-state + this name (make-instance class (symbol-name name))))) + ) + +(defmethod rudel-current-state ((this rudel-state-machine) &optional object) + "Return name and, optionally, state object of the current state of THIS. +If OBJECT is non-nil, (NAME . OBJECT) is returned. Otherwise, +just NAME." + (with-slots (states state) this + (let ((state-symbol (car (find state states :key #'cdr :test #'eq)))) + (if object + (cons state-symbol state) + state-symbol))) + ) + +(defmethod rudel-accept ((this rudel-state-machine) &rest arguments) + "Process an event described by ARGUMENTS." + (with-slots (state) this + ;; Let the current state decide which state is next. + (let ((next (apply #'rudel-accept state arguments))) + (cond + ;; If NEXT is nil, a symbol or a state object, we switch states + ;; without passing any data. + ((or (null next) (symbolp next) (rudel-state-child-p next)) + (rudel-switch this next)) + + ;; If NEXT is a list, it contains the symbol of the successor + ;; state and additional data. + ((listp next) + (apply #'rudel-switch this next)) + + ;; Other types cannot be processed. + (t + (signal 'wrong-type-argument (list (type-of next))))))) + ) + +(defmethod rudel-switch ((this rudel-state-machine) next + &rest arguments) + "Leave current state and switch to state NEXT. +ARGUMENTS are passed to the `rudel-enter' method of the successor +state." + (with-slots (states state) this + (cond + ;; When NEXT is a state object, use it. + ((rudel-state-child-p next)) + + ;; When NEXT is nil, stay in the current state. + ((null next) + (setq next state)) + + ;; When NEXT is a symbol (but not nil), look up the corresponding + ;; state. Signal an error, if there is none. + ((symbolp next) + (let ((next-state (assoc next states))) + (unless next-state + (signal 'rudel-invalid-successor-state + (list next '<- state))) + (setq next (cdr next-state)))) + + ;; Other types cannot be processed. + (t + (signal 'wrong-type-argument (list (type-of next))))) + + ;; Unless the successor state is equal to the current state, leave + ;; the current state and switch to the successor. + (if (and (eq state next) + (null arguments)) + ;; Return state + state + ;; Notify (old) current state. + (rudel-leave state) + ;; Update current state. + (setq state next) + ;; Notify (new) current state. Using the call's value as next + ;; state is a bit dangerous since a long sequence of immediate + ;; state switches could exhaust the stack. + (rudel--switch-to-return-value + this state (apply #'rudel-enter state arguments)))) + ) + +(defmethod rudel--switch-to-return-value ((this rudel-state-machine) + state next) + "Switch from STATE to the next state indicated by NEXT. +STATE is the current state. +NEXT can nil, a list or a `rudel-state' object." + (cond + ;; Remain in current state. + ((null next) + state) + ;; NEXT contains next state and arguments to pass to it when + ;; switching. + ((listp next) + (apply #'rudel-switch this next)) + ;; Otherwise NEXT is a `rudel-state' object. + (t + (rudel-switch this next))) + ) + +(defmethod object-print ((this rudel-state-machine) &rest strings) + "Add current state to the string representation of THIS." + (if (slot-boundp this 'state) + (with-slots (state) this + (apply #'call-next-method + this + (format " state: %s" + (object-name-string state)) + strings)) + (call-next-method this " state: #start")) + ) + + +;;; Miscellaneous functions +;; + +(defun rudel-state-wait (machine success-states + &optional error-states callback) + "Repeatedly call CALLBACK until MACHINE is in a state in SUCCESS-STATES or ERROR-STATES. +MACHINE should be of type rudel-state-machine-child or at least +have a method `rudel-get-state'. + +SUCCESS-STATES and ERROR-STATES are lists which contain the +names (as symbols) of success and error states respectively. +This function does not return when MACHINE enters states not in +SUCCESS-STATES or ERROR-STATES. As a result, a deadlock can occur +when MACHINE deadlocks or cycles through states not in either +list infinitely. + +When non-nil, CALLBACK has to be a function that accepts one +argument of the form (SYMBOL . STATE) where SYMBOL is the name +symbol of the current state and STATE is the state object." + ;; Wait until MACHINE enter a state in SUCCESS-STATES or + ;; ERROR-STATES. + (let ((result + (catch 'state-wait + (while t + ;; Retrieve current state. + (destructuring-bind (symbol . state) + (rudel-current-state machine t) + + ;; Check against success and error states. + (when (memq symbol success-states) + (throw 'state-wait (cons 'success (cons symbol state)))) + (when (memq symbol error-states) + (throw 'state-wait (cons 'error (cons symbol state)))) + + ;; Update progress indicator and sleep. + (when callback + (funcall callback (cons symbol state))) + (sleep-for 0.05)))))) + (when callback + (funcall callback t)) + + ;; If MACHINE ended up in an error state, signal + (unless (eq (car result) 'success) + (signal 'rudel-entered-error-state (cdr result))) + ;; Return state + (cdr result)) + ) + +(provide 'rudel-state-machine) +;;; rudel-state-machine.el ends here diff --git a/emacs.d/lisp/rudel/rudel-tls.el b/emacs.d/lisp/rudel/rudel-tls.el new file mode 100644 index 0000000..a770851 --- /dev/null +++ b/emacs.d/lisp/rudel/rudel-tls.el @@ -0,0 +1,201 @@ +;;; rudel-tls.el --- Start TLS protocol. +;; +;; Copyright (C) 2008, 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Rudel, TLS, encryption, starttls, gnutls +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 rudel. If not, see . + + +;;; Commentary: +;; +;; This contains a simple implementation of the so calls Start TLS +;; protocol, which means enabling TLS encryption for an existing +;; network connection. + + +;;; History: +;; +;; 0.1 - Initial revision. + + +;;; Code: +;; + +(require 'format-spec) + +(require 'rudel) +(require 'rudel-util) + + +;;; Customization +;; + +(defcustom rudel-tls-client-program + "gnutls-cli" + "The gnutls client program to use for encrypted connections." + :group 'rudel + :type 'file) + +(defcustom rudel-tls-client-arguments + "--starttls --kx ANON_DH --port %p %h" + "Arguments passed to the gnutls client program." + :group 'rudel + :type 'string) + + +;;; TLS functions +;; + +(defun rudel-tls-make-process (&rest args) + "Make a network process with keyword arguments ARGS. +This function works similar to `make-network-process'. Supported +keyword arguments are :name (ignore), :host, :port, :filter +and :sentinel. The returned process object is suitable for +start-TLS. Once the enclosing protocol indicates TLS encryption +should start, `rudel-tls-start-tls' can be called to enabled TLS +for the network connection." + (let* ((host (plist-get args :host)) ;; TODO clumsy + (port (plist-get args :service)) + (filter (plist-get args :filter)) + (sentinel (plist-get args :sentinel)) + ;; Compile the command to start the TLS binary. + (arguments (format-spec rudel-tls-client-arguments + (format-spec-make + ?h host + ?p (number-to-string port)))) + ;; Start the TLS program. + (process (apply #'start-process + (format "*tls-%s*" host) nil + rudel-tls-client-program + (split-string arguments " ")))) + + ;; Store filter function and attach proxy filter to handle TLS + ;; handshake. + (when filter + (rudel-set-process-object process filter :old-filter)) + (set-process-filter process #'rudel-tls-wait-init) + + ;; Attach sentinel function. + (when sentinel + (set-process-sentinel process sentinel)) + + ;; Mark the process as supporting TLS encryption + (rudel-set-process-object process t :supports-tls) + + process) + ) + +(defun rudel-tls-start-tls (process) + "Enable TLS encryption for the network connection PROCESS. +This only works if PROCESS has been created by +`rudel-tls-make-process'." + ;; Save current filter function. + (rudel-set-process-object + process (process-filter process) :old-filter) + ;; Install TLS handshake filter function and signal program to start + ;; TLS handshake. + (message "tls-start-tls: switching to \"handshake\" filter") + (set-process-filter process #'rudel-tls-wait-handshake) + (signal-process process 'sigalrm) + ) + +(defun rudel-tls-wait-init (process data) + "Is installed as process filter on PROCESS until gnutls is done printing messages." + ;; Retrieve complete lines. + (let ((buffer (rudel-process-object process :buffer))) + (rudel-assemble-line-fragments data buffer) + (rudel-set-process-object process buffer :buffer)) + + (let ((client-data) + (old-filter (rudel-process-object process :old-filter)) + (client-mode)) + + ;; Assemble lines that were not generated by gnutls. It is very + ;; brittle to wait for last line of gnutls output like, but it + ;; cannot be helped. + (rudel-loop-lines data line + (if client-mode + (setq client-data (concat client-data line "\n")) + (when (string-match-p "- Simple Client Mode.*" line) + (setq client-mode t)))) + + ;; When there are any lines not generated by gnutls, + ;; initialization is over. Process the data and install the old + ;; filter function. + (when client-data + (funcall old-filter process client-data)) + (when client-mode + (message "tls-wait-init: switching back to old filter") + (set-process-filter process old-filter))) + ) + +(defun rudel-tls-wait-handshake (process data) + "Is installed as process filter on PROCESS while gnutls is doing the TLS handshake." + ;; Retrieve complete lines. + (let ((buffer (rudel-process-object process :buffer))) + (rudel-assemble-line-fragments data buffer) + (rudel-set-process-object process buffer :buffer)) + + (let ((client-data) + (old-filter (rudel-process-object process :old-filter)) + (client-mode)) + + ;; Assemble lines that were not generated by gnutls. It is very + ;; brittle to wait for last line of gnutls output like, but it + ;; cannot be helped. + (rudel-loop-lines data line + (if client-mode + (setq client-data (concat client-data line "\n")) + (when (string-match-p "- Compression.*" line) + (setq client-mode t)))) + + ;; When there are any lines not generated by gnutls, handshake is + ;; over. Process the data and install `established' filter + ;; function. + (when client-data + (funcall old-filter process client-data)) + (when client-mode + (message "tls-wait-handshake: switching to \"established\" filter") + (set-process-filter process #'rudel-tls-established) + (rudel-set-process-object process t :encryption))) + ) + +(defun rudel-tls-established (process data) + "Is installed as process filter on PROCESS after gnutls has established TLS encryption." + ;; Retrieve complete lines. + (let ((buffer (rudel-process-object process :buffer))) + (rudel-assemble-line-fragments data buffer) + (rudel-set-process-object process buffer :buffer)) + + (let ((client-data) + (old-filter (rudel-process-object process :old-filter))) + + ;; Assemble lines that were not generated by gnutls. + (rudel-loop-lines data line + (unless (string-match-p "- Peer has closed the GNUTLS connection" line) + (setq client-data (concat client-data line "\n")))) + + ;; When there are any lines not generated by gnutls, pass those to + ;; the old filter function. + (when client-data + (funcall old-filter process client-data))) + ) + +(provide 'rudel-tls) +;;; rudel-tls.el ends here diff --git a/emacs.d/lisp/rudel/rudel-transport.el b/emacs.d/lisp/rudel/rudel-transport.el new file mode 100644 index 0000000..8f0b21d --- /dev/null +++ b/emacs.d/lisp/rudel/rudel-transport.el @@ -0,0 +1,72 @@ +;;; rudel-transport.el --- Rudel transport interface and backend +;; +;; Copyright (C) 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Rudel, backend, transport +;; X-RCS: $Id:$ +;; +;; This program 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. +;; +;; This program 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 this program. If not, see . + + +;;; Commentary: +;; +;; This file contains the interface for Rudel transport backends. + + +;;; History: +;; +;; 0.1 - Initial revision + + +;;; Code: +;; + +(require 'eieio) + +(require 'rudel-backend) + + +;;; Class rudel-transport +;; + +(defclass rudel-transport () + () + "Interface for transport objects.") + +(defgeneric rudel-transport-send ((this rudel-transport) data) + "Send DATA through THIS transport object.") + +(defgeneric rudel-transport-set-handler ((this rudel-transport) + handler) + "Install HANDLER as dispatcher for messages received by THIS.") + + +;;; Class rudel-transport-backend +;; + +(defclass rudel-transport-backend (rudel-backend) + () + "Interface implemented by transport backends." + :abstract t) + +(defgeneric rudel-make-connection ((this rudel-transport-backend) info) + "Create a transport object according to INFO.") + +(defgeneric rudel-wait-for-connections ((this rudel-transport-backend) + info) + "Create a transport object according to INFO.") + +(provide 'rudel-transport) +;;; rudel-transport.el ends here diff --git a/emacs.d/lisp/rudel/rudel-util.el b/emacs.d/lisp/rudel/rudel-util.el new file mode 100644 index 0000000..12a967c --- /dev/null +++ b/emacs.d/lisp/rudel/rudel-util.el @@ -0,0 +1,261 @@ +;;; rudel-util.el --- Miscellaneous functions for Rudel +;; +;; Copyright (C) 2008, 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: rudel, miscellaneous, util +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; This file contains miscellaneous functions for Rudel. + + +;;; History: +;; +;; 0.1 - Initial revision. + + +;;; Code: +;; + +(eval-when-compile + (require 'cl)) + +(require 'eieio) + +(require 'rudel-errors) + + +;;; Errors +;; + +;; rudel-dispatch-error + +(intern "rudel-dispatch-error") + +(put 'rudel-dispatch-error 'error-conditions + '(error + rudel-error rudel-dispatch-error)) + +(put 'rudel-dispatch-error 'error-message + "Could not dispatch message to handler") + + +;;; Class rudel-hook-object +;; + +(defclass rudel-hook-object () + () + "Mixin for classes that offer one or more hooks for each of +their objects. + +This idiom is usually called something like signal/slot or +event/subscription, but for Emacs, the notion of hooks seems more +appropriate." + :abstract t) + +(defmethod object-add-hook ((this rudel-hook-object) + hook function &optional append) + "Add FUNCTION to HOOK for THIS. +If APPEND is non-nil FUNCTION becomes the last element in the +list of hooks." + (let ((value (slot-value this hook))) + (unless (member function value) + (set-slot-value this hook + (if append (append value (list function)) + (cons function value))))) + ) + +(defmethod object-remove-hook ((this rudel-hook-object) + hook function) + "Remove FUNCTION from HOOK for THIS." + (set-slot-value this hook + (remove function (slot-value this hook)))) + +(defmethod object-run-hook-with-args ((this rudel-hook-object) + hook &rest arguments) + "Run HOOK of THIS with arguments ARGUMENTS." + (let ((hook (slot-value this hook))) + (apply #'run-hook-with-args 'hook this arguments))) + + +;;; Class rudel-socket-owner +;; + +(defclass rudel-socket-owner () + ((socket :initarg :socket + :type process + :documentation + "The process object representing the socket through +which the communication happens.")) + "Class rudel-socket-owner ") + +(defmethod initialize-instance :after ((this rudel-socket-owner) + &rest slots) + "Attach THIS to as process object of our socket." + ;; Attach to our socket. + (with-slots (socket) this + (rudel-set-process-object socket this)) + ) + +(defmethod rudel-disconnect ((this rudel-socket-owner)) + "Disconnect the network connection owned by THIS." + (with-slots (socket) this + (delete-process socket))) + +(defmethod rudel-state-change ((this rudel-socket-owner) state message) + "Called when the state of THIS changes to STATE. +MESSAGE is the message emitted when the state transition +occurred." + (with-slots (socket) this + (case state + ;; Nothing to do here. + (run + nil) + + ;; Dispatch events which indicate the termination of the + ;; connection to `rudel-close'. + ((closed failed exit) + (rudel-close this)))) + ) + +(defmethod rudel-close ((this rudel-socket-owner)) + "Called when the connection associated to THIS is closed.") + + +;;; Networking helper functions and macros +;; + +(defun rudel-process-object (process &optional key) + "Return the object attached to PROCESS using identifier KEY." + (unless key + (setq key :object)) + (get (intern (process-name process)) key)) + +(defun rudel-set-process-object (process object &optional key) + "Set object attached to PROCESS using identifier KEY to OBJECT." + (unless key + (setq key :object)) + (put (intern (process-name process)) key object)) + +(defun rudel-filter-dispatch (process data) + "Call `rudel-receive' method of object attached to PROCESS with DATA." + (let ((object (rudel-process-object process))) + (rudel-receive object data))) + +(defun rudel-sentinel-dispatch (process message) + "Call `rudel-state-change' method of the object attached to PROCESS with state and MESSAGE." + (let ((object (rudel-process-object process)) + (state (process-status process))) + (rudel-state-change object state message))) + + +;;; Fragmentation and assembling functions. +;; + +(defmacro rudel-assemble-line-fragments (data storage) + "Find an return complete lines in DATA, store excess data in STORAGE. +If STORAGE is non-nil when calling, consider content as leftover +data from last and concatenate with DATA before processing." + (declare (debug (form form))) + (let ((index (make-symbol "index"))) + `(progn + ;; If there are stored fragments, append them to the new data. + (when ,storage + (setq ,data (concat ,storage ,data)) + (setq ,storage nil)) + ;; Try to find a line break in the augmented data. + (let ((,index (position ?\n ,data :from-end t))) + (unless (and ,index (eq ,index (- (length ,data) 1))) + (setq ,storage (if ,index + (substring ,data (+ ,index 1)) + ,data)) + (setq ,data (when ,index + (substring ,data 0 (+ ,index 1)))))) + ,data)) + ) + +(defmacro rudel-loop-lines (data var &rest forms) + "Execute FROMS with VAR subsequently bound to all lines in DATA." + (declare (indent 2) + (debug (form symbolp &rest form))) + (let ((lines (make-symbol "lines"))) + `(when ,data + (let ((,lines (split-string ,data "\n" t))) + (dolist (,var ,lines) + (progn ,@forms))))) + ) + +(defmacro rudel-loop-chunks (data var size &rest forms) + "Execute FROMS in a loop with VAR bound to chunks of DATA of SIZE. +Unless (zerop (mod (length data) size) 0) the final chunk is +truncated. The expression SIZE is evaluated in each loop unless +it is a number." + (declare (indent 3) + (debug (form symbolp numberp &rest form))) + ;; If we got a constant number as SIZE, we can check right away. + (when (and (numberp size) (<= size 0)) + (error "Size should be positive")) + + (let ((rest (make-symbol "rest")) + (amount (make-symbol "amount")) + ;; If SIZE has to be evaluated, we have to check at runtime. + (check (unless (numberp size) + `((when (<= ,size 0) + (error "Size should be positive")))))) + `(progn + ,@check + (let ((,rest ,data) + (,var) + (,amount)) + (while (not (string= ,rest "")) + (setq ,amount (min (length ,rest) ,size) + ,var (substring ,rest 0 ,amount) + ,rest (substring ,rest ,amount)) + (progn ,@forms))))) + ) + + +;;; Miscellaneous functions +;; + +(defun rudel-dispatch (object prefix name arguments) + "Call method (concat PREFIX NAME) of OBJECT with ARGUMENTS. +If no such method can be found, the condition +rudel-dispatch-error is signalled." + ;; Construct a matching symbol. + (let* ((method (intern-soft (concat prefix name)))) + ;; If we found a suitable method, run it; Otherwise signal. + (unless method + (signal 'rudel-dispatch-error 'method-symbol-unbound)) + (condition-case error + ;; Try to call METHOD. This can still fail when METHOD is not + ;; defined for the class of OBJECT. + (apply method object arguments) + ;; Only handle a condition 'no-method-definition' that refers to + ;; METHOD, otherwise continue unwinding. + (no-method-definition + (if (eq method (cadr error)) + (signal 'rudel-dispatch-error 'no-method-for-object) + (signal (car error) (cdr error)))))) + ) + +(provide 'rudel-util) +;;; rudel-util.el ends here diff --git a/emacs.d/lisp/rudel/rudel.el b/emacs.d/lisp/rudel/rudel.el new file mode 100644 index 0000000..88603fa --- /dev/null +++ b/emacs.d/lisp/rudel/rudel.el @@ -0,0 +1,1012 @@ +;;; rudel.el --- A collaborative editing framework for Emacs +;; +;; Copyright (C) 2008, 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: rudel, collaboration +;; URL: http://rudel.sourceforge.net/ +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 rudel. If not, see . + + +;;; Commentary: +;; +;; Rudel is a framework for collaborative editing in Emacs. Its +;; architecture allows communication with arbitrary collaborative +;; editors. + + +;;; History: +;; +;; 0.1 - Initial revision. + + +;;; Code: +;; + +(eval-when-compile + (require 'cl)) + +(require 'eieio) +(require 'eieio-base) + +(require 'eieio-speedbar) ;; TODO required for now + +(require 'rudel-util) +(require 'rudel-backend) +(require 'rudel-session-initiation) +(require 'rudel-operations) +(require 'rudel-operators) +(require 'rudel-overlay) +(require 'rudel-hooks) +(require 'rudel-interactive) ;; for `rudel-read-backend', + ;; `rudel-read-document', + ;; `rudel-read-session' +(require 'rudel-icons) +(require 'rudel-compat) ;; for `read-color' replacement + + +;;; Global variables +;; + +(defconst rudel-version '(0 3) + "Version of the Rudel framework.") + +(defvar rudel-current-session nil + "Global object representing the current Rudel session. +nil if there is no active session.") + +(defvar rudel-buffer-document nil + "Buffer-local variable which holds the Rudel document associated with the buffer.") +(make-variable-buffer-local 'rudel-buffer-document) +(put 'rudel-buffer-document 'permanent-local t) + +(defvar rudel-buffer-change-workaround-data nil + "Buffer-local variable which holds change data that could not be accessed otherwise. +It would be nice to find another way to do this.") +(make-variable-buffer-local 'rudel-buffer-change-workaround-data) +(put 'rudel-buffer-change-workaround-data 'permanent-local t) + + +;;; Customization +;; + +(defgroup rudel nil + "Rudel collaborative editing framework." + :group 'applications) + +(defcustom rudel-allocate-buffer-function + 'rudel-allocate-buffer-clear-existing + "A function used to find or create buffers to associate to documents. +The function is called with the document name as the sole +argument and has to return a buffer object which will be attached +to the document in question." + :group 'rudel + :type '(choice (const :tag "Clear content of existing buffer" + rudel-allocate-buffer-clear-existing ) + (const :tag "Create a new uniquely named buffer" + rudel-allocate-buffer-make-unique ) + (function :tag "Other function")) + :require 'rudel-interactive) + +(defcustom rudel-default-username (user-login-name) + "*" + :group 'rudel + :type '(string)) + + +;;; Class rudel-session +;; + +(defclass rudel-session (rudel-hook-object) + ((backend :initarg :backend + :type rudel-backend-child + :documentation + "The backend used by this session.") + (users :initarg :users + :type list + :initform nil + :documentation + "The list of users participating in this +session.") + (documents :initarg :documents + :type list + :initform nil + :documentation + "This list of documents available in +this session.") + ;; Hooks + (end-hook :initarg :end-hook + :type list + :initform nil + :documentation + "") + (add-user-hook :initarg :add-user-hook + :type list + :initform nil + :documentation + "This hook is run when a user gets added +to the session. +The arguments are the session and the user object.") + (remove-user-hook :initarg :remove-user-hook + :type list + :initform nil + :documentation + "This hook is run when a user gets +removed from the session. +The arguments are the session and the user object.") + (add-document-hook :initarg :add-document-hook + :type list + :initform nil + :documentation + "This hook is run when a document gets +added to the session. +The arguments are the session and the document object.") + (remove-document-hook :initarg :remove-document-hook + :type list + :initform nil + :documentation + "This hook is run when a document gets +removed from the session. +The arguments are the session and the document object.")) + "This class serves as a base class for rudel-client-session and +rudel-server-session. Consequently, it consists of slots common +to client and server sessions." + :abstract t) + +(defmethod rudel-end ((this rudel-session)) + "Terminate THIS session performing all necessary cleanup." + ;; Run the hook. + (object-run-hook-with-args this 'end-hook)) + +(defmethod rudel-add-user ((this rudel-session) user) + "Add USER to the user list of THIS session. + +Runs object hook (see `rudel-hook-object') `add-user-hook' with +arguments THIS and USER." + ;; Add USER to list. + (object-add-to-list this :users user) + + ;; Run the hook. + (object-run-hook-with-args this 'add-user-hook user)) + +(defmethod rudel-remove-user ((this rudel-session) user) + "Remove USER from the user list of THIS session. + +Runs object hook (see `rudel-hook-object') `remove-user-hook' +with arguments THIS and USER." + ;; Remove USER from list. + (object-remove-from-list this :users user) + + ;; Run the hook. + (object-run-hook-with-args this 'remove-user-hook user)) + +(defmethod rudel-find-user ((this rudel-session) + which &optional test key) + "Find user WHICH in the user list. +WHICH is compared to the result of KEY using TEST." + (unless test + (setq test #'string=)) + (unless key + (setq key #'object-name-string)) + (with-slots (users) this + (find which users :key key :test test)) + ) + +(defmethod rudel-add-document ((this rudel-session) document) + "" + (unless (slot-boundp document :session) + (oset document :session this)) + + ;; Add DOCUMENT to the list of documents in THIS session. + (object-add-to-list this :documents document) + + ;; Run the hook. + (object-run-hook-with-args this 'add-document-hook document)) + +(defmethod rudel-remove-document ((this rudel-session) document) + "Remove DOCUMENT from THIS session, detaching it if necessary." + ;; Detach document from its buffer when necessary. + (when (rudel-attached-p document) + (rudel-detach-from-buffer document)) + + ;; Remove DOCUMENT from the list of documents in THIS session. + (object-remove-from-list this :documents document) + + ;; Run the hook. + (object-run-hook-with-args this 'remove-document-hook document)) + +(defmethod rudel-find-document ((this rudel-session) + which &optional test key) + "Find document WHICH in the document list. +WHICH is compared to the result of KEY using TEST." + (unless test + (setq test #'string=)) + (unless key + (setq key #'object-name-string)) + (with-slots (documents) this + (find which documents :key key :test test)) + ) + +(defmethod rudel-unsubscribed-documents ((this rudel-session)) + "" + (unless (slot-boundp this :self) + (error "Cannot find unsubscribed documents unless slot self is bound")) + (with-slots (documents self) this + (remove-if + (lambda (document) + (with-slots (subscribed) document + (memq self subscribed))) + documents)) + ) + + +;;; Class rudel-client-session +;; +(defclass rudel-client-session (rudel-session) + ((connection :initarg :connection + :type (or null rudel-connection-child) + :initform nil + :documentation + "The connection used for communication by this +session.") + (self :initarg :self + :type rudel-user-child + :documentation + "Points into USERS to the user object representing +the local user")) + "Objects represent a collaborative editing session from a +client perspective.") + +(defmethod rudel-end ((this rudel-client-session)) + ;; Clean everything up + (with-slots (connection users documents) this + ;; Detach all documents from their buffers + (mapc #'rudel-detach-from-buffer documents) + + ;; Terminate the connection + (when connection + (condition-case nil + (rudel-disconnect connection) + (error nil)))) + + ;; + (when (next-method-p) + (call-next-method)) + ) + + +;;; Class rudel-server-session +;; + +(defclass rudel-server-session (rudel-session) + () + "Class rudel-server-session " + :abstract t) + + +;;; Class rudel-connection +;; + +(defclass rudel-connection () + ((session :initarg :session + :type rudel-session-child + :documentation + "")) + "This abstract class defines the interface implementations of +client protocols have to obey." + :abstract t) + +(defgeneric rudel-disconnect ((this rudel-connection)) + "Close the connection.") + +(defgeneric rudel-change-color- ((this rudel-connection) color) ;; TODO name + "") + +(defgeneric rudel-publish ((this rudel-connection) document) + "") + +(defgeneric rudel-subscribe-to ((this rudel-connection) document) + "") + +(defgeneric rudel-unsubscribe-from ((this rudel-connection) document) ; TODO name should be rudel-unsubscribe + "") + +(defgeneric rudel-local-insert ((this rudel-connection)) + "") + +(defgeneric rudel-local-delete ((this rudel-connection)) + "") + +(defgeneric rudel-remote-insert ((this rudel-connection)) + "") + +(defgeneric rudel-remote-delete ((this rudel-connection)) + "") + + +;;; Class rudel-user +;; + +(defclass rudel-user (eieio-named + eieio-speedbar-file-button + rudel-hook-object) + ((color :initarg :color + :accessor rudel-color + :documentation + "Color used to indicate ownership or authorship +by the user. Examples includes text written by the user or the +user name itself.") + (change-hook :initarg :change-hook + :type list + :initform nil + :documentation + "This hook is run when this user object +changes.")) + "Objects of this class represent users participating in +collaborative editing session. Note that a participating user +does not have to be connected to the session at any given time." + :abstract t) + +(defmethod rudel-display-string ((this rudel-user) + &optional use-images align) + "Return a textual representation of THIS for user interface stuff." + (with-slots ((name :object-name) color) this + (propertize + (concat + (when use-images + (propertize "*" 'display rudel-icon-person)) + name) + 'face (list :background color))) + ) + + +;;; Class rudel-document +;; + +(defclass rudel-document (eieio-named + eieio-speedbar-file-button + rudel-hook-object) + ((session :initarg :session + :type rudel-session + :documentation + "") + (buffer :initarg :buffer + :type (or null buffer) + :initform nil + :documentation + "") + (subscribed :initarg :subscribed + :type list + :initform nil + :documentation + "") + ;; Hooks + (subscribe-hook :initarg :subscribe-hook + :type list + :initform nil + :documentation + "This hook is run when a user subscribes to +this document object.") + (unsubscribe-hook :initarg :unsubscribe-hook + :type list + :initform nil + :documentation + "This hook is run when a user unsubscribes +from this document object.") + (attach-hook :initarg :attach-hook + :type list + :initform nil + :documentation + "This hook is run when a buffer is attached +to this document object.") + (detach-hook :initarg :detach-hook + :type list + :initform nil + :documentation + "This hook is run when the attached buffer +is detached from this document object.")) + "This class represents a document, which participants of a +collaborative editing session can subscribe to." + :abstract t) + +(defmethod rudel-unique-name ((this rudel-document)) + "Returns a suggested name for the buffer attached to THIS document." + (object-name-string this)) + +(defmethod rudel-suggested-buffer-name ((this rudel-document)) + "Returns a suggested name for the buffer attached to THIS document." + (rudel-unique-name this)) + +(defmethod rudel-attached-p ((this rudel-document)) + (with-slots (buffer) this + buffer)) + +(defmethod rudel-attach-to-buffer ((this rudel-document) buffer) + "Attach THIS document to BUFFER" + (with-slots ((doc-buffer :buffer)) this + ;; Set buffer slot of THIS to BUFFER and associated THIS with + ;; BUFFER. + (setq doc-buffer buffer) + (rudel-set-buffer-document this buffer) + + (with-current-buffer doc-buffer + ;; Add the handler function for buffer changes to the buffer's + ;; change hook. + (add-hook 'after-change-functions + #'rudel-handle-buffer-change + nil t) + + ;; Store change data before the change a done. This is necessary + ;; because the number of bytes (not characters) cannot otherwise + ;; be recovered after a deletion. + (add-hook 'before-change-functions + #'rudel-buffer-change-workaround + nil t) + + ;; Add a handler to the kill-buffer hook to unsubscribe from the + ;; document when the buffer gets killed. + (add-hook 'kill-buffer-hook + #'rudel-unpublish-buffer + nil t) + + ;; + (add-hook 'change-major-mode-hook + #'rudel-handle-major-mode-change + nil t)) + + ;; Run the hook. + (object-run-hook-with-args this 'attach-hook doc-buffer)) + ) + +(defmethod rudel-detach-from-buffer ((this rudel-document)) + "Detach document THIS from its buffer. +Do nothing, if THIS is not attached to any buffer." + (with-slots (buffer) this + (let ((buffer-save buffer)) + + ;; Only try to detach from BUFFER, if it is non-nil. BUFFER can + ;; be nil, if the user did not subscribe to the document, or + ;; unsubscribed after subscribing. + (when buffer + + (with-current-buffer buffer + ;; Remove our handler function from the kill-buffer hook. + (remove-hook 'kill-buffer-hook + #'rudel-unpublish-buffer + t) + + ;; Remove our handler function from the after-change hook. + (remove-hook 'after-change-functions + #'rudel-handle-buffer-change + t) + + ;; Remove our handler function from the before-change hook. + (remove-hook 'before-change-functions + #'rudel-buffer-change-workaround + t) + + ;; Remove all overlays. + (rudel-overlays-remove-all) + + ;; Remove the major mode change handler. + (remove-hook 'change-major-mode-hook + #'rudel-handle-major-mode-change + t)) + + ;; Unset buffer slot of THIS and delete association of THIS with + ;; BUFFER. + (rudel-set-buffer-document nil buffer) + (setq buffer nil)) + + ;; Run the hook. + (object-run-hook-with-args this 'detach-hook buffer-save))) + ) + +(defmethod rudel-add-user ((this rudel-document) user) + "Add USER to the list of subscribed users of THIS. + +Runs object hook (see `rudel-hook-object') `subscribe-hook' with +arguments THIS and USER." + ;; Add USER to list. + (object-add-to-list this :subscribed user) + + ;; Run the hook. + (object-run-hook-with-args this 'subscribe-hook user)) + +(defmethod rudel-remove-user ((this rudel-document) user) + "Remove USER from the list of subscribed users of THIS. + +Runs object hook (see `rudel-hook-object') `unsubscribe-hook' +with arguments THIS and USER." + ;; Remove USER from list. + (object-remove-from-list document :subscribed user) + + ;; Run the hook. + (object-run-hook-with-args this 'unsubscribe-hook user)) + +(defmethod rudel-insert ((this rudel-document) position data) + "Insert DATA at POSITION into the buffer attached to THIS. +When POSITION is nil `point-max' is used to determine the +insertion position. +Modification hooks are disabled during the insertion." + (with-slots (buffer) this + (save-excursion + (set-buffer buffer) + + (unless position + (setq position (- (point-max) 1))) + + (let ((inhibit-modification-hooks t)) + (goto-char (+ position 1)) + (insert data)))) + ) + +(defmethod rudel-delete ((this rudel-document) position length) + "Delete a region of LENGTH character at POSITION from the buffer attached to THIS. +Modification hooks are disabled during the insertion." + (with-slots (buffer) this + (save-excursion + (set-buffer buffer) + + (let ((inhibit-modification-hooks t)) + (delete-region (+ position 1) (+ position length 1))))) + ) + +(defmethod rudel-local-operation ((this rudel-document) operation) + "Apply the local operation OPERATION to THIS." + (with-slots (session buffer) this + (with-slots (connection (user :self)) session + (dolist (operators (list + + ;; Update overlays + (rudel-overlay-operators + "overlay-operators" + :document this + :user user) + + ;; Notify connection + (rudel-connection-operators + "connection-operators" + :connection connection + :document this))) + + ;; Apply the operation using each set of operators + (rudel-apply operation operators)))) + ) + +(defmethod rudel-remote-operation ((this rudel-document) user operation) + "Apply the remote operation OPERATION performed by USER to THIS." + (dolist (operators (append + + ;; Update buffer contents + (list (rudel-document-operators + "document-operators" + :document this)) + + ;; Update overlays + (when user + (list (rudel-overlay-operators + "overlay-operators" + :document this + :user user))))) + + ;; Apply the operation using each set of operators + (rudel-apply operation operators)) + ) + +(defmethod rudel-chunks ((this rudel-document)) + "Return a list of text chunks of the associated buffer. +Each element in the chunk is a list structured like this (START +END AUTHOR). START and END are numbers, AUTHOR is of type (or +null rudel-user-child)." + (with-slots (buffer) this + ;; Extract buffer string and a list of chunks partitioning the + ;; string according to the respective author (or nil). + (with-current-buffer buffer + (let ((string (buffer-string)) ;; TODO no-properties? + (overlay-chunks (mapcar + (lambda (overlay) + (list (- (overlay-start overlay) 1) + (- (overlay-end overlay) 1) + (rudel-overlay-user overlay))) + (sort* (rudel-author-overlays) + '< :key 'overlay-start))) + (last) + (augmented-chunks)) + + ;; Iterate through the list of chunks to find gaps between + ;; chunks (also before the first) and insert entries with + ;; author nil accordingly. + (dolist (chunk overlay-chunks) + (when (or (and (not last) + (> (nth 0 chunk) 0)) + (and last + (/= (nth 1 last) + (nth 0 chunk)))) + (push (list (if last (nth 1 last) 0) + (nth 0 chunk) + nil) + augmented-chunks)) + (push chunk augmented-chunks) + (setq last chunk)) + + ;; If there is text after the last chunk, create another one + ;; with author nil. If there were no chunks at all, this chunk + ;; can also cover the whole buffer string. + (when (or (and (not last) + (/= (length string) 0)) + (and last + (/= (nth 1 last) (length string)))) + (push (list (if last (nth 1 last) 0) + (length string) + nil) + augmented-chunks)) + + ;; Sort chunks according to the start position. + (sort* augmented-chunks '< :key 'car)))) + ) + + +;;; Buffer-related functions +;; + +(defun rudel-buffer-has-document-p (&optional buffer) + "Return non-nil if a document object is attached to BUFFER. +If BUFFER is nil, use the current buffer." + (unless buffer + (setq buffer (current-buffer))) + + (buffer-local-value 'rudel-buffer-document buffer)) + +(defun rudel-buffer-document (&optional buffer) + "Return the document object attached to BUFFER. +If BUFFER is nil, use the current buffer." + (unless buffer + (setq buffer (current-buffer))) + + (buffer-local-value 'rudel-buffer-document buffer)) + +(defun rudel-set-buffer-document (document &optional buffer) + "Associate BUFFER to DOCUMENT. +If DOCUMENT is nil, make it not associated to any buffer. +If BUFFER is nil, use the current buffer." + (unless buffer + (setq buffer (current-buffer))) + + (with-current-buffer buffer + (setq rudel-buffer-document document))) + +(defun rudel-handle-buffer-change (from to length) + "Handle buffer change at range FROM to TO with length LENGTH by relaying them to the document object of the buffer. +See after-change-functions for more information." + (when (rudel-buffer-has-document-p) + (let ((document (rudel-buffer-document)) + (text)) ; TODO with-rudel-buffer-document? + (cond + ;; The change was an insert + ((and (/= from to) + (zerop length)) + (with-slots (buffer) document + (with-current-buffer buffer + (setq text (buffer-substring-no-properties from to))) + (rudel-local-operation document + (rudel-insert-op + "insert" + :from (- from 1) + :data text)))) + + ;; The change was a delete + ((and (= from to) + (not (zerop length))) + (rudel-local-operation document + (rudel-delete-op + "delete" + :from (- from 1) + :length length))) + + ;; The operation was neither an insert nor a delete. This seems + ;; to mean that the region has changed arbitrarily. The only + ;; option we have is sending a delete and corresponding insert + ;; message that emulate the change. + (t + (with-slots (buffer) document + (with-current-buffer buffer + (setq text (buffer-substring-no-properties from to))) + (rudel-local-operation document + (rudel-delete-op + "delete" + :from (- from 1) + :length length)) + (rudel-local-operation document + (rudel-insert-op + "insert" + :from (- from 1) + :data text))))))) + ) + +(defun rudel-buffer-change-workaround (from to) + (when (/= from to) + (setq rudel-buffer-change-workaround-data + (list from to + (buffer-substring-no-properties from to))))) + + +;;; Protection against major mode changes +;; + +(defvar rudel-mode-changed-buffers nil + "List of buffers that may need to be repaired after a major + mode change.") + +(defun rudel-handle-major-mode-change () + "Store the current buffer to repair damage done by major mode change. + +Note: The way this works is inspired by mode-local.el by David +Ponce and Eric M. Ludlam." + ;; Store the buffer for later repair. + (add-to-list 'rudel-mode-changed-buffers (current-buffer)) + + ;; Schedule `rudel-after-major-mode-change' to run after the + ;; command, that caused the major mode change. + (add-hook 'post-command-hook + #'rudel-after-major-mode-change) + ) + +(defun rudel-after-major-mode-change () + "Repair damage done by major mode changes. +As a function in `post-command-hook', this is run after there was +a `major-mode' change. + +Note: The way this works is inspired by mode-local.el by David +Ponce and Eric M. Ludlam." + ;; Remove this function from `post-command-hook'. + (remove-hook 'post-command-hook + #'rudel-after-major-mode-change) + + ;; Repair all buffers affected by the major mode change. + (dolist (buffer rudel-mode-changed-buffers) + (let ((document (buffer-local-value 'rudel-buffer-document + buffer))) + (rudel-attach-to-buffer document buffer))) + ) + + +;;; Interactive functions +;; + +;;;###autoload +(defun rudel-join-session (info) + "Join the collaborative editing session described by INFO. +INFO is a property list that describes the collaborative editing +session in terms of properties like :host, :port +and :encryption. The particular properties and their respective +meanings depend on the used backend. + +When called interactively, all data required to join a session +will be prompted for." + (interactive + ;; Try the discover method of session initiation backends to find + ;; available sessions. + (list + (let ((info) + (session-initiation-backend)) + (while (not info) + (message "Discovering Sessions ...") + (let* ((sessions (rudel-session-initiation-discover + session-initiation-backend)) + (maybe-info (if (= (length sessions) 1) + (car sessions) + (rudel-read-session + sessions "Choose Session: " 'object)))) + (if (rudel-backend-cons-p maybe-info) + (setq session-initiation-backend (car maybe-info)) + (setq info maybe-info)))) + info))) + + ;; First, create the session object. + (let* ((backend (cdr (plist-get info :backend))) + (session (rudel-client-session + (plist-get info :name) + :backend backend)) + (connection)) + ;; Give the backend a chance to collect remaining connect + ;; info. For session initiation methods like Zeroconf, we have the + ;; _connection_ info, but are still missing the username and + ;; stuff. + (setq info (rudel-ask-connect-info backend info)) + + ;; Add the session object to the connect information. + (plist-put info :session session) + + ;; Ask BACKEND to connect using INFO. Do not catch errors since + ;; the error messages are probably the best feedback we can give. + (setq connection (rudel-connect backend info)) + + ;; Set the connection slot of the session object and store it + ;; globally. + (oset session :connection connection) + (setq rudel-current-session session) + + ;; Reset the global session variable when the session ends. + (object-add-hook session 'end-hook + (lambda (session) + (setq rudel-current-session nil))) + + ;; Run the hook. + (run-hook-with-args 'rudel-session-start-hook session)) + ) + +;;;###autoload +(defun rudel-host-session () + "Host a collaborative editing session. +All data required to host a session will be prompted for +interactively." + (interactive) + ;; If necessary, ask the user for the backend we should use. + (let* ((backend (cdr (rudel-backend-choose + 'protocol + (lambda (backend) + (rudel-capable-of-p backend 'host))))) + (info (rudel-ask-host-info backend)) + (server)) + + ;; Try to create the server + (condition-case error-data + (setq server (rudel-host backend info)) + ('error + (error "Could not host session using backend `%s' with %s: %s" + (object-name-string backend) + info + (car error-data)))) + server)) + +;;;###autoload +(defun rudel-end-session () + "End the current collaborative editing session." + (interactive) + (unless rudel-current-session + (error "No active Rudel session")) + + ;; Actually end the session. + (rudel-end rudel-current-session) + ) + +;;;###autoload +(defun rudel-change-color () + "Change the color associated with the local user. +Not all backends support this operation." + (interactive) + ;; Make sure we have a session. + (unless rudel-current-session + (error "No active Rudel session")) + + (with-slots (backend connection self) rudel-current-session + ;; Make sure the backend can change colors. + (unless (rudel-capable-of-p backend 'change-color) + (error "Backend `%s' cannot change colors" + (object-name-string backend))) + + (with-slots ((name :object-name) color) self + ;; Ask the user for a new color. + (setq color (read-color "New Color: " t)) + + ;; Tell the connection to announce the change and change it in + ;; our user object. + (rudel-change-color- connection color) + + ;; Run the change hook. + (object-run-hook-with-args self 'change-hook) + + ;; Update overlay color. + (rudel-overlay-set-face-attributes + (rudel-overlay-make-face-symbol 'author name) + color))) + ) + +;;;###autoload +(defun rudel-subscribe (document) + "Subscribe to DOCUMENT offered by a peer in a collaborative editing session. +When called interactively, DOCUMENT is prompted for interactively." + (interactive + (list (progn + ;; We have to retrieve the document list from an active + ;; session. + (unless rudel-current-session + (error "No active Rudel session")) + ;; Select unsubscribed documents. + (let ((documents (rudel-unsubscribed-documents + rudel-current-session))) + ;; Already subscribed to all documents. This is an error. + (when (null documents) + (error "No unsubscribed documents")) + ;; Read an unsubscribed document. + (rudel-read-document documents nil 'object))))) + + ;; Make sure we have a session. + (unless rudel-current-session + (error "No active Rudel session")) + + ;; Create a new buffer and attach the document to it. + (let* ((name (rudel-suggested-buffer-name document)) + (buffer (funcall rudel-allocate-buffer-function name))) + (rudel-attach-to-buffer document buffer) + + (let ((connection (oref (oref document :session) :connection))) + (rudel-subscribe-to connection document)) + + ;; Show the new buffer. + (set-window-buffer nil buffer)) + ) + +;;;###autoload +(defun rudel-publish-buffer (&optional buffer) + "Make the BUFFER available for subscription to peers in a collaborative editing session. +If BUFFER is nil, the current buffer is used." + (interactive (list nil)) + + ;; Make sure we have a session. + (unless rudel-current-session + (error "No active Rudel session")) + + (unless buffer + (setq buffer (current-buffer))) + + (with-current-buffer buffer + (when (rudel-buffer-has-document-p) + (error "Buffer already published or subscribed"))) ; TODO keep this? + + ;; + (with-slots (backend connection self) rudel-current-session + (let ((document (rudel-make-document backend + (buffer-name buffer) + rudel-current-session))) + (rudel-add-document rudel-current-session document) + + (rudel-attach-to-buffer document buffer) + (object-add-to-list document :subscribed self) + + (rudel-publish connection document))) + ) + +;;;###autoload +(defun rudel-unpublish-buffer (&optional buffer) + "Deny peers access to BUFFER in a collaborative editing session. +If BUFFER is nil, the current is used." + (interactive) + + ;; Make sure we have a session. + (unless rudel-current-session + (error "No active Rudel session")) + + (unless buffer + (setq buffer (current-buffer))) + + (with-current-buffer buffer + (unless (rudel-buffer-has-document-p) + (error "Buffer is not published"))) + + ;; + (with-slots (connection) rudel-current-session + (let ((document (rudel-buffer-document buffer))) + (rudel-detach-from-buffer document) + + (rudel-unsubscribe-from connection document))) + ) + +(provide 'rudel) +;;; rudel.el ends here diff --git a/emacs.d/lisp/rudel/telepathy/.svn/all-wcprops b/emacs.d/lisp/rudel/telepathy/.svn/all-wcprops new file mode 100644 index 0000000..e89bd10 --- /dev/null +++ b/emacs.d/lisp/rudel/telepathy/.svn/all-wcprops @@ -0,0 +1,11 @@ +K 25 +svn:wc:ra_dav:version-url +V 43 +/svnroot/rudel/!svn/ver/301/trunk/telepathy +END +rudel-telepathy.el +K 25 +svn:wc:ra_dav:version-url +V 62 +/svnroot/rudel/!svn/ver/301/trunk/telepathy/rudel-telepathy.el +END diff --git a/emacs.d/lisp/rudel/telepathy/.svn/entries b/emacs.d/lisp/rudel/telepathy/.svn/entries new file mode 100644 index 0000000..ee21fde --- /dev/null +++ b/emacs.d/lisp/rudel/telepathy/.svn/entries @@ -0,0 +1,62 @@ +10 + +dir +545 +https://rudel.svn.sourceforge.net/svnroot/rudel/trunk/telepathy +https://rudel.svn.sourceforge.net/svnroot/rudel + + + +2009-10-03T01:27:08.697881Z +301 +scymtym + + + + + + + + + + + + + + +694b31df-dcbb-44e8-af88-74c7ea918228 + +rudel-telepathy.el +file + + + + +2009-11-18T14:01:46.000000Z +d4713da73fb5f147e0c7afcbcdb269b9 +2009-10-03T01:27:08.697881Z +301 +scymtym + + + + + + + + + + + + + + + + + + + + + +1882 + diff --git a/emacs.d/lisp/rudel/telepathy/.svn/text-base/rudel-telepathy.el.svn-base b/emacs.d/lisp/rudel/telepathy/.svn/text-base/rudel-telepathy.el.svn-base new file mode 100644 index 0000000..3d41643 --- /dev/null +++ b/emacs.d/lisp/rudel/telepathy/.svn/text-base/rudel-telepathy.el.svn-base @@ -0,0 +1,76 @@ +;;; rudel-telepathy.el --- A telepathy backend for Rudel +;; +;; Copyright (C) 2008, 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Rudel, telepathy, backend +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 rudel. If not, see . + + +;;; Commentary: +;; +;; This file contains a Rudel backend which realizes session +;; initiation and transport of Rudel data through freedesktop's +;; Telepathy framework. + + +;;; History: +;; +;; 0.1 - Initial revision. + + +;;; Code: +;; + +(require 'eieio) + +(require 'rudel-backend) +(require 'rudel-transport) + + +;;; Constants +;; + +(defconst rudel-telepathy-version '(0 1) + "Version of the telepathy backend for Rudel.") + + +;;; Class rudel-telepathy-backend +;; + +(defclass rudel-telepathy-backend (rudel-transport-backend) + ((capabilities :initform '())) + "Class rudel-telepathy-backend ") + +(defmethod initialize-instance ((this rudel-telepathy-backend) &rest slots) + "Initialize slots of THIS according to SLOTS." + (when (next-method-p) + (call-next-method)) + + (oset this :version rudel-telepathy-version)) + + +;;; Autoloading +;; + +;;;###autoload +(rudel-add-backend (rudel-backend-get-factory 'transport) + 'telepathy 'rudel-telepathy-backend) + +(provide 'rudel-telepathy) +;;; rudel-telepathy.el ends here diff --git a/emacs.d/lisp/rudel/telepathy/rudel-telepathy.el b/emacs.d/lisp/rudel/telepathy/rudel-telepathy.el new file mode 100644 index 0000000..3d41643 --- /dev/null +++ b/emacs.d/lisp/rudel/telepathy/rudel-telepathy.el @@ -0,0 +1,76 @@ +;;; rudel-telepathy.el --- A telepathy backend for Rudel +;; +;; Copyright (C) 2008, 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Rudel, telepathy, backend +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 rudel. If not, see . + + +;;; Commentary: +;; +;; This file contains a Rudel backend which realizes session +;; initiation and transport of Rudel data through freedesktop's +;; Telepathy framework. + + +;;; History: +;; +;; 0.1 - Initial revision. + + +;;; Code: +;; + +(require 'eieio) + +(require 'rudel-backend) +(require 'rudel-transport) + + +;;; Constants +;; + +(defconst rudel-telepathy-version '(0 1) + "Version of the telepathy backend for Rudel.") + + +;;; Class rudel-telepathy-backend +;; + +(defclass rudel-telepathy-backend (rudel-transport-backend) + ((capabilities :initform '())) + "Class rudel-telepathy-backend ") + +(defmethod initialize-instance ((this rudel-telepathy-backend) &rest slots) + "Initialize slots of THIS according to SLOTS." + (when (next-method-p) + (call-next-method)) + + (oset this :version rudel-telepathy-version)) + + +;;; Autoloading +;; + +;;;###autoload +(rudel-add-backend (rudel-backend-get-factory 'transport) + 'telepathy 'rudel-telepathy-backend) + +(provide 'rudel-telepathy) +;;; rudel-telepathy.el ends here diff --git a/emacs.d/lisp/rudel/wave/.svn/all-wcprops b/emacs.d/lisp/rudel/wave/.svn/all-wcprops new file mode 100644 index 0000000..5430a3f --- /dev/null +++ b/emacs.d/lisp/rudel/wave/.svn/all-wcprops @@ -0,0 +1,17 @@ +K 25 +svn:wc:ra_dav:version-url +V 38 +/svnroot/rudel/!svn/ver/345/trunk/wave +END +Project.ede +K 25 +svn:wc:ra_dav:version-url +V 50 +/svnroot/rudel/!svn/ver/291/trunk/wave/Project.ede +END +rudel-wave.el +K 25 +svn:wc:ra_dav:version-url +V 52 +/svnroot/rudel/!svn/ver/291/trunk/wave/rudel-wave.el +END diff --git a/emacs.d/lisp/rudel/wave/.svn/entries b/emacs.d/lisp/rudel/wave/.svn/entries new file mode 100644 index 0000000..537adf3 --- /dev/null +++ b/emacs.d/lisp/rudel/wave/.svn/entries @@ -0,0 +1,96 @@ +10 + +dir +545 +https://rudel.svn.sourceforge.net/svnroot/rudel/trunk/wave +https://rudel.svn.sourceforge.net/svnroot/rudel + + + +2009-10-03T02:03:51.626714Z +345 +scymtym + + + + + + + + + + + + + + +694b31df-dcbb-44e8-af88-74c7ea918228 + +Project.ede +file + + + + +2009-11-18T14:01:45.000000Z +74bbb6356abac34a5aa8305ff84a2631 +2009-10-03T01:23:04.047285Z +291 +scymtym + + + + + + + + + + + + + + + + + + + + + +265 + +rudel-wave.el +file + + + + +2009-11-18T14:01:45.000000Z +b53a8ef79d48d009a10a0b013e2c69a5 +2009-10-03T01:23:04.047285Z +291 +scymtym + + + + + + + + + + + + + + + + + + + + + +1833 + diff --git a/emacs.d/lisp/rudel/wave/.svn/text-base/Project.ede.svn-base b/emacs.d/lisp/rudel/wave/.svn/text-base/Project.ede.svn-base new file mode 100644 index 0000000..af39c6f --- /dev/null +++ b/emacs.d/lisp/rudel/wave/.svn/text-base/Project.ede.svn-base @@ -0,0 +1,14 @@ +;; Object rudel/wave +;; EDE project file. +(ede-proj-project "rudel/wave" + :name "wave" + :file "Project.ede" + :targets (list + (ede-proj-target-elisp "wave" + :name "wave" + :path "" + :source '("rudel-wave.el") + :aux-packages '("rudel") + ) + ) + ) diff --git a/emacs.d/lisp/rudel/wave/.svn/text-base/rudel-wave.el.svn-base b/emacs.d/lisp/rudel/wave/.svn/text-base/rudel-wave.el.svn-base new file mode 100644 index 0000000..f8e0555 --- /dev/null +++ b/emacs.d/lisp/rudel/wave/.svn/text-base/rudel-wave.el.svn-base @@ -0,0 +1,77 @@ +;;; rudel-wave.el --- A Wave backend for Rudel +;; +;; Copyright (C) 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Rudel, Wave protocol, backend +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; This file contains a Rudel protocol backend, which implements the +;; Wave client protocol. + + +;;; History: +;; +;; 0.1 - Initial version + + +;;; Code: +;; + +(require 'rudel-backend) +(require 'rudel-protocol) + + +;;; Constants +;; + +(defconst rudel-wave-version '(0 1) + "Version of the wave backend for Rudel.") + + +;;; Class rudel-wave-backend +;; + +;;;###autoload +(defclass rudel-wave-backend (rudel-protocol-backend) + ((capabilities :initform '(join + chat + track-subscriptions))) + "Main class of the Rudel Wave backend. Creates wave client +connections.") + +(defmethod initialize-instance ((this rudel-wave-backend) &rest slots) + "Initialize slots of THIS with SLOTS." + (when (next-method-p) + (call-next-method)) + + (oset this :version rudel-wave-version)) + + +;;; Autoloading +;; + +;;;###autoload +(rudel-add-backend (rudel-backend-get-factory 'protocol) + 'wave 'rudel-wave-backend) + +(provide 'rudel-wave) +;;; rudel-wave.el ends here diff --git a/emacs.d/lisp/rudel/wave/Project.ede b/emacs.d/lisp/rudel/wave/Project.ede new file mode 100644 index 0000000..af39c6f --- /dev/null +++ b/emacs.d/lisp/rudel/wave/Project.ede @@ -0,0 +1,14 @@ +;; Object rudel/wave +;; EDE project file. +(ede-proj-project "rudel/wave" + :name "wave" + :file "Project.ede" + :targets (list + (ede-proj-target-elisp "wave" + :name "wave" + :path "" + :source '("rudel-wave.el") + :aux-packages '("rudel") + ) + ) + ) diff --git a/emacs.d/lisp/rudel/wave/rudel-wave.el b/emacs.d/lisp/rudel/wave/rudel-wave.el new file mode 100644 index 0000000..f8e0555 --- /dev/null +++ b/emacs.d/lisp/rudel/wave/rudel-wave.el @@ -0,0 +1,77 @@ +;;; rudel-wave.el --- A Wave backend for Rudel +;; +;; Copyright (C) 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Rudel, Wave protocol, backend +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; This file contains a Rudel protocol backend, which implements the +;; Wave client protocol. + + +;;; History: +;; +;; 0.1 - Initial version + + +;;; Code: +;; + +(require 'rudel-backend) +(require 'rudel-protocol) + + +;;; Constants +;; + +(defconst rudel-wave-version '(0 1) + "Version of the wave backend for Rudel.") + + +;;; Class rudel-wave-backend +;; + +;;;###autoload +(defclass rudel-wave-backend (rudel-protocol-backend) + ((capabilities :initform '(join + chat + track-subscriptions))) + "Main class of the Rudel Wave backend. Creates wave client +connections.") + +(defmethod initialize-instance ((this rudel-wave-backend) &rest slots) + "Initialize slots of THIS with SLOTS." + (when (next-method-p) + (call-next-method)) + + (oset this :version rudel-wave-version)) + + +;;; Autoloading +;; + +;;;###autoload +(rudel-add-backend (rudel-backend-get-factory 'protocol) + 'wave 'rudel-wave-backend) + +(provide 'rudel-wave) +;;; rudel-wave.el ends here diff --git a/emacs.d/lisp/rudel/zeroconf/.svn/all-wcprops b/emacs.d/lisp/rudel/zeroconf/.svn/all-wcprops new file mode 100644 index 0000000..f660614 --- /dev/null +++ b/emacs.d/lisp/rudel/zeroconf/.svn/all-wcprops @@ -0,0 +1,17 @@ +K 25 +svn:wc:ra_dav:version-url +V 42 +/svnroot/rudel/!svn/ver/345/trunk/zeroconf +END +Project.ede +K 25 +svn:wc:ra_dav:version-url +V 54 +/svnroot/rudel/!svn/ver/260/trunk/zeroconf/Project.ede +END +rudel-zeroconf.el +K 25 +svn:wc:ra_dav:version-url +V 60 +/svnroot/rudel/!svn/ver/260/trunk/zeroconf/rudel-zeroconf.el +END diff --git a/emacs.d/lisp/rudel/zeroconf/.svn/entries b/emacs.d/lisp/rudel/zeroconf/.svn/entries new file mode 100644 index 0000000..3ab3d1c --- /dev/null +++ b/emacs.d/lisp/rudel/zeroconf/.svn/entries @@ -0,0 +1,96 @@ +10 + +dir +545 +https://rudel.svn.sourceforge.net/svnroot/rudel/trunk/zeroconf +https://rudel.svn.sourceforge.net/svnroot/rudel + + + +2009-10-03T02:03:51.626714Z +345 +scymtym + + + + + + + + + + + + + + +694b31df-dcbb-44e8-af88-74c7ea918228 + +Project.ede +file + + + + +2009-11-18T14:01:44.000000Z +5d5760fd4535be2a6e18cc9c42334b05 +2009-10-03T01:06:09.107570Z +260 +scymtym + + + + + + + + + + + + + + + + + + + + + +286 + +rudel-zeroconf.el +file + + + + +2009-11-18T14:01:44.000000Z +deed32ca24d718009bd1137e49439fc0 +2009-10-03T01:06:09.107570Z +260 +scymtym + + + + + + + + + + + + + + + + + + + + + +7370 + diff --git a/emacs.d/lisp/rudel/zeroconf/.svn/text-base/Project.ede.svn-base b/emacs.d/lisp/rudel/zeroconf/.svn/text-base/Project.ede.svn-base new file mode 100644 index 0000000..7bee2d0 --- /dev/null +++ b/emacs.d/lisp/rudel/zeroconf/.svn/text-base/Project.ede.svn-base @@ -0,0 +1,14 @@ +;; Object rudel/obby +;; EDE project file. +(ede-proj-project "rudel/zeroconf" + :name "zeroconf" + :file "Project.ede" + :targets (list + (ede-proj-target-elisp "zeroconf" + :name "zeroconf" + :path "" + :source '("rudel-zeroconf.el") + :aux-packages '("rudel") + ) + ) + ) diff --git a/emacs.d/lisp/rudel/zeroconf/.svn/text-base/rudel-zeroconf.el.svn-base b/emacs.d/lisp/rudel/zeroconf/.svn/text-base/rudel-zeroconf.el.svn-base new file mode 100644 index 0000000..01ffc0d --- /dev/null +++ b/emacs.d/lisp/rudel/zeroconf/.svn/text-base/rudel-zeroconf.el.svn-base @@ -0,0 +1,253 @@ +;;; rudel-zeroconf.el --- Zeroconf support for Rudel +;; +;; Copyright (C) 2008, 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Rudel, service, discovery, advertising, zeroconf, +;; rendezvous, avahi +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; Zeroconf session discovery and advertising for Rudel. The main +;; class `rudel-zeroconf-backend' implements discovery and advertising +;; for registered service types. Service types are registered by +;; adding an element of the form (SERVICE BACKEND) to the +;; `rudel-zeroconf-service-types'. BACKEND is the symbol of the +;; protocol backend and SERVICE is the string used as service type in +;; the Zeroconf record (example: '("_lobby._tcp" obby)). + + +;;; History: +;; +;; 0.1 - Initial revision. + + +;;; Code: +;; + +(eval-when-compile + (require 'cl)) ;; first, second + +(require 'zeroconf) + +(require 'rudel-backend) +(require 'rudel-session-initiation) + + +;;; Constants and global variables +;; + +(defconst rudel-zeroconf-version '(0 1) + "Version of the Zeroconf backend for Rudel.") + +(defvar rudel-zeroconf-service-types nil + "Service types used by Rudel backends. +Each element is of the form (SERVICE BACKEND).") + + +;;; Accessors and manipulators for the service list +;; + +(defalias 'rudel-zeroconf-service-type 'first + "Return type of service.") + +(defalias 'rudel-zeroconf-service-backend 'second + "Return backend associated with service type.") + +(defun rudel-zeroconf-service (key which) + "Return the Zeroconf service type used by BACKEND." + (find which rudel-zeroconf-service-types + :key key :test (if (eq key 'rudel-zeroconf-service-type) + #'string= #'eq))) + +;;;###autoload +(defun rudel-zeroconf-register-service (type backend) + "Add an entry for TYPE with BACKEND to the list of service types. +BACKEND is the name of the protocol backend handling the service +type TYPE." + (push (list type backend) + rudel-zeroconf-service-types)) + + +;;; Initialization +;; + +(message "Initializing Zeroconf ...") +(zeroconf-init) + + +;;; Class rudel-zeroconf-backend +;; + +;;;###autoload +(defclass rudel-zeroconf-backend (rudel-session-initiation-backend) + ((capabilities :initform (discover advertise)) + (priority :initform primary)) + "") + +(defmethod initialize-instance ((this rudel-zeroconf-backend) &rest slots) + "Initialize slots of THIS with SLOTS." + (when (next-method-p) + (call-next-method)) + + (oset this :version rudel-zeroconf-version)) + +(defmethod rudel-discover ((this rudel-zeroconf-backend)) + "Return a list of session information property lists for Zeroconf-advertised sessions." + (mapcar + #'rudel-zeroconf-service->plist + (remove-if + #'null + (mapcar + #'zeroconf-resolve-service + (apply + #'append + (mapcar + #'rudel-zeroconf-services + (mapcar #'rudel-zeroconf-service-backend + rudel-zeroconf-service-types)))))) + ) + +(defmethod rudel-advertise ((this rudel-session-initiation-backend) info) + "Use Zeroconf to advertise the session described by INFO to other users." + (let ((name (plist-get info :name)) + (backend (plist-get info :backend)) + (host (plist-get info :host)) + (port (plist-get info :port)) + (data (plist-get info :data))) + (when backend + (apply #'rudel-zeroconf-publish + backend name host port data))) + t) + +(defmethod rudel-withdraw ((this rudel-session-initiation-backend)) + "Withdraw Zeroconf record." + (error "Not implemented, yet")) + + +;;; Zeroconf wrapper functions +;; + +(defun rudel-zeroconf-services (backend) + "List Zeroconf services for BACKEND." + (zeroconf-list-services + (rudel-zeroconf-service-type + (rudel-zeroconf-service + #'rudel-zeroconf-service-backend backend)))) + +(defun rudel-zeroconf-services-present-p (backend) + "Check whether there are Zeroconf services for BACKEND." + (rudel-zeroconf-services backend)) + +(defun rudel-zeroconf-publish (backend name host port &rest data) + "Publish BACKEND service NAME for HOST and PORT." + ;; Try to find the service entry for the protocol backend and + ;; publish the service if an entry is found. + (let ((service (rudel-zeroconf-service + #'rudel-zeroconf-service-backend backend))) + (when service + (zeroconf-publish-service + name + (rudel-zeroconf-service-type service) + "local" + (concat host ".local") + port + "" ; address + (mapcar + (lambda (item) + (concat (car item) "=" (cdr item))) + data) + ))) + ) + +(defun rudel-zeroconf-withdraw (backend name) + "Withdraw service NAME for BACKEND." + (error "Not implemented, yet")) + + +;;; Miscellaneous functions +;; + +(defun rudel-zeroconf-service->plist (service) + "Convert a Zeroconf service record to an info property list." + (let* ((type (zeroconf-service-type service)) + (data (rudel-zeroconf-parse-txt-record + (zeroconf-service-txt service))) + (service-type (rudel-zeroconf-service + #'rudel-zeroconf-service-type type))) + ;; Construct information property list. + (list + :name (format "Zeroconf advertised session \"%s\"" + (zeroconf-service-name service)) + :backend (rudel-backend-get + 'protocol + (rudel-zeroconf-service-backend service-type)) + :host (zeroconf-service-host service) + :port (zeroconf-service-port service) + ;; Encryption defaults to yes to be compatible with Gobby. + :encryption (or (not (member :encryption data)) + (string= (plist-get data :encryption) + "yes")))) + ) + +(defun rudel-zeroconf-parse-txt-record (record) + "Parse RECORD into a property list of keys and values." + (apply #'append + (mapcar + (lambda (entry) + (multiple-value-bind (key value) (split-string entry "=") + (list (intern (concat ":" key)) + value))) + record)) + ) + + +;;; User interaction +;; + +(defun rudel-zeroconf-read-service (backend) + "Retrieve services for BACKEND and read one from user." + ;; First, find all matching services for the backend. + (let* ((services (rudel-zeroconf-services backend)) + ;; Read one of the names of these services. + (service-name (completing-read + "Service: " + (mapcar #'zeroconf-service-name services) + nil t)) + ;; Retrieve and resolve the selected service object. + (service (find service-name services + :key #'zeroconf-service-name + :test #'string=)) + (service-resolved (zeroconf-resolve-service service))) + ;; Return host and port + (list (zeroconf-service-host service-resolved) + (zeroconf-service-port service-resolved))) + ) + + +;;; Autoloading +;; + +;;;###autoload +(rudel-add-backend (rudel-backend-get-factory 'session-initiation) + 'zeroconf 'rudel-zeroconf-backend) + +(provide 'rudel-zeroconf) +;;; rudel-zeroconf.el ends here diff --git a/emacs.d/lisp/rudel/zeroconf/Project.ede b/emacs.d/lisp/rudel/zeroconf/Project.ede new file mode 100644 index 0000000..7bee2d0 --- /dev/null +++ b/emacs.d/lisp/rudel/zeroconf/Project.ede @@ -0,0 +1,14 @@ +;; Object rudel/obby +;; EDE project file. +(ede-proj-project "rudel/zeroconf" + :name "zeroconf" + :file "Project.ede" + :targets (list + (ede-proj-target-elisp "zeroconf" + :name "zeroconf" + :path "" + :source '("rudel-zeroconf.el") + :aux-packages '("rudel") + ) + ) + ) diff --git a/emacs.d/lisp/rudel/zeroconf/rudel-zeroconf.el b/emacs.d/lisp/rudel/zeroconf/rudel-zeroconf.el new file mode 100644 index 0000000..01ffc0d --- /dev/null +++ b/emacs.d/lisp/rudel/zeroconf/rudel-zeroconf.el @@ -0,0 +1,253 @@ +;;; rudel-zeroconf.el --- Zeroconf support for Rudel +;; +;; Copyright (C) 2008, 2009 Jan Moringen +;; +;; Author: Jan Moringen +;; Keywords: Rudel, service, discovery, advertising, zeroconf, +;; rendezvous, avahi +;; X-RCS: $Id:$ +;; +;; This file is part of Rudel. +;; +;; Rudel 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. +;; +;; Rudel 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 Rudel. If not, see . + + +;;; Commentary: +;; +;; Zeroconf session discovery and advertising for Rudel. The main +;; class `rudel-zeroconf-backend' implements discovery and advertising +;; for registered service types. Service types are registered by +;; adding an element of the form (SERVICE BACKEND) to the +;; `rudel-zeroconf-service-types'. BACKEND is the symbol of the +;; protocol backend and SERVICE is the string used as service type in +;; the Zeroconf record (example: '("_lobby._tcp" obby)). + + +;;; History: +;; +;; 0.1 - Initial revision. + + +;;; Code: +;; + +(eval-when-compile + (require 'cl)) ;; first, second + +(require 'zeroconf) + +(require 'rudel-backend) +(require 'rudel-session-initiation) + + +;;; Constants and global variables +;; + +(defconst rudel-zeroconf-version '(0 1) + "Version of the Zeroconf backend for Rudel.") + +(defvar rudel-zeroconf-service-types nil + "Service types used by Rudel backends. +Each element is of the form (SERVICE BACKEND).") + + +;;; Accessors and manipulators for the service list +;; + +(defalias 'rudel-zeroconf-service-type 'first + "Return type of service.") + +(defalias 'rudel-zeroconf-service-backend 'second + "Return backend associated with service type.") + +(defun rudel-zeroconf-service (key which) + "Return the Zeroconf service type used by BACKEND." + (find which rudel-zeroconf-service-types + :key key :test (if (eq key 'rudel-zeroconf-service-type) + #'string= #'eq))) + +;;;###autoload +(defun rudel-zeroconf-register-service (type backend) + "Add an entry for TYPE with BACKEND to the list of service types. +BACKEND is the name of the protocol backend handling the service +type TYPE." + (push (list type backend) + rudel-zeroconf-service-types)) + + +;;; Initialization +;; + +(message "Initializing Zeroconf ...") +(zeroconf-init) + + +;;; Class rudel-zeroconf-backend +;; + +;;;###autoload +(defclass rudel-zeroconf-backend (rudel-session-initiation-backend) + ((capabilities :initform (discover advertise)) + (priority :initform primary)) + "") + +(defmethod initialize-instance ((this rudel-zeroconf-backend) &rest slots) + "Initialize slots of THIS with SLOTS." + (when (next-method-p) + (call-next-method)) + + (oset this :version rudel-zeroconf-version)) + +(defmethod rudel-discover ((this rudel-zeroconf-backend)) + "Return a list of session information property lists for Zeroconf-advertised sessions." + (mapcar + #'rudel-zeroconf-service->plist + (remove-if + #'null + (mapcar + #'zeroconf-resolve-service + (apply + #'append + (mapcar + #'rudel-zeroconf-services + (mapcar #'rudel-zeroconf-service-backend + rudel-zeroconf-service-types)))))) + ) + +(defmethod rudel-advertise ((this rudel-session-initiation-backend) info) + "Use Zeroconf to advertise the session described by INFO to other users." + (let ((name (plist-get info :name)) + (backend (plist-get info :backend)) + (host (plist-get info :host)) + (port (plist-get info :port)) + (data (plist-get info :data))) + (when backend + (apply #'rudel-zeroconf-publish + backend name host port data))) + t) + +(defmethod rudel-withdraw ((this rudel-session-initiation-backend)) + "Withdraw Zeroconf record." + (error "Not implemented, yet")) + + +;;; Zeroconf wrapper functions +;; + +(defun rudel-zeroconf-services (backend) + "List Zeroconf services for BACKEND." + (zeroconf-list-services + (rudel-zeroconf-service-type + (rudel-zeroconf-service + #'rudel-zeroconf-service-backend backend)))) + +(defun rudel-zeroconf-services-present-p (backend) + "Check whether there are Zeroconf services for BACKEND." + (rudel-zeroconf-services backend)) + +(defun rudel-zeroconf-publish (backend name host port &rest data) + "Publish BACKEND service NAME for HOST and PORT." + ;; Try to find the service entry for the protocol backend and + ;; publish the service if an entry is found. + (let ((service (rudel-zeroconf-service + #'rudel-zeroconf-service-backend backend))) + (when service + (zeroconf-publish-service + name + (rudel-zeroconf-service-type service) + "local" + (concat host ".local") + port + "" ; address + (mapcar + (lambda (item) + (concat (car item) "=" (cdr item))) + data) + ))) + ) + +(defun rudel-zeroconf-withdraw (backend name) + "Withdraw service NAME for BACKEND." + (error "Not implemented, yet")) + + +;;; Miscellaneous functions +;; + +(defun rudel-zeroconf-service->plist (service) + "Convert a Zeroconf service record to an info property list." + (let* ((type (zeroconf-service-type service)) + (data (rudel-zeroconf-parse-txt-record + (zeroconf-service-txt service))) + (service-type (rudel-zeroconf-service + #'rudel-zeroconf-service-type type))) + ;; Construct information property list. + (list + :name (format "Zeroconf advertised session \"%s\"" + (zeroconf-service-name service)) + :backend (rudel-backend-get + 'protocol + (rudel-zeroconf-service-backend service-type)) + :host (zeroconf-service-host service) + :port (zeroconf-service-port service) + ;; Encryption defaults to yes to be compatible with Gobby. + :encryption (or (not (member :encryption data)) + (string= (plist-get data :encryption) + "yes")))) + ) + +(defun rudel-zeroconf-parse-txt-record (record) + "Parse RECORD into a property list of keys and values." + (apply #'append + (mapcar + (lambda (entry) + (multiple-value-bind (key value) (split-string entry "=") + (list (intern (concat ":" key)) + value))) + record)) + ) + + +;;; User interaction +;; + +(defun rudel-zeroconf-read-service (backend) + "Retrieve services for BACKEND and read one from user." + ;; First, find all matching services for the backend. + (let* ((services (rudel-zeroconf-services backend)) + ;; Read one of the names of these services. + (service-name (completing-read + "Service: " + (mapcar #'zeroconf-service-name services) + nil t)) + ;; Retrieve and resolve the selected service object. + (service (find service-name services + :key #'zeroconf-service-name + :test #'string=)) + (service-resolved (zeroconf-resolve-service service))) + ;; Return host and port + (list (zeroconf-service-host service-resolved) + (zeroconf-service-port service-resolved))) + ) + + +;;; Autoloading +;; + +;;;###autoload +(rudel-add-backend (rudel-backend-get-factory 'session-initiation) + 'zeroconf 'rudel-zeroconf-backend) + +(provide 'rudel-zeroconf) +;;; rudel-zeroconf.el ends here -- cgit v1.2.3