From 74d9459cfb4bedb4b75f46d311f51fc8955e1bf9 Mon Sep 17 00:00:00 2001 From: mathieui Date: Thu, 26 Jul 2012 18:32:25 +0200 Subject: [PATCH] Add an overview of the poezio internals to the documentation --- doc/en/dev/index.txt | 7 +++ doc/en/dev/overview.txt | 106 ++++++++++++++++++++++++++++++++++++++++ doc/images/layers.png | Bin 0 -> 15642 bytes 3 files changed, 113 insertions(+) create mode 100644 doc/en/dev/index.txt create mode 100644 doc/en/dev/overview.txt create mode 100644 doc/images/layers.png diff --git a/doc/en/dev/index.txt b/doc/en/dev/index.txt new file mode 100644 index 00000000..263151fb --- /dev/null +++ b/doc/en/dev/index.txt @@ -0,0 +1,7 @@ +Development +=========== + +This section of the documentation is here to provide a quickstart for someone +willing to start hacking poezio. + +* link:overview.html[Overview] diff --git a/doc/en/dev/overview.txt b/doc/en/dev/overview.txt new file mode 100644 index 00000000..ad8f63bf --- /dev/null +++ b/doc/en/dev/overview.txt @@ -0,0 +1,106 @@ +Overview +======== + +NOTE: This is not an introduction to XMPP, but to poezio + + +Global overview +--------------- + +Poezio is an application that has three main layers, mostly separated in three +different python modules: _core_, _tabs_, and _windows_. An UML diagram of +Poezio would be inneficient, cluttered, or incomplete, so there is none, if +that bugs you. + +image::../../images/layers.png["The application layers", title="Layers"] + +_Core_ is mostly a “global” object containing the state of the application at +any time, it contains the global commands, the xmpp event handlers, the list +of open tabs, etc. Most objects in poezio have a self.core attribute +referencing the _Core_ (it’s a singleton, so there is never more than one +instance). _Core_ also contains the main loop of the application, which then +dispatchs the I/O events (keypress) to the appropriate methods. + + +But the main loop is not the most important thing in poezio; because it is an +IM client, it is essentially event-driven. The event part is handled by +SleekXMPP, which is the library we chose after moving away from xmpppy. + + +_Tabs_ are the second layer of poezio, but the first dealing with the UI: each +_Tab_ is a layout of several _windows_, it contains tab-specific commands, +tab-specific keybinds, and it has methods in order for core to +interact with it, and some methods are only proxies for the methods of a +_window_ + +Example scenario: If someone presses the key PageUp, then Core will call the +appropriate method on the current _Tab_, which will in turn, if it implements the +method (inherited empty from the Tab class), call a scrolling method from the +appropriate _window_. + +All tabs types inherit from the class _Tab_, and the _Tabs_ featuring +chat functionnality will inherit fro _ChatTab_ (which inherits from _Tab_). + +Examples of _Tabs_: MUCTab, XMLTab, RosterTab, MUCListTab, etc… + +Event handlers +-------------- + +The events handlers are registered right at the start of poezio, and then +when a matching stanza is received, the handler is called in a separate thread +from the main loop. The handlers are in _Core_, and then they call the +appropriate methods in the corresponding _tabs_. + +Example scenario: if a message is received from a MUC, then the _Core_ handler +will identify the _Tab_, and call the relevant handler from this _Tab_, this tab +will in turn, add the message to the buffer, which will then add it to the +relevant _windows_. + +NOTE: All the _windows_ that deal with received or generated text are linked +to a _text_buffer_, in order to rebuild all the display lines from an the +sources if necessary. This also enables us to have several _windows_ +presenting the same text, even if they are not of the same size and layout. + + +Commands and completion +----------------------- + +Commands are quite straightforward: those are methods that take a string as a +parameter, and they do stuff. + +From an user point of view, the methods are entered like that: + +================================== + + /command arg1 arg2 + +or + + /command "arg1 with spaces" arg2 + +================================== + +However, when creating a command, you wil deal with _one_ str, no matter what. +There are utilities to deal with it (common.shell_split), but it is not always +necessary. Commands are registered in the _commands_ dictionnary of a tab +structured as key (command name) -> tuple(command function, help string, completion). + + +Completions are a bit tricky, but it’s easy once you get used to it: + +They take an _Input_ (a _windows_ class) as a parameter, named the_input +everywhere in the sources. To effectively have a completion, you have to call +_the_input.auto_completion()_ at the end of the function. + +*the_input.auto_completion(completion_list, after='', quote=True)*: +Set the input to iterate over _completion_list_ when the user hits tab, insert +_after_ after the completed item, and surround the item with double quotes or +not. + +There is no method to find the current argument in the input (although the +feature is planned), so you have to assume the current argument is the last, +and guess it by splitting the string an checking for end-space. + +You can look for examples in the sources, all the possible cases are +covered (single-argument, complex arguments with spaces, several arguments, +etc…) diff --git a/doc/images/layers.png b/doc/images/layers.png new file mode 100644 index 0000000000000000000000000000000000000000..9ec71521078587de3fededd34dc658f42fb4c837 GIT binary patch literal 15642 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