pax_global_header00006660000000000000000000000064144615171340014520gustar00rootroot0000000000000052 comment=ced07fe0d48ce1111d7a8376fdbfef34d927c967 helpful-0.21/000077500000000000000000000000001446151713400131015ustar00rootroot00000000000000helpful-0.21/.github/000077500000000000000000000000001446151713400144415ustar00rootroot00000000000000helpful-0.21/.github/workflows/000077500000000000000000000000001446151713400164765ustar00rootroot00000000000000helpful-0.21/.github/workflows/test.yml000066400000000000000000000025151446151713400202030ustar00rootroot00000000000000name: Run test suite on: [push, pull_request] jobs: test: runs-on: ubuntu-latest strategy: matrix: emacs_version: - '27.1' - '28.1' steps: - uses: purcell/setup-emacs@master with: version: ${{ matrix.emacs_version }} - uses: conao3/setup-cask@master - uses: actions/checkout@v2 - name: Install dependencies with Cask run: cask install - name: Download Emacs source code run: "./download_emacs_src.sh" - name: Run tests env: COVERALLS_FLAG_NAME: Emacs ${{ matrix.emacs_version }} COVERALLS_PARALLEL: 1 GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Run tests under both interpreted and compiled elisp. run: cask exec ert-runner - name: Run tests (compiled elisp) env: COVERALLS_FLAG_NAME: Emacs ${{ matrix.emacs_version }} compiled COVERALLS_PARALLEL: 1 GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Run tests under both interpreted and compiled elisp. run: | cask build cask exec ert-runner finalize: runs-on: ubuntu-latest if: always() needs: test steps: - run: curl "https://coveralls.io/webhook?repo_name=$GITHUB_REPOSITORY&repo_token=${{ secrets.GITHUB_TOKEN }}" -d "payload[build_num]=$GITHUB_RUN_NUMBER&payload[status]=done" helpful-0.21/.gitignore000066400000000000000000000000521446151713400150660ustar00rootroot00000000000000.cask *.elc emacs-25.3 emacs-25.3.tar.gz helpful-0.21/CHANGELOG.md000066400000000000000000000307631446151713400147230ustar00rootroot00000000000000# v0.21 (unreleased) Added more keybindings (`C-h x`, remapped `revert-buffer` and `revert-buffer-quick`). # v0.20 (released 30th July 2023) Fixed support for Emacs 29. Added support for storing org-mode links from inside Helpful buffers. # v0.19 (released 12th May 2022) Fixed a hang when looking at functions that had advice but hadn't yet been loaded by the autoloader (#179, #191). Fixed issue with displaying circular data structures. Fixed a crash in `helpful-variable` in files that weren't syntactically valid lisp. Fixed stack overflow in macroexpanding large s-expressions (#279). # v0.18 Show the original value for custom variables whose value has changed. Report the package version when custom variables were added. Fixed a crash on assigning byte-compiled objects to keybindings. Fixed an issue with advice being shown in docstrings on Emacs 27+. Symbol links in docstrings are now smarter in cases like "function `foo'". ## Improvements to the default symbol offered Functions: * If the symbol at point is a bound function, offer that. * If point is at a function call, offer that. Variables: * If the symbol at point is a bound variable, offer that. * If point is at a `defvar` or `defcustom` call, offer that variable. Symbols: * If the symbol at point is a bound function, offer that. * Try the function, then the variable heuristics described above. This should also make transitioning from help.el easier, and should improve helpful in literate org-mode files. # v0.17 Fixed a minor docstring formatting issue when keymap references were on their own line. Symbols of the form `foo-functions` (e.g. `after-change-functions`) are now rendered as hooks. String literals are now rendered correctly in docstrings. Previously command substitution applied inside literals. Fixed a crash on keyboard macros in keymaps. String values are now shown as literals by default. Fixed an issue with modes that override after-change-major-mode-hook, such as `global-hi-lock-mode`. Added bookmark support. # v0.16 Improved wording when looking at aliases. Fixed several issues for symbols that contain spaces. Fixed an issue when viewing `inhibit-read-only`. Improved the buffer prompt to be more relevant when inspecting buffer-local values. Better source detection for functions defined by `defstruct` or other macros. Fixed issues with functions that had more than one advice active. Added looking up C-style Lisp names. Set `comment-start` inside helpful buffers, to fix external packages relying on that variable. Helpful now always autoloads callables if they aren't already loaded. This is consistent with help.el (unless you've overridden `help-enable-auto-load`), produces more useful results, and fixes crashes rendering some docstrings. # v0.15 Fixed a crash on formatting values. # v0.14 Named keyboard macros are now supported. Function callees are now sorted. Fixed an issue where source code of primitive variables included the whole surrounding function. This was less useful and significantly slower. Fixed an issue with Remacs compatibility. Fixed an issue with the prompt when setting variables whose current value was nil or a keyword. Fixed an issue with going to definitions when the source buffer was narrowed. Navigation keybindings now work in callee list buffers. Fixed an issue where we didn't show function aliases as aliases when they pointed to a primitive function. Fixed an issue with primitive variables when using Helpful with Remacs. # v0.13 Buffer-local variables are now highlighted, and it's possible to see all the different values of a buffer-local variable. Variable values are now shown before docstrings. Fixed an issue where special forms were incorrectly described as functions. Helpful now shows keybindings for aliases of the current command too. Fixed an issue where functions defined in .el.gz files were not recognised as being autoloaded. Fixed an issue where we didn't show the source code for advised primitives. Show the default value for the symbol in the minibuffer prompt. # v0.12 Added a 'pretty view' for string values, keymap values, and hooks. * For strings, we show properties natively in Emacs. * For keymaps, we render each keybinding in a human-readable way along with a link to the relevant command. * For hooks, which are lists of functions and symbols, convert symbols to links. Added a 'view callees' button which shows functions called inside the bodies of source code, with links to view them inside helpful. Fixed a crash on keymaps where keys are bound to anonymous functions. Improved performance (PR by @nickdrozd). It's now possible to move between buttons in the Helpful buffer with `n` and `p`. # v0.11 Further work on syntax highlighting performance for large code snippets. Helpful now informs the user when it has intentionally disabled highlighting, and shows a link to the relevant setting. Fixed an issue where Helpful didn't find keybindings if the command was only referenced in `minor-mode-map-alist`. Fixed a crash when minor modes had invalid keymaps. Fixed an issue with `helpful-variable` where the user was pointlessly prompted about file variables. Fixed a crash when a docstring referenced a non-existent keymap. Linkify docstring URLs of the form ``. # v0.10 When visiting a reference, the occurrences of the symbol are temporarily highlighted. Keybindings in `widget-global-map` are now ignored as they're rarely informative. URLs in docstrings are now converted to buttons. Function signatures are now correct when a function uses `(declare (advertised-calling-convention ...))`. Fixed an issue where escaped backticks were confused with unescaped backticks in docstrings. Fixed an issue with very large code snippets making helpful hang due to slow syntax highlighting. This was particularly problematic for C functions and variables. When extracting the source for an item, include preceding comments and autoload cookies. # v0.9 Much better handling of aliases: show aliases differently to their underlying symbol, show the `defalias` call in the source code, don't show duplicates in the Aliases list, and cross-reference the Elisp manual. Fixed an issue where keymaps with prefix keys were not rendered correctly. Fixed an issue when looking at variable docs where we would visit the definition buffer without the user requesting it. Wording polish for finding references of primitives. Fixed some corner cases in references to info manual sections not being linkified. Fixed an issue where the definition of interactively defined functions wasn't shown. Pretty-printing is now much more robust, gracefully handling very large lists. # v0.8 Added setting `helpful-switch-buffer-function` to allow users to control how the Helpful buffer is shown. Fixed a crash on functions where the last form in their definition is a plain symbol. Fixed an issue where we would show function source code for variables where we can't find the variable definition. Fixed a crash on functions defined in a buffer that isn't backed by a file. We now provide a button to navigate to that buffer. Improved wording for functions with no source at all. Fixed an issue with obsolete aliases without version information. Fixed an issue with top-level references where we would show the previous form rather than the relevant one. # v0.7 Helpful buffers now start with a summary of what you're looking at, including quick links to the relevant source code. Interactive and autoloaded functions are highlighted and include relevant links to the manual. Fixed a crash on functions defined in a .elc when the .el file is not available. Fixed some crashes on primitive functions. Improved finding source code when edebug info is available. Better handling of docstrings: * All strings in quotes are highlighted. For example, previously `` `C-M-\' `` was not highlighted because `C-M-\` doesn't look like a symbol. * Handle nested key sequences correctly, such as `` `\\[foo]' ``. * Handle keymap sequences, such as `\\{foo-mode-map}`. # v0.6 Added imenu support! You can now navigate between headings with imenu. Better handling of docstrings: * Correctly handle \= escapes * Quoted keywords are now highlighted * Correctly highlight docstrings that contain standalone `` ` `` * Ensure we link command references in variable docstrings too Added disassemble buttons to byte-code functions in symbol properties. Fixed an issue with the prompt when setting variables to symbol values. Smarter handling of keybindings: * Consider all keymaps, not just `foo-mode-map`. * Show keybindings where they are defined, don't show them in keymaps that inherit them. * Don't show menu bar items, as they clutter the display (please file a bug if you miss this). * Correctly handle `\` in docstrings. When `helpful-max-buffers` is set, we now kill buffers in the order of least recently accessed. Previously, we killed in creation order. If a function also has a unit test of the same name, allow it to be run directly from the property list. Fixed an issue where we would repeatedly prompt the user regarding unsafe buffer-local variables. # v0.5 Allow function tracing to be enabled/disabled from Helpful buffers. Ensure docstring references to Info nodes are converted to buttons. Docstring references to command keys are converted to buttons too (see `set-mark-command` for an example). Helpful now shows all aliases for callables and variables, and highlights which aliases are obsolete. Improved helpful performance for primitives when Emacs source code is loaded. Helpful will now only keep the last 5 buffers, to avoid cluttering your buffer list. You can customise this behaviour with `helpful-max-buffers`: set it to 1 to cleanup all previous buffers, or set it to `nil` never cleanup buffers. ## recentf bug Helpful had an issue where it would call find-file with propertized strings. This broke various recentf features. This has been fixed, and you can check if you're running a fixed version by seeing whether you have a `helpful--button` function defined. If you do, your version is new enough. You will aso need to edit your `~/.emacs.d/recentf` and `recentf-save.el` to remove any lines that start with a `#`: ``` emacs-lisp #("/usr/share/emacs/25.3.50/lisp/frame.el.gz" 0 41 (button (t) category helpful-navigate-button-button path #0 position 2815)) ``` Otherwise, you will get `Invalid read syntax: "#"` when starting Emacs. # v0.4 You can now enable edebug directly from helpful buffers! Variables may be set from the helpful buffer, and booleans can be toggled. If a variable is a `defcustom`, we also offer Customize. Show the name of variables at the top of the helpful buffer. Improved handling of special forms, and prevent users from accidentally unbinding special forms. Ensure we can find (and jump to) the definition of functions even if the source buffer has narrowing in effect. Fixed an issue where calling helpful commands on interactively defined functions would overwrite unrelated buffers. Fixed a crash with interactively defined functions being edebugged. Added a cleanup command `helpful-kill-buffers`. Fixed an issue with helpful making recentf write broken paths to ~/.recentf, breaking Emacs startup. # v0.3 Fixed a crash on autoloaded functions that aren't loaded yet. Fixed a crash in `helpful-key` for symbols that aren't bound to commands. Fixed an issue where viewing help for a function opened a buffer with its source code. Helpful now cleans up any extra buffers it created. Pressing RET on the extracted source code now goes to the buffer and position where the source code is located. TAB now moves between buttons in helpful buffers. Buffer names now include 'function' or 'variable' etc, e.g. `*helpful function: message*`. # v0.2 Fixed a crash on viewing aliased primitive functions. Fixed an issue where we didn't find the path for functions defined interactively using `cl-defstruct`. Interactively defined functions (e.g. try re-evaluating package.el.gz and looking at `package-desc-name`) are converted from raw closures to equivalent defun forms. We now show the value of variables too. Added a command `helpful-callable`, which offers both macros and functions. This should be a drop-in replacement for `describe-function`. Added a command `helpful-key`, which offers help on keybindings much like `describe-key`. Added a command `helpful-symbol`, which offers help on variables, functions and macros. It prompts the user if a symbol is both a variable and a callable. If a symbol is mentioned in the Emacs manual, show a link to the relevant section. # v0.1 First release. helpful-0.21/Cask000066400000000000000000000002031446151713400137000ustar00rootroot00000000000000(source melpa) (package-file "helpful.el") (development (depends-on "f") (depends-on "ert-runner") (depends-on "undercover")) helpful-0.21/README.md000066400000000000000000000077251446151713400143730ustar00rootroot00000000000000# Helpful [![Coverage Status](https://coveralls.io/repos/github/Wilfred/helpful/badge.svg?branch=master)](https://coveralls.io/github/Wilfred/helpful?branch=master) [![MELPA](http://melpa.org/packages/helpful-badge.svg)](http://melpa.org/#/helpful) [![](https://tokei.rs/b1/github/wilfred/helpful)](https://github.com/Aaronepower/tokei) Helpful is an alternative to the built-in Emacs help that provides much more contextual information. ![screenshot](screenshots/helpful.png) ## Usage Install from MELPA, then call one of the following commands: * `helpful-callable` * `helpful-function` * `helpful-macro` * `helpful-command` * `helpful-key` * `helpful-variable` * `helpful-at-point` If you want to replace the default Emacs help keybindings, you can do so: ``` emacs-lisp ;; Note that the built-in `describe-function' includes both functions ;; and macros. `helpful-function' is functions only, so we provide ;; `helpful-callable' as a drop-in replacement. (global-set-key (kbd "C-h f") #'helpful-callable) (global-set-key (kbd "C-h v") #'helpful-variable) (global-set-key (kbd "C-h k") #'helpful-key) (global-set-key (kbd "C-h x") #'helpful-command) ``` I also recommend the following keybindings to get the most out of helpful: ``` emacs-lisp ;; Lookup the current symbol at point. C-c C-d is a common keybinding ;; for this in lisp modes. (global-set-key (kbd "C-c C-d") #'helpful-at-point) ;; Look up *F*unctions (excludes macros). ;; ;; By default, C-h F is bound to `Info-goto-emacs-command-node'. Helpful ;; already links to the manual, if a function is referenced there. (global-set-key (kbd "C-h F") #'helpful-function) ``` [Ivy](https://github.com/abo-abo/swiper) users can use Helpful with counsel commands: ``` emacs-lisp (setq counsel-describe-function-function #'helpful-callable) (setq counsel-describe-variable-function #'helpful-variable) ``` ## Features ### Source code ![screenshot](screenshots/helpful_source.png) Helpful will try really hard to show the source code. It shows the source code for interactively defined functions (unlike the built-in Help) and falls back to the raw sexp if no source is available. ### View Callers ![screenshot](screenshots/helpful_refs.png) Helpful will show you where a function is being called! ### Prettier Docstrings ![screenshot](screenshots/helpful_docstring.png) Docstrings in helpful: * Highlight the summary (the first sentence) * Include cross-references to other functions/variables * Linkify references to Info nodes * Hide superfluous punctuation ![screenshot](screenshots/helpful_view_in_manual.png) If a symbol is also documented in the Info manual, helpful will provide a link to the relevant section too. ### Symbol Properties ![screenshot](screenshots/helpful_props.png) Helpful will show you the properties that have been applied to the current symbol. This provides visibility of features like edebug or byte-code optimisation. Helpful will also highlight any symbol aliases. ### Describe Commands Helpful provides a separate `helpful-command` function, for when you just want to view interactive functions. ### View Keymaps ![screenshot](screenshots/helpful_bindings.png) Helpful displays any keybindings that apply to interactive functions. ### Integrated Tooling ![screenshot](screenshots/helpful_tools.png) You can trace, debug or disassemble functions from inside Helpful. This is discoverable and doesn't require memorisation of commands. ### Aliases ![screenshot](screenshots/helpful_aliases.png) If a function has multiple aliases in Emacs, Helpful will show all of the aliases defined. ## Inspirations This project has been heavily influenced by: * [help+.el](https://www.emacswiki.org/emacs/help+.el), help-fns+.el, help-mode+.el * [Dave Williams' demo of Lucid's Energize](https://www.youtube.com/watch?v=pQQTScuApWk) ## License GPLv3+. I am providing code in the repository to you under an open source license. Because this is my personal repository, the license you receive to my code is from me and not my employer. helpful-0.21/download_emacs_src.sh000077500000000000000000000002371446151713400172700ustar00rootroot00000000000000#!/bin/bash if [ ! -d "emacs-25.3" ]; then set -e set -x wget http://ftpmirror.gnu.org/emacs/emacs-25.3.tar.gz tar -xzf emacs-25.3.tar.gz fi helpful-0.21/helpful.el000066400000000000000000003210751446151713400150720ustar00rootroot00000000000000;;; helpful.el --- A better *help* buffer -*- lexical-binding: t; -*- ;; Copyright (C) 2017-2022 Wilfred Hughes ;; Author: Wilfred Hughes ;; URL: https://github.com/Wilfred/helpful ;; Keywords: help, lisp ;; Version: 0.21 ;; Package-Requires: ((emacs "25") (dash "2.18.0") (s "1.11.0") (f "0.20.0") (elisp-refs "1.2")) ;; 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: ;; Helpful is a replacement for *help* buffers that provides much more ;; contextual information. To get started, try: ;; `M-x helpful-function RET helpful-function ;; ;; The full set of commands you can try is: ;; ;; * helpful-function ;; * helpful-command ;; * helpful-key ;; * helpful-macro ;; * helpful-callable ;; * helpful-variable ;; * helpful-at-point ;; ;; For more information and screenshots, see ;; https://github.com/wilfred/helpful ;;; Code: (require 'elisp-refs) (require 'help) (require 'help-fns) (require 'dash) (require 's) (require 'f) (require 'find-func) (require 'nadvice) (require 'info-look) (require 'edebug) (require 'trace) (require 'imenu) (require 'cc-langs) (declare-function org-link-types "ol" ()) (declare-function org-link-store-props "ol" (&rest plist)) (declare-function org-link-get-parameter "ol" (type key)) (defvar-local helpful--sym nil) (defvar-local helpful--callable-p nil) (defvar-local helpful--associated-buffer nil "The buffer being used when showing inspecting buffer-local variables.") (defvar-local helpful--start-buffer nil "The buffer we were originally called from.") (defvar-local helpful--view-literal nil "Whether to show a value as a literal, or a pretty interactive view.") (defvar-local helpful--first-display t "Whether this is the first time this results buffer has been displayed. Nil means that we're refreshing, so we don't want to clobber any settings changed by the user.") (defgroup helpful nil "A rich help system with contextual information." :link '(url-link "https://github.com/Wilfred/helpful") :group 'help) (defcustom helpful-max-buffers 5 "Helpful will kill the least recently used Helpful buffer if there are more than this many. To disable cleanup entirely, set this variable to nil. See also `helpful-kill-buffers' for a one-off cleanup." :type '(choice (const nil) number) :group 'helpful) (defcustom helpful-switch-buffer-function #'pop-to-buffer "Function called to display the *Helpful* buffer." :type 'function :group 'helpful) ;; TODO: explore whether more basic highlighting is fast enough to ;; handle larger functions. See `c-font-lock-init' and its use of ;; font-lock-keywords-1. (defconst helpful-max-highlight 5000 "Don't highlight code with more than this many characters. This is currently only used for C code, as lisp highlighting seems to be more efficient. This may change again in future. See `this-command' as an example of a large piece of C code that can make Helpful very slow.") (defun helpful--kind-name (symbol callable-p) "Describe what kind of symbol this is." (cond ((not callable-p) "variable") ((commandp symbol) "command") ((macrop symbol) "macro") ((functionp symbol) "function") ((special-form-p symbol) "special form"))) (defun helpful--buffer (symbol callable-p) "Return a buffer to show help for SYMBOL in." (let* ((current-buffer (current-buffer)) (buf-name (format "*helpful %s*" (if (symbolp symbol) (format "%s: %s" (helpful--kind-name symbol callable-p) symbol) "lambda"))) (buf (get-buffer buf-name))) (unless buf ;; If we need to create the buffer, ensure we don't exceed ;; `helpful-max-buffers' by killing the least recently used. (when (numberp helpful-max-buffers) (let* ((buffers (buffer-list)) (helpful-bufs (--filter (with-current-buffer it (eq major-mode 'helpful-mode)) buffers)) ;; `buffer-list' seems to be ordered by most recently ;; visited first, so keep those. (excess-buffers (-drop (1- helpful-max-buffers) helpful-bufs))) ;; Kill buffers so we have one buffer less than the maximum ;; before we create a new one. (-each excess-buffers #'kill-buffer))) (setq buf (get-buffer-create buf-name))) ;; Initialise the buffer with the symbol and associated data. (with-current-buffer buf (helpful-mode) (setq helpful--sym symbol) (setq helpful--callable-p callable-p) (setq helpful--start-buffer current-buffer) (setq helpful--associated-buffer current-buffer) (setq list-buffers-directory (if (symbolp symbol) (format "%s: %s" (helpful--kind-name symbol callable-p) symbol) "lambda")) (if (helpful--primitive-p symbol callable-p) (setq-local comment-start "//") (setq-local comment-start ";"))) buf)) (defface helpful-heading '((t (:weight bold))) "Face used for headings in Helpful buffers.") (defun helpful--heading (text) "Propertize TEXT as a heading." (propertize (concat text "\n") 'face 'helpful-heading)) (defun helpful--format-closure (sym form) "Given a closure, return an equivalent defun form." (-let (((_keyword _env args . body) form) (docstring nil)) (when (stringp (car body)) (setq docstring (car body)) (setq body (cdr body)) ;; Ensure that the docstring doesn't have lines starting with (, ;; or it breaks indentation. (setq docstring (s-replace "\n(" "\n\\(" docstring))) (if docstring `(defun ,sym ,args ,docstring ,@body) `(defun ,sym ,args ,@body)))) (defun helpful--pretty-print (value) "Pretty-print VALUE. If VALUE is very big, the user may press \\[keyboard-quit] to gracefully stop the printing. If VALUE is self-referential, the error will be caught and displayed." ;; Inspired by `ielm-eval-input'. (condition-case err (s-trim-right (pp-to-string value)) (error (propertize (format "(Display error: %s)" (cadr err)) 'face 'font-lock-comment-face)) (quit (propertize "(User quit during pretty-printing.)" 'face 'font-lock-comment-face)))) (defun helpful--sort-symbols (sym-list) "Sort symbols in SYM-LIST alphabetically." (--sort (string< (symbol-name it) (symbol-name other)) sym-list)) (defun helpful--button (text type &rest properties) ;; `make-text-button' mutates our string to add properties. Copy ;; TEXT to prevent mutating our arguments, and to support 'pure' ;; strings, which are read-only. (setq text (substring-no-properties text)) (apply #'make-text-button text nil :type type properties)) (defun helpful--canonical-symbol (sym callable-p) "If SYM is an alias, return the underlying symbol. Return SYM otherwise." (let ((depth 0)) (if (and (symbolp sym) callable-p) (progn ;; Follow the chain of symbols until we find a symbol that ;; isn't pointing to a symbol. (while (and (symbolp (symbol-function sym)) (< depth 10)) (setq sym (symbol-function sym)) (setq depth (1+ depth))) ;; If this is an alias to a primitive, return the ;; primitive's symbol. (when (subrp (symbol-function sym)) (setq sym (intern (subr-name (symbol-function sym)))))) (setq sym (indirect-variable sym)))) sym) (defun helpful--aliases (sym callable-p) "Return all the aliases for SYM." (let ((canonical (helpful--canonical-symbol sym callable-p)) aliases) (mapatoms (lambda (s) (when (and ;; Skip variables that aren't bound, so we're faster. (if callable-p (fboundp s) (boundp s)) ;; If this symbol is a new alias for our target sym, ;; add it. (eq canonical (helpful--canonical-symbol s callable-p)) ;; Don't include SYM. (not (eq sym s))) (push s aliases)))) (helpful--sort-symbols aliases))) (defun helpful--obsolete-info (sym callable-p) (when (symbolp sym) (get sym (if callable-p 'byte-obsolete-info 'byte-obsolete-variable)))) (defun helpful--format-alias (sym callable-p) (let ((obsolete-info (helpful--obsolete-info sym callable-p)) (sym-button (helpful--button (symbol-name sym) 'helpful-describe-exactly-button 'symbol sym 'callable-p callable-p))) (cond (obsolete-info (-if-let (version (-last-item obsolete-info)) (format "%s (obsolete since %s)" sym-button version) (format "%s (obsolete)" sym-button))) (t sym-button)))) (defun helpful--indent-rigidly (s amount) "Indent string S by adding AMOUNT spaces to each line." (with-temp-buffer (insert s) (indent-rigidly (point-min) (point-max) amount) (buffer-string))) (defun helpful--format-properties (symbol) "Return a string describing all the properties of SYMBOL." (let* ((syms-and-vals (-partition 2 (and (symbolp symbol) (symbol-plist symbol)))) (syms-and-vals (-sort (-lambda ((sym1 _) (sym2 _)) (string-lessp (symbol-name sym1) (symbol-name sym2))) syms-and-vals)) (lines (--map (-let* (((sym val) it) (pretty-val (helpful--pretty-print val))) (format "%s\n%s%s" (propertize (symbol-name sym) 'face 'font-lock-constant-face) (helpful--indent-rigidly pretty-val 2) (cond ;; Also offer to disassemble byte-code ;; properties. ((byte-code-function-p val) (format "\n %s" (helpful--make-disassemble-button val))) ((eq sym 'ert--test) (format "\n %s" (helpful--make-run-test-button symbol))) (t "")))) syms-and-vals))) (when lines (s-join "\n" lines)))) (define-button-type 'helpful-forget-button 'action #'helpful--forget 'symbol nil 'callable-p nil 'follow-link t 'help-echo "Unbind this function") ;; TODO: it would be nice to optionally delete the source code too. (defun helpful--forget (button) "Unbind the current symbol." (let* ((sym (button-get button 'symbol)) (callable-p (button-get button 'callable-p)) (kind (helpful--kind-name sym callable-p))) (when (yes-or-no-p (format "Forget %s %s?" kind sym)) (if callable-p (fmakunbound sym) (makunbound sym)) (message "Forgot %s %s." kind sym) (kill-buffer (current-buffer))))) (define-button-type 'helpful-c-source-directory 'action #'helpful--c-source-directory 'follow-link t 'help-echo "Set directory to Emacs C source code") (defun helpful--c-source-directory (_button) "Set `find-function-C-source-directory' so we can show the source code to primitives." (let ((emacs-src-dir (read-directory-name "Path to Emacs source code: "))) ;; Let the user specify the source path with or without src/, ;; which is a subdirectory in the Emacs tree. (unless (equal (f-filename emacs-src-dir) "src") (setq emacs-src-dir (f-join emacs-src-dir "src"))) (setq find-function-C-source-directory emacs-src-dir)) (helpful-update)) (define-button-type 'helpful-disassemble-button 'action #'helpful--disassemble 'follow-link t 'object nil 'help-echo "Show disassembled bytecode") (defun helpful--disassemble (button) "Disassemble the current symbol." ;; `disassemble' can handle both symbols (e.g. 'when) and raw ;; byte-code objects. (disassemble (button-get button 'object))) (define-button-type 'helpful-run-test-button 'action #'helpful--run-test 'follow-link t 'symbol nil 'help-echo "Run ERT test") (defun helpful--run-test (button) "Disassemble the current symbol." (ert (button-get button 'symbol))) (define-button-type 'helpful-edebug-button 'action #'helpful--edebug 'follow-link t 'symbol nil 'help-echo "Toggle edebug (re-evaluates definition)") (defun helpful--kbd-macro-p (sym) "Is SYM a keyboard macro?" (and (symbolp sym) (let ((func (symbol-function sym))) (or (stringp func) (vectorp func))))) (defun helpful--edebug-p (sym) "Does function SYM have its definition patched by edebug?" (let ((fn-def (indirect-function sym))) ;; Edebug replaces function source code with a sexp that has ;; `edebug-enter', `edebug-after' etc interleaved. This means the ;; function is interpreted, so `indirect-function' returns a list. (when (and (consp fn-def) (consp (cdr fn-def))) (-let [fn-end (-last-item fn-def)] (and (consp fn-end) (eq (car fn-end) 'edebug-enter)))))) (defun helpful--can-edebug-p (sym callable-p buf pos) "Can we use edebug with SYM?" (and ;; SYM must be a function. callable-p ;; The function cannot be a primitive, it must be defined in elisp. (not (helpful--primitive-p sym callable-p)) ;; We need to be able to find its definition, or we can't step ;; through the source. buf pos)) (defun helpful--toggle-edebug (sym) "Enable edebug when function SYM is called, or disable if already enabled." (-let ((should-edebug (not (helpful--edebug-p sym))) ((buf pos created) (helpful--definition sym t))) (if (and buf pos) (progn (with-current-buffer buf (save-excursion (save-restriction (widen) (goto-char pos) (let* ((edebug-all-forms should-edebug) (edebug-all-defs should-edebug) (form (edebug-read-top-level-form))) ;; Based on `edebug-eval-defun'. (eval (eval-sexp-add-defvars form) lexical-binding))))) ;; If we're enabling edebug, we need the source buffer to ;; exist. Otherwise, we can clean it up. (when (and created (not should-edebug)) (kill-buffer buf))) (user-error "Could not find source for edebug")))) (defun helpful--edebug (button) "Toggle edebug for the current symbol." (helpful--toggle-edebug (button-get button 'symbol)) (helpful-update)) (define-button-type 'helpful-trace-button 'action #'helpful--trace 'follow-link t 'symbol nil 'help-echo "Toggle function tracing") (defun helpful--trace (button) "Toggle tracing for the current symbol." (let ((sym (button-get button 'symbol))) (if (trace-is-traced sym) (untrace-function sym) (trace-function sym))) (helpful-update)) (define-button-type 'helpful-navigate-button 'action #'helpful--navigate 'path nil 'position nil 'follow-link t 'help-echo "Navigate to definition") (defun helpful--goto-char-widen (pos) "Move point to POS in the current buffer. If narrowing is in effect, widen if POS isn't in the narrowed area." (when (or (< pos (point-min)) (> pos (point-max))) (widen)) (goto-char pos)) (defun helpful--navigate (button) "Navigate to the path this BUTTON represents." (find-file (substring-no-properties (button-get button 'path))) ;; We use `get-text-property' to work around an Emacs 25 bug: ;; http://git.savannah.gnu.org/cgit/emacs.git/commit/?id=f7c4bad17d83297ee9a1b57552b1944020f23aea (-when-let (pos (get-text-property button 'position (marker-buffer button))) (helpful--goto-char-widen pos))) (defun helpful--navigate-button (text path &optional pos) "Return a button that opens PATH and puts point at POS." (helpful--button text 'helpful-navigate-button 'path path 'position pos)) (define-button-type 'helpful-buffer-button 'action #'helpful--switch-to-buffer 'buffer nil 'position nil 'follow-link t 'help-echo "Switch to this buffer") (defun helpful--switch-to-buffer (button) "Navigate to the buffer this BUTTON represents." (let ((buf (button-get button 'buffer)) (pos (button-get button 'position))) (switch-to-buffer buf) (when pos (helpful--goto-char-widen pos)))) (defun helpful--buffer-button (buffer &optional pos) "Return a button that switches to BUFFER and puts point at POS." (helpful--button (buffer-name buffer) 'helpful-buffer-button 'buffer buffer 'position pos)) (define-button-type 'helpful-customize-button 'action #'helpful--customize 'symbol nil 'follow-link t 'help-echo "Open Customize for this symbol") (defun helpful--customize (button) "Open Customize for this symbol." (customize-variable (button-get button 'symbol))) (define-button-type 'helpful-associated-buffer-button 'action #'helpful--associated-buffer 'symbol nil 'prompt-p nil 'follow-link t 'help-echo "Change associated buffer") (defun helpful--read-live-buffer (prompt predicate) "Read a live buffer name, and return the buffer object. This is largely equivalent to `read-buffer', but counsel.el overrides that to include previously opened buffers." (let* ((names (-map #'buffer-name (buffer-list))) (default (cond ;; If we're already looking at a buffer-local value, start ;; the prompt from the relevant buffer. ((and helpful--associated-buffer (buffer-live-p helpful--associated-buffer)) (buffer-name helpful--associated-buffer)) ;; If we're looking at the global value, offer the initial ;; buffer. ((and helpful--start-buffer (buffer-live-p helpful--start-buffer)) (buffer-name helpful--start-buffer)) ;; If we're looking at the global value and have no initial ;; buffer, choose the first normal buffer. (t (--first (and (not (s-starts-with-p " " it)) (not (s-starts-with-p "*" it))) names)) ))) (get-buffer (completing-read prompt names predicate t nil nil default)))) (defun helpful--associated-buffer (button) "Change the associated buffer, so we can see buffer-local values." (let ((sym (button-get button 'symbol)) (prompt-p (button-get button 'prompt-p))) (if prompt-p (setq helpful--associated-buffer (helpful--read-live-buffer "View variable in: " (lambda (buf-name) (local-variable-p sym (get-buffer buf-name))))) (setq helpful--associated-buffer nil))) (helpful-update)) (define-button-type 'helpful-toggle-button 'action #'helpful--toggle 'symbol nil 'buffer nil 'follow-link t 'help-echo "Toggle this symbol between t and nil") (defun helpful--toggle (button) "Toggle the symbol between nil and t." (let ((sym (button-get button 'symbol)) (buf (button-get button 'buffer))) (save-current-buffer ;; If this is a buffer-local variable, ensure we're in the right ;; buffer. (when buf (set-buffer buf)) (set sym (not (symbol-value sym)))) (helpful-update))) (define-button-type 'helpful-set-button 'action #'helpful--set 'symbol nil 'buffer nil 'follow-link t 'help-echo "Set the value of this symbol") (defun helpful--set (button) "Set the value of this symbol." (let* ((sym (button-get button 'symbol)) (buf (button-get button 'buffer)) (sym-value (helpful--sym-value sym buf)) ;; Inspired by `counsel-read-setq-expression'. (expr (minibuffer-with-setup-hook (lambda () (add-function :before-until (local 'eldoc-documentation-function) #'elisp-eldoc-documentation-function) (run-hooks 'eval-expression-minibuffer-setup-hook) (goto-char (minibuffer-prompt-end)) (forward-char (length (format "(setq %S " sym)))) (read-from-minibuffer "Eval: " (format (if (or (consp sym-value) (and (symbolp sym-value) (not (null sym-value)) (not (keywordp sym-value)))) "(setq %s '%S)" "(setq %s %S)") sym sym-value) read-expression-map t 'read-expression-history)))) (save-current-buffer ;; If this is a buffer-local variable, ensure we're in the right ;; buffer. (when buf (set-buffer buf)) (eval-expression expr)) (helpful-update))) (define-button-type 'helpful-view-literal-button 'action #'helpful--view-literal 'help-echo "Toggle viewing as a literal") (defun helpful--view-literal (_button) "Set the value of this symbol." (setq helpful--view-literal (not helpful--view-literal)) (helpful-update)) (define-button-type 'helpful-all-references-button 'action #'helpful--all-references 'symbol nil 'callable-p nil 'follow-link t 'help-echo "Find all references to this symbol") (defun helpful--all-references (button) "Find all the references to the symbol that this BUTTON represents." (let ((sym (button-get button 'symbol)) (callable-p (button-get button 'callable-p))) (cond ((not callable-p) (elisp-refs-variable sym)) ((functionp sym) (elisp-refs-function sym)) ((macrop sym) (elisp-refs-macro sym))))) (define-button-type 'helpful-callees-button 'action #'helpful--show-callees 'symbol nil 'source nil 'follow-link t 'help-echo "Find the functions called by this function/macro") (defun helpful--display-callee-group (callees) "Insert every entry in CALLEES." (dolist (sym (helpful--sort-symbols callees)) (insert " " (helpful--button (symbol-name sym) 'helpful-describe-exactly-button 'symbol sym 'callable-p t) "\n"))) (defun helpful--show-callees (button) "Find all the references to the symbol that this BUTTON represents." (let* ((buf (get-buffer-create "*helpful callees*")) (sym (button-get button 'symbol)) (raw-source (button-get button 'source)) (source (if (stringp raw-source) (read raw-source) raw-source)) (syms (helpful--callees source)) (primitives (-filter (lambda (sym) (helpful--primitive-p sym t)) syms)) (compounds (-remove (lambda (sym) (helpful--primitive-p sym t)) syms))) (pop-to-buffer buf) (let ((inhibit-read-only t)) (erase-buffer) ;; TODO: Macros used, special forms used, global vars used. (insert (format "Functions called by %s:\n\n" sym)) (helpful--display-callee-group compounds) (when primitives (insert "\n") (insert (format "Primitives called by %s:\n\n" sym)) (helpful--display-callee-group primitives)) (goto-char (point-min)) (helpful-mode)))) (define-button-type 'helpful-manual-button 'action #'helpful--manual 'symbol nil 'follow-link t 'help-echo "View this symbol in the Emacs manual") (defun helpful--manual (button) "Open the manual for the system that this BUTTON represents." (let ((sym (button-get button 'symbol))) (info-lookup 'symbol sym #'emacs-lisp-mode))) (define-button-type 'helpful-describe-button 'action #'helpful--describe 'symbol nil 'follow-link t 'help-echo "Describe this symbol") (defun helpful--describe (button) "Describe the symbol that this BUTTON represents." (let ((sym (button-get button 'symbol))) (helpful-symbol sym))) (define-button-type 'helpful-describe-exactly-button 'action #'helpful--describe-exactly 'symbol nil 'callable-p nil 'follow-link t 'help-echo "Describe this symbol") (defun helpful--describe-exactly (button) "Describe the symbol that this BUTTON represents. This differs from `helpful--describe' because here we know whether the symbol represents a variable or a callable." (let ((sym (button-get button 'symbol)) (callable-p (button-get button 'callable-p))) (if callable-p (helpful-callable sym) (helpful-variable sym)))) (define-button-type 'helpful-info-button 'action #'helpful--info 'info-node nil 'follow-link t 'help-echo "View this Info node") (defun helpful--info (button) "Describe the symbol that this BUTTON represents." (info (button-get button 'info-node))) (define-button-type 'helpful-shortdoc-button 'action #'helpful--shortdoc 'info-node nil 'follow-link t 'help-echo "View this Shortdoc group") (defun helpful--shortdoc (button) "Describe the symbol that this BUTTON represents." (shortdoc-display-group (button-get button 'shortdoc-group) (button-get button 'symbol))) (defun helpful--split-first-line (docstring) "If the first line is a standalone sentence, ensure we have a blank line afterwards." (let* ((lines (s-lines docstring)) (first-line (-first-item lines)) (second-line (when (> (length lines) 1) (nth 1 lines)))) (if (and (s-ends-with-p "." first-line) (stringp second-line) (not (equal second-line ""))) (s-join "\n" (-cons* first-line "" (cdr lines))) docstring))) (defun helpful--propertize-sym-ref (sym-name before-txt after-txt) "Given a symbol name from a docstring, convert to a button (if bound) or else highlight." (let* ((sym (intern sym-name))) (cond ;; Highlight keywords. ((s-matches-p (rx ":" symbol-start (+? (or (syntax word) (syntax symbol))) symbol-end) sym-name) (propertize sym-name 'face 'font-lock-builtin-face)) ((and (boundp sym) (s-ends-with-p "variable " before-txt)) (helpful--button sym-name 'helpful-describe-exactly-button 'symbol sym 'callable-p nil)) ((and (fboundp sym) (or (s-starts-with-p " command" after-txt) (s-ends-with-p "command " before-txt) (s-ends-with-p "function " before-txt))) (helpful--button sym-name 'helpful-describe-exactly-button 'symbol sym 'callable-p t)) ;; Only create a link if this is a symbol that is bound as a ;; variable or callable. ((or (boundp sym) (fboundp sym)) (helpful--button sym-name 'helpful-describe-button 'symbol sym)) ;; If this is already a button, don't modify it. ((get-text-property 0 'button sym-name) sym-name) ;; Highlight the quoted string. (t (propertize sym-name 'face 'font-lock-constant-face))))) (defun helpful--propertize-info (docstring) "Convert info references in DOCSTRING to buttons." (replace-regexp-in-string ;; Replace all text that looks like a link to an Info page. (rx (seq (group bow (any "Ii") "nfo" (one-or-more whitespace)) (group (or "node" "anchor") (one-or-more whitespace)) (any "'`‘") (group (one-or-more (not (any "'’")))) (any "'’"))) (lambda (it) ;; info-name matches "[Ii]nfo ". ;; space matches "node " or "anchor ". ;; info-node has the form "(cl)Loop Facility". (let ((info-name (match-string 1 it)) (space (match-string 2 it)) (info-node (match-string 3 it))) ;; If the docstring doesn't specify a manual, assume the Emacs manual. (save-match-data (unless (string-match "^([^)]+)" info-node) (setq info-node (concat "(emacs)" info-node)))) (concat info-name space (helpful--button info-node 'helpful-info-button 'info-node info-node)))) docstring t t)) (defun helpful--keymap-keys (keymap) "Return all the keys and commands in KEYMAP. Flattens nested keymaps and follows remapped commands. Returns a list of pairs (KEYCODES COMMAND), where KEYCODES is a vector suitable for `key-description', and COMMAND is a smbol." (cond ;; Prefix keys. ((and (symbolp keymap) (fboundp keymap) ;; Prefix keys use a keymap in the function slot of a symbol. (keymapp (symbol-function keymap))) (helpful--keymap-keys (symbol-function keymap))) ;; Other symbols or compiled functions mean we've reached a leaf, ;; so this is a command we can call. ((or (symbolp keymap) (functionp keymap) ;; Strings or vectors mean a keyboard macro. (stringp keymap) (vectorp keymap)) `(([] ,keymap))) ((stringp (car keymap)) (helpful--keymap-keys (cdr keymap))) ;; Otherwise, recurse on the keys at this level of the keymap. (t (let (result) (dolist (item (cdr keymap)) (cond ((and (consp item) (eq (car item) 'menu-bar)) ;; Skip menu bar items. nil) ;; Sparse keymaps are lists. ((consp item) (-let [(keycode . value) item] (-each (helpful--keymap-keys value) (-lambda ((keycodes command)) (push (list (vconcat (vector keycode) keycodes) command) result))))) ;; Dense keymaps are char-tables. ((char-table-p item) (map-char-table (lambda (keycode value) (-each (helpful--keymap-keys value) (-lambda ((keycodes command)) (push (list (vconcat (vector keycode) keycodes) command) result)))) item)))) ;; For every command `new-func' mapped to a command `orig-func', show `new-func' with ;; the key sequence for `orig-func'. (setq result (-map-when (-lambda ((keycodes _)) (and (> (length keycodes) 1) (eq (elt keycodes 0) 'remap))) (-lambda ((keycodes command)) (list (where-is-internal (elt keycodes 1) global-map t) command)) result)) ;; Preserve the original order of the keymap. (nreverse result))))) (defun helpful--format-hook (hook-val) "Given a list value assigned to a hook, format it with links to functions." (let ((lines (--map (if (and (symbolp it) (fboundp it)) (helpful--button (symbol-name it) 'helpful-describe-exactly-button 'symbol it 'callable-p t) (helpful--syntax-highlight (helpful--pretty-print it))) hook-val))) (format "(%s)" (s-join "\n " lines)))) ;; TODO: unlike `substitute-command-keys', this shows keybindings ;; which are currently shadowed (e.g. a global minor mode map). (defun helpful--format-keymap (keymap) "Format KEYMAP." (let* ((keys-and-commands (helpful--keymap-keys keymap)) ;; Convert keycodes [27 i] to "C-M-i". (keys (-map #'-first-item keys-and-commands)) ;; Add padding so all our strings are the same length. (formatted-keys (-map #'key-description keys)) (max-formatted-length (-max (cons 0 (-map #'length formatted-keys)))) (aligned-keys (--map (s-pad-right (1+ max-formatted-length) " " it) formatted-keys)) ;; Format commands as buttons. (commands (-map (-lambda ((_ command)) command) keys-and-commands)) (formatted-commands (--map (cond ((symbolp it) (helpful--button (symbol-name it) 'helpful-describe-button 'symbol it)) ((or (stringp it) (vectorp it)) "Keyboard Macro") (t "#")) commands)) ;; Build lines for display. (lines (-map (-lambda ((key . command)) (format "%s %s" key command)) (-zip-pair aligned-keys formatted-commands)))) ;; The flattened keymap will have normal bindings first, and ;; inherited bindings last. Sort so that we group by prefix. (s-join "\n" (-sort #'string< lines)))) (defun helpful--format-commands (str keymap) "Replace all the \\[ references in STR with buttons." (replace-regexp-in-string ;; Text of the form \\[foo-command] (rx "\\[" (group (+ (not (in "]")))) "]") (lambda (it) (let* ((button-face (if (>= emacs-major-version 28) 'help-key-binding 'button)) (symbol-name (match-string 1 it)) (symbol (intern symbol-name)) (key (where-is-internal symbol keymap t)) (key-description (if key (key-description key) (format "M-x %s" symbol-name)))) (helpful--button key-description 'helpful-describe-exactly-button 'symbol symbol 'callable-p t 'face button-face))) str t t)) (defun helpful--chars-before (pos n) "Return up to N chars before POS in the current buffer. The string may be shorter than N or empty if out-of-range." (buffer-substring (max (point-min) (- pos n)) pos)) (defun helpful--chars-after (pos n) "Return up to N chars after POS in the current buffer. The string may be shorter than N or empty if out-of-range." (buffer-substring pos (min (point-max) (+ pos n)))) (defun helpful--format-command-keys (docstring) "Convert command key references and keymap references in DOCSTRING to buttons. Emacs uses \\= to escape \\[ references, so replace that unescaping too." ;; Loosely based on `substitute-command-keys', but converts ;; references to buttons. (let ((keymap nil)) (with-temp-buffer (insert docstring) (goto-char (point-min)) (while (not (eobp)) (cond ((looking-at ;; Text of the form "foo" (rx "\"")) ;; For literal strings, escape backslashes so our output ;; shows copy-pasteable literals. (let* ((start-pos (point)) (end-pos (progn (forward-char) (search-forward "\"" nil t))) contents) (if end-pos (progn (setq contents (buffer-substring start-pos end-pos)) (delete-region start-pos end-pos) (insert (s-replace "\\" "\\\\" contents))) (forward-char 1)))) ((looking-at ;; Text of the form \=X (rx "\\=")) ;; Remove the escaping, then step over the escaped char. ;; Step over the escaped character. (delete-region (point) (+ (point) 2)) (forward-char 1)) ((looking-at ;; Text of the form `foo' (rx "`")) (let* ((start-pos (point)) (end-pos (search-forward "'" nil t)) (contents (when end-pos (buffer-substring (1+ start-pos) (1- end-pos))))) (cond ((null contents) ;; If there's no closing ' to match the opening `, just ;; leave it. (goto-char (1+ start-pos))) ((s-contains-p "`" contents) ;; If we have repeated backticks `foo `bar', leave the ;; first one. (goto-char (1+ start-pos))) ((s-contains-p "\\[" contents) (delete-region start-pos end-pos) (insert (helpful--format-commands contents keymap))) ;; Highlight a normal `foo', extracting the surrounding ;; text so we can detect e.g. "function `foo'". (t (let ((before (helpful--chars-before start-pos 10)) (after (helpful--chars-after end-pos 10))) (delete-region start-pos end-pos) (insert (helpful--propertize-sym-ref contents before after))))))) ((looking-at ;; Text of the form \\ (rx "\\<" (group (+ (not (in ">")))) ">" (? "\n"))) (let* ((symbol-with-parens (match-string 0)) (symbol-name (match-string 1))) ;; Remove the original string. (delete-region (point) (+ (point) (length symbol-with-parens))) ;; Set the new keymap. (setq keymap (symbol-value (intern symbol-name))))) ((looking-at ;; Text of the form \\{foo-mode-map} (rx "\\{" (group (+ (not (in "}")))) "}")) (let* ((symbol-with-parens (match-string 0)) (symbol-name (match-string 1)) (keymap ;; Gracefully handle variables not being defined. (ignore-errors (symbol-value (intern symbol-name))))) ;; Remove the original string. (delete-region (point) (+ (point) (length symbol-with-parens))) (if keymap (insert (helpful--format-keymap keymap)) (insert (format "Keymap %s is not currently defined." symbol-name))))) ((looking-at ;; Text of the form \\[foo-command] (rx "\\[" (group (+ (not (in "]")))) "]")) (let* ((symbol-with-parens (match-string 0))) ;; Remove the original string. (delete-region (point) (+ (point) (length symbol-with-parens))) ;; Add a button. (insert (helpful--format-commands symbol-with-parens keymap)))) ;; Don't modify other characters. (t (forward-char 1)))) (buffer-string)))) ;; TODO: fix upstream Emacs bug that means `-map' is not highlighted ;; in the docstring for `--map'. (defun helpful--format-docstring (docstring) "Replace cross-references with links in DOCSTRING." (-> docstring (helpful--split-first-line) (helpful--propertize-info) (helpful--propertize-links) (helpful--propertize-bare-links) (helpful--format-command-keys) (s-trim))) (define-button-type 'helpful-link-button 'action #'helpful--follow-link 'follow-link t 'help-echo "Follow this link") (defun helpful--propertize-links (docstring) "Convert URL links in docstrings to buttons." (replace-regexp-in-string (rx "URL `" (group (*? any)) "'") (lambda (match) (let ((url (match-string 1 match))) (concat "URL " (helpful--button url 'helpful-link-button 'url url)))) docstring)) (defun helpful--propertize-bare-links (docstring) "Convert URL links in docstrings to buttons." (replace-regexp-in-string (rx (group (or string-start space "<")) (group "http" (? "s") "://" (+? (not (any space)))) (group (? (any "." ">" ")")) (or space string-end ">"))) (lambda (match) (let ((space-before (match-string 1 match)) (url (match-string 2 match)) (after (match-string 3 match))) (concat space-before (helpful--button url 'helpful-link-button 'url url) after))) docstring)) (defun helpful--follow-link (button) "Follow the URL specified by BUTTON." (browse-url (button-get button 'url))) (defconst helpful--highlighting-funcs '(ert--activate-font-lock-keywords highlight-quoted-mode rainbow-delimiters-mode) "Highlighting functions that are safe to run in a temporary buffer. This is used in `helpful--syntax-highlight' to support extra highlighting that the user may have configured in their mode hooks.") ;; TODO: crashes on `backtrace-frame' on a recent checkout. (defun helpful--syntax-highlight (source &optional mode) "Return a propertized version of SOURCE in MODE." (unless mode (setq mode #'emacs-lisp-mode)) (if (or (< (length source) helpful-max-highlight) (eq mode 'emacs-lisp-mode)) (with-temp-buffer (insert source) ;; Switch to major-mode MODE, but don't run any hooks. (delay-mode-hooks (funcall mode)) ;; `delayed-mode-hooks' contains mode hooks like ;; `emacs-lisp-mode-hook'. Build a list of functions that are run ;; when the mode hooks run. (let (hook-funcs) (dolist (hook delayed-mode-hooks) (let ((funcs (symbol-value hook))) (setq hook-funcs (append hook-funcs funcs)))) ;; Filter hooks to those that relate to highlighting, and run them. (setq hook-funcs (-intersection hook-funcs helpful--highlighting-funcs)) (-map #'funcall hook-funcs)) (if (fboundp 'font-lock-ensure) (font-lock-ensure) (with-no-warnings (font-lock-fontify-buffer))) (buffer-string)) ;; SOURCE was too long to highlight in a reasonable amount of ;; time. (concat (propertize "// Skipping highlighting due to " 'face 'font-lock-comment-face) (helpful--button "helpful-max-highlight" 'helpful-describe-exactly-button 'symbol 'helpful-max-highlight 'callable-p nil) (propertize ".\n" 'face 'font-lock-comment-face) source))) (defun helpful--source (sym callable-p buf pos) "Return the source code of SYM. If the source code cannot be found, return the sexp used." (catch 'source (unless (symbolp sym) (throw 'source sym)) (let ((source nil)) (when (and buf pos) (with-current-buffer buf (save-excursion (save-restriction (goto-char pos) (if (and (helpful--primitive-p sym callable-p) (not callable-p)) ;; For variables defined in .c files, only show the ;; DEFVAR expression rather than the huge containing ;; function. (progn (setq pos (line-beginning-position)) (forward-list) (forward-char) (narrow-to-region pos (point))) ;; Narrow to the top-level definition. (let ((parse-sexp-ignore-comments t)) (narrow-to-defun t))) ;; If there was a preceding comment, POS will be ;; after that comment. Move the position to include that comment. (setq pos (point-min)) (setq source (buffer-substring-no-properties (point-min) (point-max)))))) (setq source (s-trim-right source)) (when (and source (buffer-file-name buf)) (setq source (propertize source 'helpful-path (buffer-file-name buf) 'helpful-pos pos 'helpful-pos-is-start t))) (throw 'source source))) (when callable-p ;; Could not find source -- probably defined interactively, or via ;; a macro, or file has changed. ;; TODO: verify that the source hasn't changed before showing. ;; TODO: offer to download C sources for current version. (throw 'source (indirect-function sym))))) (defun helpful--has-shortdoc-p (sym) "Return non-nil if shortdoc.el is available and SYM is in a shortdoc group." (and (featurep 'shortdoc) (shortdoc-function-groups sym))) (defun helpful--in-manual-p (sym) "Return non-nil if SYM is in an Info manual." (let ((completions (cl-letf (((symbol-function #'message) (lambda (_format-string &rest _args)))) (info-lookup->completions 'symbol 'emacs-lisp-mode)))) (-when-let (buf (get-buffer " temp-info-look")) (kill-buffer buf)) (or (assoc sym completions) (assoc-string sym completions)))) (defun helpful--version-info (sym) "If SYM has version information, format and return it. Return nil otherwise." (when (symbolp sym) (let ((package-version (get sym 'custom-package-version)) (emacs-version (get sym 'custom-version))) (cond (package-version (format "This variable was added, or its default value changed, in %s version %s." (car package-version) (cdr package-version))) (emacs-version (format "This variable was added, or its default value changed, in Emacs %s." emacs-version)))))) (defun helpful--library-path (library-name) "Find the absolute path for the source of LIBRARY-NAME. LIBRARY-NAME takes the form \"foo.el\" , \"foo.el\" or \"src/foo.c\". If .elc files exist without the corresponding .el, return nil." (when (member (f-ext library-name) '("c" "rs")) (setq library-name (f-expand library-name (f-parent find-function-C-source-directory)))) (condition-case nil (find-library-name library-name) (error nil))) (defun helpful--macroexpand-try (form) "Try to fully macroexpand FORM. If it fails, attempt to partially macroexpand FORM." (catch 'result (ignore-errors ;; Happy path: we can fully expand the form. (throw 'result (macroexpand-all form))) (ignore-errors ;; Attempt one level of macroexpansion. (throw 'result (macroexpand-1 form))) ;; Fallback: just return the original form. form)) (defun helpful--tree-any-p (pred tree) "Walk TREE, applying PRED to every subtree. Return t if PRED ever returns t." (catch 'found (let ((stack (list tree))) (while stack (let ((next (pop stack))) (cond ((funcall pred next) (throw 'found t)) ((consp next) (push (car next) stack) (push (cdr next) stack)))))) nil)) (defun helpful--find-by-macroexpanding (buf sym callable-p) "Search BUF for the definition of SYM by macroexpanding interesting forms in BUF." (catch 'found (with-current-buffer buf (save-excursion (goto-char (point-min)) (condition-case nil (while t (let ((form (read (current-buffer))) (var-def-p (lambda (sexp) (and (eq (car-safe sexp) 'defvar) (eq (car-safe (cdr sexp)) sym)))) (fn-def-p (lambda (sexp) ;; `defun' ultimately expands to `defalias'. (and (eq (car-safe sexp) 'defalias) (equal (car-safe (cdr sexp)) `(quote ,sym)))))) (setq form (helpful--macroexpand-try form)) (when (helpful--tree-any-p (if callable-p fn-def-p var-def-p) form) ;; `read' puts point at the end of the form, so go ;; back to the start. (throw 'found (scan-sexps (point) -1))))) (end-of-file nil)))))) (defun helpful--open-if-needed (path) "Return a list (BUF OPENED) where BUF is a buffer visiting PATH. If a buffer already exists, return that. If not, open PATH with the `emacs-lisp-mode' syntax table active but skip any hooks." (let ((initial-buffers (buffer-list)) (buf nil) (opened nil) ;; Skip running hooks that may prompt the user. (find-file-hook nil) ;; If we end up opening a buffer, don't bother with file ;; variables. It prompts the user, and we discard the buffer ;; afterwards anyway. (enable-local-variables nil)) ;; Opening large .c files can be slow (e.g. when looking at ;; `defalias'), especially if the user has configured mode hooks. ;; ;; Bind `auto-mode-alist' to nil, so we open the buffer in ;; `fundamental-mode' if it isn't already open. (let ((auto-mode-alist nil)) (setq buf (find-file-noselect path))) (unless (-contains-p initial-buffers buf) (setq opened t) (let ((syntax-table emacs-lisp-mode-syntax-table)) (when (s-ends-with-p ".c" path) (setq syntax-table (make-syntax-table)) (c-populate-syntax-table syntax-table)) ;; If it's a freshly opened buffer, we need to set the syntax ;; table so we can search correctly. (with-current-buffer buf (set-syntax-table syntax-table)))) (list buf opened))) (defun helpful--definition (sym callable-p) "Return a list (BUF POS OPENED) where SYM is defined. BUF is the buffer containing the definition. If the user wasn't already visiting this buffer, OPENED is t and callers should kill the buffer when done. POS is the position of the start of the definition within the buffer." (let ((primitive-p (helpful--primitive-p sym callable-p)) (library-name nil) (src-path nil) (buf nil) (pos nil) (opened nil)) ;; We shouldn't be called on primitive functions if we don't have ;; a directory of Emacs C sourcecode. (cl-assert (or find-function-C-source-directory (not primitive-p))) (when (symbolp sym) (if callable-p (setq library-name (cdr (find-function-library sym))) ;; Based on `find-variable-noselect'. (setq library-name (or (symbol-file sym 'defvar) (help-C-file-name sym 'var))))) (when library-name (setq src-path (helpful--library-path library-name))) (cond ((and (not (symbolp sym)) (functionp sym)) (list nil nil nil)) ((and callable-p library-name) (when src-path (-let [(src-buf src-opened) (helpful--open-if-needed src-path)] (setq buf src-buf) (setq opened src-opened)) ;; Based on `find-function-noselect'. (with-current-buffer buf ;; `find-function-search-for-symbol' moves point. Prevent ;; that. (save-excursion ;; Narrowing has been fixed upstream: ;; http://git.savannah.gnu.org/cgit/emacs.git/commit/?id=abd18254aec76b26e86ae27e91d2c916ec20cc46 (save-restriction (widen) (setq pos (cdr (find-function-search-for-symbol sym nil library-name)))))) ;; If we found the containing buffer, but not the symbol, attempt ;; to find it by macroexpanding interesting forms. (when (and buf (not pos)) (setq pos (helpful--find-by-macroexpanding buf sym t))))) ;; A function, but no file found. (callable-p ;; Functions defined interactively may have an edebug property ;; that contains the location of the definition. (-when-let (edebug-info (get sym 'edebug)) (-let [marker (if (consp edebug-info) (car edebug-info) edebug-info)] (setq buf (marker-buffer marker)) (setq pos (marker-position marker))))) ((and (not callable-p) src-path) (-let [(src-buf src-opened) (helpful--open-if-needed src-path)] (setq buf src-buf) (setq opened src-opened) (with-current-buffer buf ;; `find-function-search-for-symbol' moves point. Prevent ;; that. (save-excursion (condition-case _err (setq pos (cdr (find-variable-noselect sym 'defvar))) (search-failed nil) ;; If your current Emacs instance doesn't match the source ;; code configured in find-function-C-source-directory, we can ;; get an error about not finding source. Try ;; `default-tab-width' against Emacs trunk. (error nil))))))) (list buf pos opened))) (defun helpful--reference-positions (sym callable-p buf) "Return all the buffer positions of references to SYM in BUF." (-let* ((forms-and-bufs (elisp-refs--search-1 (list buf) (lambda (buf) (elisp-refs--read-and-find buf sym (if callable-p #'elisp-refs--function-p #'elisp-refs--variable-p))))) ;; Since we only searched one buffer, we know that ;; forms-and-bufs has only one item. (forms-and-buf (-first-item forms-and-bufs)) ((forms . _buf) forms-and-buf)) (-map (-lambda ((_code start-pos _end-pos)) start-pos) forms))) (defun helpful--all-keymap-syms () "Return all keymaps defined in this Emacs instance." (let (keymaps) (mapatoms (lambda (sym) (when (and (boundp sym) (keymapp (symbol-value sym))) (push sym keymaps)))) keymaps)) (defun helpful--key-sequences (command-sym keymap global-keycodes) "Return all the key sequences of COMMAND-SYM in KEYMAP." (let* ((keycodes ;; Look up this command in the keymap, its parent and the ;; global map. We need to include the global map to find ;; remapped commands. (where-is-internal command-sym keymap nil t)) ;; Look up this command in the parent keymap. (parent-keymap (keymap-parent keymap)) (parent-keycodes (when parent-keymap (where-is-internal command-sym (list parent-keymap) nil t))) ;; Look up this command in the global map. (global-keycodes (unless (eq keymap global-map) global-keycodes))) (->> keycodes ;; Ignore keybindings from the parent or global map. (--remove (or (-contains-p global-keycodes it) (-contains-p parent-keycodes it))) ;; Convert raw keycode vectors into human-readable strings. (-map #'key-description)))) (defun helpful--keymaps-containing (command-sym) "Return a list of pairs listing keymap names that contain COMMAND-SYM, along with the keybindings in each keymap. Keymap names are typically variable names, but may also be descriptions of values in `minor-mode-map-alist'. We ignore keybindings that are menu items, and ignore keybindings from parent keymaps. `widget-global-map' is also ignored as it generally contains the same bindings as `global-map'." (let* ((keymap-syms (helpful--all-keymap-syms)) (keymap-sym-vals (-map #'symbol-value keymap-syms)) (global-keycodes (where-is-internal command-sym (list global-map) nil t)) matching-keymaps) ;; Look for this command in all keymaps bound to variables. (-map (-lambda ((keymap-sym . keymap)) (let ((key-sequences (helpful--key-sequences command-sym keymap global-keycodes))) (when (and key-sequences (not (eq keymap-sym 'widget-global-map))) (push (cons (symbol-name keymap-sym) key-sequences) matching-keymaps)))) (-zip-pair keymap-syms keymap-sym-vals)) ;; Look for this command in keymaps used by minor modes that ;; aren't bound to variables. (-map (-lambda ((minor-mode . keymap)) ;; Only consider this keymap if we didn't find it bound to a variable. (when (and (keymapp keymap) (not (memq keymap keymap-sym-vals))) (let ((key-sequences (helpful--key-sequences command-sym keymap global-keycodes))) (when key-sequences (push (cons (format "minor-mode-map-alist (%s)" minor-mode) key-sequences) matching-keymaps))))) ;; TODO: examine `minor-mode-overriding-map-alist' too. minor-mode-map-alist) matching-keymaps)) (defun helpful--merge-alists (l1 l2) "Given two alists mapping symbols to lists, return a single alist with the lists concatenated." (let* ((l1-keys (-map #'-first-item l1)) (l2-keys (-map #'-first-item l2)) (l2-extra-keys (-difference l2-keys l1-keys)) (l2-extra-values (--map (assoc it l2) l2-extra-keys)) (l1-with-values (-map (-lambda ((key . values)) (cons key (append values (cdr (assoc key l2))))) l1))) (append l1-with-values l2-extra-values))) (defun helpful--keymaps-containing-aliases (command-sym aliases) "Return a list of pairs mapping keymap symbols to the keybindings for COMMAND-SYM in each keymap. Includes keybindings for aliases, unlike `helpful--keymaps-containing'." (let* ((syms (cons command-sym aliases)) (syms-keymaps (-map #'helpful--keymaps-containing syms))) (-reduce #'helpful--merge-alists syms-keymaps))) (defun helpful--format-keys (command-sym aliases) "Describe all the keys that call COMMAND-SYM." (let (mode-lines global-lines) (--each (helpful--keymaps-containing-aliases command-sym aliases) (-let [(map . keys) it] (dolist (key keys) (push (format "%s %s" (propertize map 'face 'font-lock-variable-name-face) (if (>= emacs-major-version 28) (propertize key 'face 'help-key-binding) key)) (if (eq map 'global-map) global-lines mode-lines))))) (setq global-lines (-sort #'string< global-lines)) (setq mode-lines (-sort #'string< mode-lines)) (-let [lines (-concat global-lines mode-lines)] (if lines (s-join "\n" lines) "This command is not in any keymaps.")))) (defun helpful--outer-sexp (buf pos) "Find position POS in BUF, and return the name of the outer sexp, along with its position. Moves point in BUF." (with-current-buffer buf (goto-char pos) (let* ((ppss (syntax-ppss)) (outer-sexp-posns (nth 9 ppss))) (when outer-sexp-posns (goto-char (car outer-sexp-posns)))) (list (point) (-take 2 (read buf))))) (defun helpful--count-values (items) "Return an alist of the count of each value in ITEMS. E.g. (x x y z y) -> ((x . 2) (y . 2) (z . 1))" (let (counts) (dolist (item items (nreverse counts)) (-if-let (item-and-count (assoc item counts)) (setcdr item-and-count (1+ (cdr item-and-count))) (push (cons item 1) counts))))) (defun helpful--without-advice (sym) "Given advised function SYM, return the function object without the advice. Assumes function has been loaded." (advice--cd*r (advice--symbol-function sym))) (defun helpful--advised-p (sym) "Does SYM have advice associated with it?" (and (symbolp sym) (advice--p (advice--symbol-function sym)))) (defun helpful--format-head (head) "Given a 'head' (the first two symbols of a sexp) format and syntax highlight it." (-let* (((def name) head) (formatted-name (if (and (consp name) (eq (car name) 'quote)) (format "'%S" (cadr name)) (format "%S" name))) (formatted-def (format "(%s %s ...)" def formatted-name)) ) (helpful--syntax-highlight formatted-def))) (defun helpful--format-reference (head longest-head ref-count position path) "Return a syntax-highlighted version of HEAD, with a link to its source location." (let ((formatted-count (format "%d reference%s" ref-count (if (> ref-count 1) "s" "")))) (propertize (format "%s %s" (s-pad-right longest-head " " (helpful--format-head head)) (propertize formatted-count 'face 'font-lock-comment-face)) 'helpful-path path 'helpful-pos position))) (defun helpful--format-position-heads (position-heads path) "Given a list of outer sexps, format them for display. POSITION-HEADS takes the form ((123 (defun foo)) (456 (defun bar)))." (let ((longest-head (->> position-heads (-map (-lambda ((_pos head)) (helpful--format-head head))) (-map #'length) (-max)))) (->> (helpful--count-values position-heads) (-map (-lambda (((pos head) . count)) (helpful--format-reference head longest-head count pos path))) (s-join "\n")))) (defun helpful--primitive-p (sym callable-p) "Return t if SYM is defined in C." (let ((subrp (if (fboundp 'subr-primitive-p) #'subr-primitive-p #'subrp))) (cond ((and callable-p (helpful--advised-p sym)) (funcall subrp (helpful--without-advice sym))) (callable-p (funcall subrp (indirect-function sym))) (t (let ((filename (find-lisp-object-file-name sym 'defvar))) (or (eq filename 'C-source) (and (stringp filename) (let ((ext (file-name-extension filename))) (or (equal ext "c") (equal ext "rs")))))))))) (defun helpful--sym-value (sym buf) "Return the value of SYM in BUF." (cond ;; If we're given a buffer, look up the variable in that buffer. (buf (with-current-buffer buf (symbol-value sym))) ;; If we don't have a buffer, and this is a buffer-local variable, ;; ensure we return the default value. ((local-variable-if-set-p sym) (default-value sym)) ;; Otherwise, just return the value in the current buffer, which is ;; the global value. (t (symbol-value sym)))) (defun helpful--insert-section-break () "Insert section break into helpful buffer." (insert "\n\n")) (defun helpful--insert-implementations () "When `helpful--sym' is a generic method, insert its implementations." (let ((func helpful--sym) (content)) (when (fboundp #'cl--generic-describe) (with-temp-buffer (declare-function cl--generic-describe "cl-generic" (function)) (cl--generic-describe func) (goto-char (point-min)) (when (re-search-forward "^Implementations:$" nil t) (setq content (buffer-substring (point) (point-max))))) (when content (helpful--insert-section-break) (insert (helpful--heading "Implementations") (s-trim content)))))) (defun helpful--calculate-references (sym callable-p source-path) "Calculate references for SYM in SOURCE-PATH." (when source-path (let* ((primitive-p (helpful--primitive-p sym callable-p)) (buf (elisp-refs--contents-buffer source-path)) (positions (if primitive-p nil (helpful--reference-positions helpful--sym helpful--callable-p buf))) (return-value (--map (helpful--outer-sexp buf it) positions))) (kill-buffer buf) return-value))) (defun helpful--make-shortdoc-sentence (sym) "Make a line for shortdoc groups of SYM." (when (featurep 'shortdoc) (-when-let (groups (--map (helpful--button (symbol-name it) 'helpful-shortdoc-button 'shortdoc-group it) (shortdoc-function-groups sym))) (if (= 1 (length groups)) (format "Other relevant functions are documented in the %s group." (car groups)) (format "Other relevant functions are documented in the %s groups." (concat (s-join ", " (butlast groups)) " and " (car (last groups)))))))) (defun helpful--make-manual-button (sym) "Make manual button for SYM." (helpful--button "View in manual" 'helpful-manual-button 'symbol sym)) (defun helpful--make-toggle-button (sym buffer) "Make toggle button for SYM in BUFFER." (helpful--button "Toggle" 'helpful-toggle-button 'symbol sym 'buffer buffer)) (defun helpful--make-set-button (sym buffer) "Make set button for SYM in BUFFER." (helpful--button "Set" 'helpful-set-button 'symbol sym 'buffer buffer)) (defun helpful--make-toggle-literal-button () "Make set button for SYM in BUFFER." (helpful--button (if helpful--view-literal ;; TODO: only offer for strings that have newlines, tabs or ;; properties. "Pretty view" "View as literal") 'helpful-view-literal-button)) (defun helpful--make-customize-button (sym) "Make customize button for SYM." (helpful--button "Customize" 'helpful-customize-button 'symbol sym)) (defun helpful--make-references-button (sym callable-p) "Make references button for SYM." (helpful--button "Find all references" 'helpful-all-references-button 'symbol sym 'callable-p callable-p)) (defun helpful--make-edebug-button (sym) "Make edebug button for SYM." (helpful--button (format "%s edebug" (if (helpful--edebug-p sym) "Disable" "Enable")) 'helpful-edebug-button 'symbol sym)) (defun helpful--make-tracing-button (sym) "Make tracing button for SYM." (helpful--button (format "%s tracing" (if (trace-is-traced sym) "Disable" "Enable")) 'helpful-trace-button 'symbol sym)) (defun helpful--make-disassemble-button (obj) "Make disassemble button for OBJ. OBJ may be a symbol or a compiled function object." (helpful--button "Disassemble" 'helpful-disassemble-button 'object obj)) (defun helpful--make-run-test-button (sym) "Make an ERT test button for SYM." (helpful--button "Run test" 'helpful-run-test-button 'symbol sym)) (defun helpful--make-forget-button (sym callable-p) "Make forget button for SYM." (helpful--button "Forget" 'helpful-forget-button 'symbol sym 'callable-p callable-p)) (defun helpful--make-callees-button (sym source) (helpful--button (format "Functions used by %s" sym) 'helpful-callees-button 'symbol sym 'source source)) ;; TODO: this only reports if a function is autoloaded because we ;; autoloaded it. This ignores newly defined functions that are ;; autoloaded. Built-in help has this limitation too, but if we can ;; find the source, we should instead see if there's an autoload ;; cookie. (defun helpful--autoloaded-p (sym buf) "Return non-nil if function SYM is autoloaded." (-when-let (file-name (buffer-file-name buf)) (setq file-name (s-chop-suffix ".gz" file-name)) (condition-case nil (help-fns--autoloaded-p sym file-name) ; new in Emacs 29.0.50 ; see https://github.com/Wilfred/helpful/pull/283 (error (help-fns--autoloaded-p sym))))) (defun helpful--compiled-p (sym) "Return non-nil if function SYM is byte-compiled" (and (symbolp sym) (byte-code-function-p (symbol-function sym)))) (defun helpful--native-compiled-p (sym) "Return non-nil if function SYM is native-compiled" (and (symbolp sym) (fboundp 'subr-native-elisp-p) (subr-native-elisp-p (symbol-function sym)))) (defun helpful--join-and (items) "Join a list of strings with commas and \"and\"." (cond ((= (length items) 0) "") ((= (length items) 1) (car items)) (t (format "%s and %s" (s-join ", " (-drop-last 1 items)) (-last-item items))))) (defun helpful--summary (sym callable-p buf pos) "Return a one sentence summary for SYM." (-let* ((primitive-p (helpful--primitive-p sym callable-p)) (canonical-sym (helpful--canonical-symbol sym callable-p)) (alias-p (not (eq canonical-sym sym))) (alias-button (if callable-p ;; Show a link to 'defalias' in the manual. (helpful--button "function alias" 'helpful-manual-button 'symbol 'defalias) ;; Show a link to the variable aliases section in the ;; manual. (helpful--button "alias" 'helpful-info-button 'info-node "(elisp)Variable Aliases"))) (special-form-button (helpful--button "special form" 'helpful-info-button 'info-node "(elisp)Special Forms")) (keyboard-macro-button (helpful--button "keyboard macro" 'helpful-info-button 'info-node "(elisp)Keyboard Macros")) (interactive-button (helpful--button "interactive" 'helpful-info-button 'info-node "(elisp)Using Interactive")) (autoload-button (helpful--button "autoloaded" 'helpful-info-button 'info-node "(elisp)Autoload")) (compiled-button (helpful--button "byte-compiled" 'helpful-info-button 'info-node "(elisp)Byte Compilation")) (native-compiled-button (helpful--button "natively compiled" 'helpful-describe-button 'symbol 'native-compile)) (buffer-local-button (helpful--button "buffer-local" 'helpful-info-button 'info-node "(elisp)Buffer-Local Variables")) (autoloaded-p (and callable-p buf (helpful--autoloaded-p sym buf))) (compiled-p (and callable-p (helpful--compiled-p sym))) (native-compiled-p (and callable-p (helpful--native-compiled-p sym))) (buttons (list (if alias-p alias-button) (if (and callable-p autoloaded-p) autoload-button) (if (and callable-p (commandp sym)) interactive-button) (if compiled-p compiled-button) (if native-compiled-p native-compiled-button) (if (and (not callable-p) (local-variable-if-set-p sym)) buffer-local-button))) (description (helpful--join-and (-non-nil buttons))) (kind (cond ((special-form-p sym) special-form-button) (alias-p (format "for %s," (helpful--button (symbol-name canonical-sym) 'helpful-describe-exactly-button 'symbol canonical-sym 'callable-p callable-p))) ((not callable-p) "variable") ((macrop sym) "macro") ((helpful--kbd-macro-p sym) keyboard-macro-button) (t "function"))) (defined (cond (buf (let ((path (buffer-file-name buf))) (if path (format "defined in %s" (helpful--navigate-button (file-name-nondirectory path) path pos)) (format "defined in buffer %s" (helpful--buffer-button buf pos))))) (primitive-p "defined in C source code") ((helpful--kbd-macro-p sym) nil) (t "without a source file")))) (s-word-wrap 70 (format "%s is %s %s %s%s." (if (symbolp sym) (helpful--format-symbol sym) "This lambda") (if (string-match-p (rx bos (or "a" "e" "i" "o" "u")) description) "an" "a") description kind (if defined (concat " " defined) ""))))) (defun helpful--callees (form) "Given source code FORM, return a list of all the functions called." (let* ((expanded-form (macroexpand-all form)) ;; Find all the functions called after macro expansion. (all-fns (helpful--callees-1 expanded-form)) ;; Only consider the functions that were in the original code ;; before macro expansion. (form-syms (-filter #'symbolp (-flatten form))) (form-fns (--filter (memq it form-syms) all-fns))) (-distinct form-fns))) (defun helpful--callees-1 (form) "Return a list of all the functions called in FORM. Assumes FORM has been macro expanded. The returned list may contain duplicates." (cond ((not (consp form)) nil) ;; See `(elisp)Special Forms'. For these special forms, we recurse ;; just like functions but ignore the car. ((memq (car form) '(and catch defconst defvar if interactive or prog1 prog2 progn save-current-buffer save-restriction setq setq-default track-mouse unwind-protect while)) (-flatten (-map #'helpful--callees-1 (cdr form)))) ((eq (car form) 'cond) (let* ((clauses (cdr form)) (clause-fns ;; Each clause is a list of forms. (--map (-map #'helpful--callees-1 it) clauses))) (-flatten clause-fns))) ((eq (car form) 'condition-case) (let* ((protected-form (nth 2 form)) (protected-form-fns (helpful--callees-1 protected-form)) (handlers (-drop 3 form)) (handler-bodies (-map #'cdr handlers)) (handler-fns (--map (-map #'helpful--callees-1 it) handler-bodies))) (append protected-form-fns (-flatten handler-fns)))) ;; Calling a function with a well known higher order function, for ;; example (funcall 'foo 1 2). ((and (memq (car form) '(funcall apply call-interactively mapcar mapc mapconcat -map)) (eq (car-safe (nth 1 form)) 'quote)) (cons (cadr (nth 1 form)) (-flatten (-map #'helpful--callees-1 (cdr form))))) ((eq (car form) 'function) (let ((arg (nth 1 form))) (if (symbolp arg) ;; #'foo, which is the same as (function foo), is a function ;; reference. (list arg) ;; Handle (function (lambda ...)). (helpful--callees-1 arg)))) ((eq (car form) 'lambda) ;; Only consider the body, not the param list. (-flatten (-map #'helpful--callees-1 (-drop 2 form)))) ((eq (car form) 'closure) ;; Same as lambda, but has an additional argument of the ;; closed-over variables. (-flatten (-map #'helpful--callees-1 (-drop 3 form)))) ((memq (car form) '(let let*)) ;; Extract function calls used to set the let-bound variables. (let* ((var-vals (-second-item form)) (var-val-callees (--map (if (consp it) (-map #'helpful--callees-1 it) nil) var-vals))) (append (-flatten var-val-callees) ;; Function calls in the let body. (-map #'helpful--callees-1 (-drop 2 form))))) ((eq (car form) 'quote) nil) (t (cons (car form) (-flatten (-map #'helpful--callees-1 (cdr form))))))) (defun helpful--ensure-loaded () "Ensure the symbol associated with the current buffer has been loaded." (when (and helpful--callable-p (symbolp helpful--sym)) (let ((fn-obj (symbol-function helpful--sym))) (when (autoloadp fn-obj) (autoload-do-load fn-obj))))) (defun helpful--hook-p (symbol value) "Does SYMBOL look like a hook?" (and (or (s-ends-with-p "-hook" (symbol-name symbol)) ;; E.g. `after-change-functions', which can be used with ;; `add-hook'. (s-ends-with-p "-functions" (symbol-name symbol))) (consp value))) (defun helpful--format-value (sym value) "Format VALUE as a string." (cond (helpful--view-literal (helpful--syntax-highlight (helpful--pretty-print value))) ;; Allow strings to be viewed with properties rendered in ;; Emacs, rather than as a literal. ((stringp value) value) ;; Allow keymaps to be viewed with keybindings shown and ;; links to the commands bound. ((keymapp value) (helpful--format-keymap value)) ((helpful--hook-p sym value) (helpful--format-hook value)) (t (helpful--pretty-print value)))) (defun helpful--original-value (sym) "Return the original value for SYM, if any. If SYM has an original value, return it in a list. Return nil otherwise." (let* ((orig-val-expr (get sym 'standard-value))) (when (consp orig-val-expr) (ignore-errors (list (eval (car orig-val-expr))))))) (defun helpful--original-value-differs-p (sym) "Return t if SYM has an original value, and its current value is different." (let ((orig-val-list (helpful--original-value sym))) (and (consp orig-val-list) (not (eq (car orig-val-list) (symbol-value sym)))))) (defun helpful-update () "Update the current *Helpful* buffer to the latest state of the current symbol." (interactive) (cl-assert (not (null helpful--sym))) (unless (buffer-live-p helpful--associated-buffer) (setq helpful--associated-buffer nil)) (helpful--ensure-loaded) (-let* ((val ;; Look at the value before setting `inhibit-read-only', so ;; users can see the correct value of that variable. (unless helpful--callable-p (helpful--sym-value helpful--sym helpful--associated-buffer))) (inhibit-read-only t) (start-line (line-number-at-pos)) (start-column (current-column)) (primitive-p (helpful--primitive-p helpful--sym helpful--callable-p)) (canonical-sym (helpful--canonical-symbol helpful--sym helpful--callable-p)) (look-for-src (or (not primitive-p) find-function-C-source-directory)) ((buf pos opened) (if look-for-src (helpful--definition helpful--sym helpful--callable-p) '(nil nil nil))) (source (when look-for-src (helpful--source helpful--sym helpful--callable-p buf pos))) (source-path (when buf (buffer-file-name buf))) (references (helpful--calculate-references helpful--sym helpful--callable-p source-path)) (aliases (helpful--aliases helpful--sym helpful--callable-p))) (erase-buffer) (insert (helpful--summary helpful--sym helpful--callable-p buf pos)) (when (helpful--obsolete-info helpful--sym helpful--callable-p) (insert "\n\n" (helpful--format-obsolete-info helpful--sym helpful--callable-p))) (when (and helpful--callable-p (not (helpful--kbd-macro-p helpful--sym))) (helpful--insert-section-break) (insert (helpful--heading "Signature") (helpful--syntax-highlight (helpful--signature helpful--sym)))) (when (not helpful--callable-p) (helpful--insert-section-break) (let* ((sym helpful--sym) (multiple-views-p (or (stringp val) (keymapp val) (helpful--hook-p sym val)))) (when helpful--first-display (if (stringp val) ;; For strings, it's more intuitive to display them as ;; literals, so "1" and 1 are distinct. (setq helpful--view-literal t) ;; For everything else, prefer the pretty view if available. (setq helpful--view-literal nil))) (insert (helpful--heading (cond ;; Buffer-local variable and we're looking at the value in ;; a specific buffer. ((and helpful--associated-buffer (local-variable-p sym helpful--associated-buffer)) (format "Value in %s" (helpful--button (format "#" (buffer-name helpful--associated-buffer)) 'helpful-buffer-button 'buffer helpful--associated-buffer 'position pos))) ;; Buffer-local variable but default/global value. ((local-variable-if-set-p sym) "Global Value") ;; This variable is not buffer-local. (t "Value"))) (helpful--format-value sym val) "\n\n") (when (helpful--original-value-differs-p sym) (insert (helpful--heading "Original Value") (helpful--format-value sym (car (helpful--original-value sym))) "\n\n")) (when multiple-views-p (insert (helpful--make-toggle-literal-button) " ")) (when (local-variable-if-set-p sym) (insert (helpful--button "Buffer values" 'helpful-associated-buffer-button 'symbol sym 'prompt-p t) " " (helpful--button "Global value" 'helpful-associated-buffer-button 'symbol sym 'prompt-p nil) " ")) (when (memq (helpful--sym-value helpful--sym helpful--associated-buffer) '(nil t)) (insert (helpful--make-toggle-button helpful--sym helpful--associated-buffer) " ")) (insert (helpful--make-set-button helpful--sym helpful--associated-buffer)) (when (custom-variable-p helpful--sym) (insert " " (helpful--make-customize-button helpful--sym))))) (let ((docstring (helpful--docstring helpful--sym helpful--callable-p)) (version-info (unless helpful--callable-p (helpful--version-info helpful--sym)))) (when (or docstring version-info) (helpful--insert-section-break) (insert (helpful--heading "Documentation")) (when docstring (insert (helpful--format-docstring docstring))) (when version-info (insert "\n\n" (s-word-wrap 70 version-info))) (when (and (symbolp helpful--sym) helpful--callable-p (helpful--has-shortdoc-p helpful--sym)) (insert "\n\n") (insert (helpful--make-shortdoc-sentence helpful--sym))) (when (and (symbolp helpful--sym) (helpful--in-manual-p helpful--sym)) (insert "\n\n") (insert (helpful--make-manual-button helpful--sym))))) ;; Show keybindings. ;; TODO: allow users to conveniently add and remove keybindings. (when (commandp helpful--sym) (helpful--insert-section-break) (insert (helpful--heading "Key Bindings") (helpful--format-keys helpful--sym aliases))) (helpful--insert-section-break) (insert (helpful--heading "References") (let ((src-button (when source-path (helpful--navigate-button (file-name-nondirectory source-path) source-path (or pos 0))))) (cond ((and source-path references) (format "References in %s:\n%s" src-button (helpful--format-position-heads references source-path))) ((and source-path primitive-p) (format "Finding references in a .%s file is not supported." (f-ext source-path))) (source-path (format "%s is unused in %s." helpful--sym src-button)) ((and primitive-p (null find-function-C-source-directory)) "C code is not yet loaded.") (t "Could not find source file."))) "\n\n" (helpful--make-references-button helpful--sym helpful--callable-p)) (when (and helpful--callable-p (symbolp helpful--sym) source (not primitive-p)) (insert " " (helpful--make-callees-button helpful--sym source))) (when (helpful--advised-p helpful--sym) (helpful--insert-section-break) (insert (helpful--heading "Advice") (format "This %s is advised." (if (macrop helpful--sym) "macro" "function")))) (let ((can-edebug (helpful--can-edebug-p helpful--sym helpful--callable-p buf pos)) (can-trace (and (symbolp helpful--sym) helpful--callable-p ;; Tracing uses advice, and you can't apply advice to ;; primitive functions that are replaced with special ;; opcodes. For example, `narrow-to-region'. (not (plist-get (symbol-plist helpful--sym) 'byte-opcode)))) (can-disassemble (and helpful--callable-p (not primitive-p))) (can-forget (and (not (special-form-p helpful--sym)) (not primitive-p)))) (when (or can-edebug can-trace can-disassemble can-forget) (helpful--insert-section-break) (insert (helpful--heading "Debugging"))) (when can-edebug (insert (helpful--make-edebug-button helpful--sym))) (when can-trace (when can-edebug (insert " ")) (insert (helpful--make-tracing-button helpful--sym))) (when (and (or can-edebug can-trace) (or can-disassemble can-forget)) (insert "\n")) (when can-disassemble (insert (helpful--make-disassemble-button helpful--sym))) (when can-forget (when can-disassemble (insert " ")) (insert (helpful--make-forget-button helpful--sym helpful--callable-p)))) (when aliases (helpful--insert-section-break) (insert (helpful--heading "Aliases") (s-join "\n" (--map (helpful--format-alias it helpful--callable-p) aliases)))) (when helpful--callable-p (helpful--insert-implementations)) (helpful--insert-section-break) (when (or source-path primitive-p) (insert (helpful--heading (if (eq helpful--sym canonical-sym) "Source Code" "Alias Source Code")) (cond (source-path (concat (propertize (format "%s Defined in " (if primitive-p "//" ";;")) 'face 'font-lock-comment-face) (helpful--navigate-button (f-abbrev source-path) source-path pos) "\n")) (primitive-p (concat (propertize "C code is not yet loaded." 'face 'font-lock-comment-face) "\n\n" (helpful--button "Set C source directory" 'helpful-c-source-directory)))))) (when source (insert (cond ((stringp source) (let ((mode (when primitive-p (pcase (file-name-extension source-path) ("c" 'c-mode) ("rs" (when (fboundp 'rust-mode) 'rust-mode)))))) (helpful--syntax-highlight source mode))) ((and (consp source) (eq (car source) 'closure)) (helpful--syntax-highlight (concat ";; Closure converted to defun by helpful.\n" (helpful--pretty-print (helpful--format-closure helpful--sym source))))) (t (helpful--syntax-highlight (concat (if (eq helpful--sym canonical-sym) ";; Could not find source code, showing raw function object.\n" ";; Could not find alias source code, showing raw function object.\n") (helpful--pretty-print source))))))) (helpful--insert-section-break) (-when-let (formatted-props (helpful--format-properties helpful--sym)) (insert (helpful--heading "Symbol Properties") formatted-props)) (goto-char (point-min)) (forward-line (1- start-line)) (forward-char start-column) (setq helpful--first-display nil) (when opened (kill-buffer buf)))) ;; TODO: this isn't sufficient for `edebug-eval-defun'. (defun helpful--skip-advice (docstring) "Remove mentions of advice from DOCSTRING." (let* ((lines (s-lines docstring)) (relevant-lines (--drop-while (or (s-starts-with-p ":around advice:" it) (s-starts-with-p "This function has :around advice:" it)) lines))) (s-trim (s-join "\n" relevant-lines)))) (defun helpful--format-argument (arg) "Format ARG (a symbol) according to Emacs help conventions." (let ((arg-str (symbol-name arg))) (if (s-starts-with-p "&" arg-str) arg-str (s-upcase arg-str)))) (defun helpful--format-symbol (sym) "Format symbol as a string, escaping as necessary." ;; Arguably this is an Emacs bug. We should be able to use ;; (format "%S" sym) ;; but that converts foo? to "foo\\?". You can see this in other ;; parts of the Emacs UI, such as ERT. (s-replace " " "\\ " (format "%s" sym))) ;; TODO: this is broken for -any?. (defun helpful--signature (sym) "Get the signature for function SYM, as a string. For example, \"(some-func FOO &optional BAR)\"." (let (docstring-sig source-sig (advertised-args (when (symbolp sym) (gethash (symbol-function sym) advertised-signature-table)))) ;; Get the usage from the function definition. (let* ((function-args (cond ((symbolp sym) (help-function-arglist sym)) ((byte-code-function-p sym) ;; argdesc can be a list of arguments or an integer ;; encoding the min/max number of arguments. See ;; Byte-Code Function Objects in the elisp manual. (let ((argdesc (aref sym 0))) (if (consp argdesc) argdesc ;; TODO: properly handle argdesc values. nil))) (t ;; Interpreted function (lambda ...) (cadr sym)))) (formatted-args (cond (advertised-args (-map #'helpful--format-argument advertised-args)) ((listp function-args) (-map #'helpful--format-argument function-args)) (t (list function-args))))) (setq source-sig (cond ;; If it's a function object, just show the arguments. ((not (symbolp sym)) (format "(%s)" (s-join " " formatted-args))) ;; If it has multiple arguments, join them with spaces. (formatted-args (format "(%s %s)" (helpful--format-symbol sym) (s-join " " formatted-args))) ;; Otherwise, this function takes no arguments when called. (t (format "(%s)" (helpful--format-symbol sym)))))) ;; If the docstring ends with (fn FOO BAR), extract that. (-when-let (docstring (documentation sym)) (-when-let (docstring-with-usage (help-split-fundoc docstring sym)) (setq docstring-sig (car docstring-with-usage)))) (cond ;; Advertised signature always wins. (advertised-args source-sig) ;; If that's not set, use the usage specification in the ;; docstring, if present. (docstring-sig (replace-regexp-in-string "\\\\=\\(['\\`‘’]\\)" "\\1" docstring-sig t)) (t ;; Otherwise, just use the signature from the source code. source-sig)))) (defun helpful--format-obsolete-info (sym callable-p) (-let [(use _ date) (helpful--obsolete-info sym callable-p)] (helpful--format-docstring (s-word-wrap 70 (format "This %s is obsolete%s%s" (helpful--kind-name sym callable-p) (if date (format " since %s" date) "") (cond ((stringp use) (concat "; " use)) (use (format "; use `%s' instead." use)) (t "."))))))) (defun helpful--docstring (sym callable-p) "Get the docstring for SYM. Note that this returns the raw docstring, including \\=\\= escapes that are used by `substitute-command-keys'." (let ((text-quoting-style 'grave) docstring) (if callable-p (progn (setq docstring (documentation sym t)) (-when-let (docstring-with-usage (help-split-fundoc docstring sym)) (setq docstring (cdr docstring-with-usage)) (when docstring ;; Advice mutates the docstring, see ;; `advice--make-docstring'. Undo that. ;; TODO: Only do this if the function is advised. (setq docstring (helpful--skip-advice docstring))))) (setq docstring (documentation-property sym 'variable-documentation t))) docstring)) (defun helpful--read-symbol (prompt default-val predicate) "Read a symbol from the minibuffer, with completion. Returns the symbol." (when (and default-val (not (funcall predicate default-val))) (setq default-val nil)) (when default-val ;; `completing-read' expects a string. (setq default-val (symbol-name default-val)) ;; TODO: Only modify the prompt when we don't have ido/ivy/helm, ;; because the default is obvious for them. (setq prompt (replace-regexp-in-string (rx ": " eos) (format " (default: %s): " default-val) prompt))) (intern (completing-read prompt obarray predicate t nil nil default-val))) (defun helpful--update-and-switch-buffer (symbol callable-p) "Update and switch to help buffer for SYMBOL." (let ((buf (helpful--buffer symbol callable-p))) (with-current-buffer buf (helpful-update)) (funcall helpful-switch-buffer-function buf))) ;;;###autoload (defun helpful-function (symbol) "Show help for function named SYMBOL. See also `helpful-macro', `helpful-command' and `helpful-callable'." (interactive (list (helpful--read-symbol "Function: " (helpful--callable-at-point) #'functionp))) (helpful--update-and-switch-buffer symbol t)) ;;;###autoload (defun helpful-command (symbol) "Show help for interactive function named SYMBOL. See also `helpful-function'." (interactive (list (helpful--read-symbol "Command: " (helpful--callable-at-point) #'commandp))) (helpful--update-and-switch-buffer symbol t)) ;;;###autoload (defun helpful-key (key-sequence) "Show help for interactive command bound to KEY-SEQUENCE." (interactive (list (read-key-sequence "Press key: "))) (let ((sym (key-binding key-sequence))) (cond ((null sym) (user-error "No command is bound to %s" (key-description key-sequence))) ((commandp sym) (helpful--update-and-switch-buffer sym t)) (t (user-error "%s is bound to %s which is not a command" (key-description key-sequence) sym))))) ;;;###autoload (defun helpful-macro (symbol) "Show help for macro named SYMBOL." (interactive (list (helpful--read-symbol "Macro: " (helpful--callable-at-point) #'macrop))) (helpful--update-and-switch-buffer symbol t)) ;;;###autoload (defun helpful-callable (symbol) "Show help for function, macro or special form named SYMBOL. See also `helpful-macro', `helpful-function' and `helpful-command'." (interactive (list (helpful--read-symbol "Callable: " (helpful--callable-at-point) #'fboundp))) (helpful--update-and-switch-buffer symbol t)) (defun helpful--variable-p (symbol) "Return non-nil if SYMBOL is a variable." (or (get symbol 'variable-documentation) (and (boundp symbol) (not (keywordp symbol)) (not (eq symbol nil)) (not (eq symbol t))))) (defun helpful--bound-p (symbol) "Return non-nil if SYMBOL is a variable or callable. This differs from `boundp' because we do not consider nil, t or :foo." (or (fboundp symbol) (helpful--variable-p symbol))) (defun helpful--bookmark-jump (bookmark) "Create and switch to helpful bookmark BOOKMARK." (let ((callable-p (bookmark-prop-get bookmark 'callable-p)) (sym (bookmark-prop-get bookmark 'sym)) (position (bookmark-prop-get bookmark 'position))) (if callable-p (helpful-callable sym) (helpful-variable sym)) (goto-char position))) (defun helpful--bookmark-make-record () "Create a bookmark record for helpful buffers. See docs of `bookmark-make-record-function'." `((sym . ,helpful--sym) (callable-p . ,helpful--callable-p) (position . ,(point)) (handler . helpful--bookmark-jump))) (defun helpful--convert-c-name (symbol var) "Convert SYMBOL from a C name to an Elisp name. E.g. convert `Fmake_string' to `make-string' or `Vgc_cons_percentage' to `gc-cons-percentage'. Interpret SYMBOL as variable name if VAR, else a function name. Return nil if SYMBOL doesn't begin with \"F\" or \"V\"." (let ((string (symbol-name symbol)) (prefix (if var "V" "F"))) (when (s-starts-with-p prefix string) (intern (s-chop-prefix prefix (s-replace "_" "-" string)))))) ;;;###autoload (defun helpful-symbol (symbol) "Show help for SYMBOL, a variable, function or macro. See also `helpful-callable' and `helpful-variable'." (interactive (list (helpful--read-symbol "Symbol: " (helpful--symbol-at-point) #'helpful--bound-p))) (let ((c-var-sym (helpful--convert-c-name symbol t)) (c-fn-sym (helpful--convert-c-name symbol nil))) (cond ((and (boundp symbol) (fboundp symbol)) (if (y-or-n-p (format "%s is a both a variable and a callable, show variable?" symbol)) (helpful-variable symbol) (helpful-callable symbol))) ((fboundp symbol) (helpful-callable symbol)) ((boundp symbol) (helpful-variable symbol)) ((and c-fn-sym (fboundp c-fn-sym)) (helpful-callable c-fn-sym)) ((and c-var-sym (boundp c-var-sym)) (helpful-variable c-var-sym)) (t (user-error "Not bound: %S" symbol))))) ;;;###autoload (defun helpful-variable (symbol) "Show help for variable named SYMBOL." (interactive (list (helpful--read-symbol "Variable: " (helpful--variable-at-point) #'helpful--variable-p))) (helpful--update-and-switch-buffer symbol nil)) (defun helpful--variable-at-point-exactly () "Return the symbol at point, if it's a bound variable." (let ((var (variable-at-point))) ;; `variable-at-point' uses 0 rather than nil to signify no symbol ;; at point (presumably because 'nil is a symbol). (unless (symbolp var) (setq var nil)) (when (helpful--variable-p var) var))) (defun helpful--variable-defined-at-point () "Return the variable defined in the form enclosing point." ;; TODO: do the same thing if point is just before a top-level form. (save-excursion (save-restriction (widen) (let* ((ppss (syntax-ppss)) (sexp-start (nth 1 ppss)) sexp) (when sexp-start (goto-char sexp-start) (setq sexp (condition-case nil (read (current-buffer)) (error nil))) (when (memq (car-safe sexp) (list 'defvar 'defvar-local 'defcustom 'defconst)) (nth 1 sexp))))))) (defun helpful--variable-at-point () "Return the variable exactly under point, or defined at point." (let ((var (helpful--variable-at-point-exactly))) (if var var (let ((var (helpful--variable-defined-at-point))) (when (helpful--variable-p var) var))))) (defun helpful--callable-at-point () (let ((sym (symbol-at-point)) (enclosing-sym (function-called-at-point))) (if (fboundp sym) sym enclosing-sym))) (defun helpful--symbol-at-point-exactly () "Return the symbol at point, if it's bound." (let ((sym (symbol-at-point))) (when (helpful--bound-p sym) sym))) (defun helpful--symbol-at-point () "Find the most relevant symbol at or around point. Returns nil if nothing found." (or (helpful--symbol-at-point-exactly) (helpful--callable-at-point) (helpful--variable-at-point))) ;;;###autoload (defun helpful-at-point () "Show help for the symbol at point." (interactive) (-if-let (symbol (helpful--symbol-at-point)) (helpful-symbol symbol) (user-error "There is no symbol at point."))) (defun helpful--imenu-index () "Return a list of headings in the current buffer, suitable for imenu." (let (headings) (goto-char (point-min)) (while (not (eobp)) (when (eq (get-text-property (point) 'face) 'helpful-heading) (push (cons (buffer-substring-no-properties (line-beginning-position) (line-end-position)) (line-beginning-position)) headings)) (forward-line)) (nreverse headings))) (defun helpful--flash-region (start end) "Temporarily highlight region from START to END." (let ((overlay (make-overlay start end))) (overlay-put overlay 'face 'highlight) (run-with-timer 1.5 nil 'delete-overlay overlay))) (defun helpful-visit-reference () "Go to the reference at point." (interactive) (let* ((sym helpful--sym) (path (get-text-property (point) 'helpful-path)) (pos (get-text-property (point) 'helpful-pos)) (pos-is-start (get-text-property (point) 'helpful-pos-is-start))) (when (and path pos) ;; If we're looking at a source excerpt, calculate the offset of ;; point, so we don't just go the start of the excerpt. (when pos-is-start (save-excursion (let ((offset 0)) (while (and (get-text-property (point) 'helpful-pos) (not (eobp))) (backward-char 1) (setq offset (1+ offset))) ;; On the last iteration we moved outside the source ;; excerpt, so we overcounted by one character. (setq offset (1- offset)) ;; Set POS so we go to exactly the place in the source ;; code where point was in the helpful excerpt. (setq pos (+ pos offset))))) (find-file path) (helpful--goto-char-widen pos) (recenter 0) (save-excursion (let ((defun-end (scan-sexps (point) 1))) (while (re-search-forward (rx-to-string `(seq symbol-start ,(symbol-name sym) symbol-end)) defun-end t) (helpful--flash-region (match-beginning 0) (match-end 0)))))))) (defun helpful-kill-buffers () "Kill all `helpful-mode' buffers. See also `helpful-max-buffers'." (interactive) (dolist (buffer (buffer-list)) (when (eq (buffer-local-value 'major-mode buffer) 'helpful-mode) (kill-buffer buffer)))) (defvar helpful-mode-map (let* ((map (make-sparse-keymap))) (define-key map (kbd "g") #'helpful-update) (define-key map [remap revert-buffer] #'helpful-update) (when (fboundp 'revert-buffer-quick) (define-key map [remap revert-buffer-quick] #'helpful-update)) (define-key map (kbd "RET") #'helpful-visit-reference) (define-key map (kbd "TAB") #'forward-button) (define-key map (kbd "") #'backward-button) (define-key map (kbd "n") #'forward-button) (define-key map (kbd "p") #'backward-button) map) "Keymap for `helpful-mode'.") (declare-function bookmark-prop-get "bookmark" (bookmark prop)) (declare-function bookmark-make-record-default "bookmark" (&optional no-file no-context posn)) ;; Ensure this variable is defined even if bookmark.el isn't loaded ;; yet. This follows the pattern in help-mode.el.gz. ;; TODO: find a cleaner solution. (defvar bookmark-make-record-function) (defun helpful--add-support-for-org-links () "Improve support for org \"help\" links through helpful." (helpful--support-storing-org-links) (helpful--prefer-helpful-when-following-org-link)) (defun helpful--support-storing-org-links () "Make `org-store-link' in a helpful buffer return a \"help\" link." (when (and (fboundp 'org-link-set-parameters) (not (-contains-p (org-link-types) "helpful"))) (org-link-set-parameters "helpful" :store #'helpful--org-link-store))) (defun helpful--org-link-store () "Store \"help\" type link when in a helpful buffer." (when (derived-mode-p 'helpful-mode) ;; Create a "help" link instead of a dedicated "helpful" link: the ;; author of the Org document uses helful, but this is not ;; necessarily the case of the reader of the document. (org-link-store-props :type "help" :link (format "help:%s" helpful--sym) :description nil))) (defun helpful--prefer-helpful-when-following-org-link () "Prefer helpful when using `org-open-at-point' on a \"help\" link." (when (fboundp 'org-link-set-parameters) (let ((follow-function (org-link-get-parameter "help" :follow))) (when (not (equal follow-function #'helpful--org-link-follow)) (org-link-set-parameters "help" :follow #'helpful--org-link-follow))))) (defun helpful--org-link-follow (link _) (helpful-symbol (intern link))) (define-derived-mode helpful-mode special-mode "Helpful" "Major mode for *Helpful* buffers." (add-hook 'xref-backend-functions #'elisp--xref-backend nil t) (setq imenu-create-index-function #'helpful--imenu-index) ;; Prevent imenu converting "Source Code" to "Source.Code". (setq-local imenu-space-replacement " ") ;; Enable users to bookmark helpful buffers. (set (make-local-variable 'bookmark-make-record-function) #'helpful--bookmark-make-record) ;; This function should normally only be called once after Org and ;; helpful are loaded. To avoid using `eval-after-load' (which is ;; only recommended in user init files), the function is called each ;; time the major mode is used. (helpful--add-support-for-org-links)) (provide 'helpful) ;;; helpful.el ends here helpful-0.21/screenshots/000077500000000000000000000000001446151713400154415ustar00rootroot00000000000000helpful-0.21/screenshots/helpful.png000066400000000000000000002635061446151713400176220ustar00rootroot00000000000000PNG  IHDR[sBIT|dtEXtSoftwaregnome-screenshot> IDATxw|MɽىJ&6U#TQjԦ^5[:h*jjSZE"6a'HBvrJ"7}߯W^s{9眫D>>B!Bd` yzjj׮FAPZB!:HRRcǎDFF>U8WƍԩS  C.-BP*(JT*ǏggPzƍcʔ)$''c00B!kh4;7nL||<xt\U!V)TG dIB!NcŀipݻnTB!!qqqxxxzZmn#Bboo`JB!q~8B!$B!Al !B IB!r$[Bqnn!xs;!csVΞ;σ)T%;Pz5RX1V,zR*ٲqZ xH%xe%''3u{T\ "E S{͚6a~sj)^(*[mn#Ȅ X<Ӧ|+>dO8y4vgyٹ: !D:|5{ܱERwٺm; WnRdOs; !D+Z3g1thfϘB`1] -[ccc'R!+̟?ǎD>d0y7V-9xvZ-ۆ+3{</_݃QÆRr%޻/r ݿOyiv#zRi%Kٷ?Qe.UaŪ\~GGG+Sh8x0c&|D:h  /MJ~ܸy3а0֬X??Jĩϗ]F$%%p8HdT$>%J0ʗ+kf̚akkKX|CCxVms!ī!]ɍ(Q +W)ɜ3(陥:::y!_FF;Q+WbLrE?6o/[1g|z`XѢ񧟑Z#x{{_.III,^77YWlmmyVMGFLpHNÇyԫ[yј9۰ WzNll,!c:v禎 ۇΜ=g^gݻDFFRJ]g_|ɉS\ggg*q9 7?l7 Tɒ,^^B\g)\ WCr5Of.ˉcrQG9ƥ@Z5o1;hG#>>^yXt1J-fiՒG {>\LpHe˔uqmuС{\ dCT,_f{`0SZ<`0pijתϞg:p =~QQ8;9SdICB/\?͝B`?ݰFˁCR7]&eR~Y5kҬio;<} 1o, -E_h !^ 6j>4/~l "44?lCj7 <JVIQeP>eKp &L{wkӠRAWF-D r%? ,xtݗ}&))ŊpJ-7Ýu䈳vvvĘmkkkMv+xިU9ͅ9rP>>̙9y7O̜;gKKN78p0ſ~== pĐYZfBޤ׭'>>SX̯U:^^\HCPK8n QL| o~G BA2˔%}|1߿O0z>+TF}R`԰aXA(^T*ԡ>>>pE LBط''':os-J L…wKBsB=ZW+!Ld2];O\|** .Q\Y<*}PXQ rczDNBO-R};uhb˨sٶo~#P'vLf͝3Q*~UT*j)ս+L^Je]qM|}|ț'yڭ۷qqqOĝ]jjVP`g}c97)JͤϧҪy3<<܉fM|( {įByW}ھs];uZ TL9!TOfvctzXI$&%fvvvs Vw5kX /e˶lk:{/ yDizt{ワ>{~~;7k#\ HB ߗKh4ۺl{QQQ-S!/Y;jU*  'ůByN9ˢe Ņ7j``>/-ңT*quuH:QÆ?pCBxPfBjjSUG2yxY~cӫ~) u׭|yR׮0'[`f?l xz~L-W1˒e8E bcc'ؿ}SK~`SޥS?LdTٶ=oӲ;Vgh5"7zy3ffՒE9?=)IRrKih?Z{ F{n8+U.˛… !:4kژ_,~d$MEreSApz;\3c(H0 f=YT.]UӪ&&&y6덛+yg.:|4Kg&Vo7)Qw\Oq7WǚOddŴ4|ٲ͓zή=VO:ŊQӓ1Fġ#Oz(닋 waL ׮]':*DjV#^E(STꞞs.иEkhMv9s< s`b;N)^(vZKϓ~v??2S`AZhWNefxzx*g~|J'_޼ԨVY^[=~"e= Fzpz2:қڳƵjב s""}*_lwX >SuGٵ'4.oZ|}R]ΉFze$dϋ`OJ%gϟ3O3\B _lF fp쌝Z}ceo2ڏYϹU={S7Y5kڄ\J|{ؾ32:қo1mzg=Yɖh4T*خߓ'\…Ku> 4npX:ϟNԬU:ujqع{\y سwO_[ˁCy_GTT4 յ*FFC&a</ @ +;{ɜ>{\\_|c2e-[0Y1tl[[ w#Fw[gŏ>'ElW( "]^fpYz@:oaGưi9?e˔f<_R%,_{)SMޚ/Kٗ_J%?Uq._I\|< qc, .\Dw`ggG޹60ӳ;0͍avʱ2CF2ڏӳg'Un߹ypqv#nٜ^^l7/!!!m݊-P ;21Sm2ٚ!`svn!c ׫Kr;!32:λ xzB!Il !B IB!rB!!2fK!"I%BB![>B!\FB!AYJɆVcfrݷ_~1s;FRc~M"1R^Ju@oHJ9Ovzږw[_yϝm2Uˈ9#1{~WLO%Ro#)UgƬ^"!!!'/h$4,%QEH}$mfS\9ϺgdWmiۺptWkXî!RPc5Rd^YN .N[r" =0рmױm;ױ/#fe]tT=+v>O%R:>d0ji~c/]bM~.ӃV-mXVC&OO^5|(ʔ%..~AXz 6*͚ѧWOZ@ѵ ʖٙ۷ofLk֕6ZbVѭsg ,H||.\3}JJ'%aL 7nb՚SY3}'OL5瓏&ECH3j$^ s%8ƍBc,Z,Szzxz'yd^f 2%}C̞7` xo<6vYI>]2؆$lW* !۰;>j{\>b_+@si#:$T$h.As~6&8ojK| WDݱo /_UM4Vb{er*[+'hc 6xdke}-եOts7jdu+V9X?j'g̙7… a0x)Ri@z{q=~(ȡCБ#V Y&څe+Wq9(N;[‡=Zx{y1ixnȬsqsuNi椤b|<|e+ūHazAdTvD7iyoft/W7oҵWo|Ζmۭ{߭JUKg^ְv+Vbaa 6%dN=KdQE༱(T>޼ kk8i2j}aMݭ5dW]1gk(K1|ү֍"vd @2:Λڢw+I\Q&ɮsŚ%!LWyK\ho~mt !du0&112{jp0Lhѣ8::ҩC{svK2b C9tKWիt̙8/vm6Jz=wQ^ְzqDܽ˹ Rr;emPdmw(t (Q=Mgulet(c~JTQq705N$f#e<B;c%hcssLJ8͵=1Ӻ>X 3 o)E zٺǚj?˗獚58?ǎSƦ5a6ִcִ? knM'9Xq,9hj-(RbQ>$꘩d+ek$~@}ˢ"%UJJbkMD}< +5)[P/'OvIJST.^W4[*VdZxb-[[[w̒uvnzu$R(i'4Z6j5?eRdQTCզzre طߜ9{"E1Om:®]g܉`5q z{P(ҬVeT/kYnݾCbbuddy\_QD{2Wp6O&`۰]}?ޥ(lSŜ$=è%BbZAǽDw:HWC ڼ2:(I\ xzc͵y>o < ?*=Z1oM;'<^ēeBI|I,cd*#H.@y)$VQ$5j\P$DtS17PEo+Ps7~V2dt8;;eSm_L]W/k≉}~1l@|DR6zlCaK0u7A|@):/^7׮_Ot/Q# cq\ bѩ..Tͯ+v-ɨ^Ȯ[)qm7{kqS3O` Z| 5ѹYRQ!n̙`s}7Bx7BR̲˶['::⒳BXѢ^Ωghlfmi剋.0ÌXӎey'3HДdDzR\Z2.EXUUN^+1[}T(PCQE^XG:'OO4}m;F-pss#,wN=c?C (@pHE b|:Vа0׭G"&&hSBl͹]5iǬiaMݭaM<>gW̙8IOkKB e=e9]r:"1BobpD}zd}R>${ѯRţNB16]}9W/k֥ \M|OI3$Boy6hSR$ N5!xˡ+UbA5:|(ӾNNNDD廙?y'\t Jſ'Op m݊kL9#r"ٲm;11)\8ŦiΝxzz0ylmmسo'O[Hs1>WnL8.Pl3=˦` ۇp;_ cǹv:9s|^g51qGQf C߸Fv/kĿ1Bu̯j0x3<ٟwD 6ϬRqP_K0U; &z=\ل"95cI@R1h=+՞sf$TȮ[,]0_. 7lL| t{mZ[Y-YGaogg-kΝjaM;Qc-kn kɮ9b #ph7u2:C⫏{a`껖W;)k =K3u'"d\~^.[18"TGtFsi (T( 1י{_l)cj2,(^cFp pLn""Sw^OaMh.@[_Cgޞ-!r[tc/EVzxV_h!uc"ɖOyOB!n^Ǟ-;o~ޭ$(T;>#՘(BC?~B!$B!Al !B IB!r$[B!9H-!B$ɖB!DdK!"I%B$B!Al !B IB!r$[B!9H-!B$ɖB!DdK!"I%B$B!Al !BdFjƪ%s;1 !"5?1Is^p֭l Zvvvtl+]MNc JEw'::gϲf:vxzݶB*:zi٭+k֮K7 ܏|ңkw @bb"Ӹ[GDJ^,B2l-_+Wj1-%%% AWѧvj/}ݻj4y**UW%f!BL'[z4-yE9pMGsy Qr%ʕ)Qg1e<|м,777Z5kj~k7l`ο)Q~X_6pm:`^cO{Z^[<<<`M߸<ژFKS֦?7[&fƖÇSn޽˲hXhErrU1W(_.;냋3qq;/ÇVogk;pgW˗'99s~{xUKڶiCO< `~.ZLbRUY!xdzJVgJۑ^z߾yVeoim\]\tewHHH\ 4;KAӢ9w`5}bկn1❦|?{l”O&Qv4O9^I`Xd1oQ Rivά0FOBB6j5hڹFGr9(W-Z5kRXQCB_hB!Dnȵ.^8;UXv4D?xek Ŋɉd6m[ү8S/\*fk?Ѥ8;91?Y|U(X6F#6lJ7z^.^tx-(N[[[~ S֦?7[nS|yH۫R$[B!^KN,_aub<ɏF=J%_1~~iJeRTTtiΏZMjUSͷXs0ݙWdIh޴E'x|_f0&Ɩ͛❦8ϕWZT* H:o{id%[V}?ÕF7 -S&ͲmAAWHLJ gzt킣c)B g܅ THݨYyUfmݶ[S`̙Ell,;-۶;sg͛Dܻ_|wӾDј?˜cnؼW0t@DeWbp|vɓ0 $Fkv%Dkb޶c'mZ`⸱ 77Lt*cbpvrùr*:tHlF9;{|B^ݻ1jPzu^'_lL/S!xdk֚r1T*NN}msn>|-WC؜ի8:8P\9J4+_,˕7`]\9ʗ+6nsyZ[n3{<~,Ş^ϴseҰkeR0S1/X h`Ͽ^*k0ǟ|ƥ4O-XlY執k/]3gq58G ~'>B!r0VV-Nbb".ߵmӚaAns9:!B3f&rGǹ  < * )sBŎ_ؔ~ %#GۤX,fuիJ϶HfO9vN~uՕ93gĂK5ԾEuYӥsg~ZCB ɖRc,\82 B-/}ysicdU"C`܍ӟsuqLJcKUɗ//ݷ(smsOց} Z6kfNzv늃= #*ʔ;p@ܧmO E0 /^!itUZ5<ٵgOyÇ V-ͯveĦoMNADDƖgߟӵWoF J2e2tV^ÆMefՓ;PxqztBepvv۬Y.Sڻ?`kӪ%6j5^ lP:wPqE>Xs**(^11߸Uk~L8oLn )S䣉x{yRt:ƌW\ qХشy3>!k7n)B[ ٽw9zNxxӏ?捚59xpVF!Yիs "##S[tk~_Kzuӫ'z 7jdu+V9X?j'g̙7… a0x)Ri@z{q=~(ȡCБ#V Y&څe+Wq9(N;[‡=fjy{y1ixnȬsqsuNidb|<|e+ūHazAdTvD7iyof\w+_,7nޤk/?&<<%3bJ Apry2[@LOP5p~ej!*LINA{){ *h d채hS9p-v̙lSeCJ׏Y UpxpܼX}Pa իQ7n$eBBCQ@W7N5],X^j}111PF I"e:*S n0&јvb` g)^vI3ھo6PШA w(á#,kOyY'NtR4fֺڶižY)x9Vud~{(ӻgɔF=#:gێ]cᒥ2`F+rŲt:s/ ..c˶;iۦMZL@VP ~/MF?(v4ޔ8䃘۰}"ˁ IDAT{Lew}fZn0 'Wd<P)!_ɼi)6'OݏkBi] ժVa{MnRħVΎ͛aeůpuu :VEPh>Dr岯"B!RKtttWX/'OvIJST.^W4[*VdZxb-[[[w̒uvnzu$R(i'4Z6j5?e.RdQT3Y˗+KgΞëHlll~[kי?g6w""XzMxnܼI\\u`PE)T |A6 j ”4nq4%0] aQO=pڣ0 Ox]6{yK5APiƴ[n8qN)]ʢ\צ [mޝpm6E+Hn"Ru5(uӺE}E[C˯勺i]ugC*"Rg" DNd?e2Iȃgޟcf>;ٙnLzn@Av:ܳ?ήtoccCVvvP Çܺunxzxd ƍITT4wbbv[qy/ff{޺222RgeΜeױq%{1zg\ CdfdR}zNp-1)Ie6ԛW=x 'N;sm)׶呖描JS-^} k&ˇYj[4ǫ~UY?}Oa윞N/蓚|>#ZPOOƍP(lY&j/_$&&q5(x.OΝؾk7qqdddMJr ]沿 Pd xm^xxzY֨g s-4X[[q%(cc6lP(033'%‚1h=j7oR8V?;;99Aڿ{LvCBB89Pɉ[o}f&Ͽ <">]RA@AT-?O[f.ݫhr dr`vf@A=YLguФuju"꧿xgSQCdf}>zrSvliaAڵONۻ5kֶS!97}zDTV?vZBSDI\AKWPfM^I?wv͚L0fM3aխ] '$$-k/t%Z!/!j.\HN(SƎe0uʤ{x׎ ӾJ̘>ӓǿRspYz wb4G,[| 3aQѷ9b8S}&cnfϴU6p @ިAZ0w. ̚3h^ϤTqp`xzS_jk%݋s\`PTڅݻSbxU&+;[RHmeGJ)[[328w~]̠|^vALOW*j_Jd>FRѸQC}ܜsc{/J**77WtzLHX3B &4"\,9ADEGGݙ4n,U*WQv67j$>cP/"$4Ǘ%ǣ~ ѹcGjז]Ay^]!njb Dߺ%{t盯˫^ ==iڤ1Y\ Bvm8vA. Y ) ߏAi__xI_L5xǙ5ѭ+G^::u@*U`鄄QT)Z4XLL2vܬ9et>|q94n!_ & 8_,e=tǎa`~ܳgPդaggxՏ!o܈*/IƲqV߻ϗS?rJ1?j5O$<"ҥJUݴ)իy3JoHmKW7_c Jl\o7? ›&_  ..R껻a?Y))WW ُسacei B$ 㑳~32P4OO'=# KKjaiaANn.#zk?c8n:k!kӚ{##͉WSSSlmlxbp J 0xfYKIyj5 Ofw_z=cck@Jjr^^^<1###lHKOOqԏ116qXX~122bά(}yS. Yd=`9aԴ4*//O`HKլm)ȈҥKkGQ]v[TT4e˔yP(HK׬'-=.=vsu@)ؽSut oY?VǃZjP>\4*Y66tA'ݣGOo9QT)EjZfft %\qz8Pݿ/9)#G[DD\'+;kk~YT)[ rR=4yj>N#EI fVеkpgA4mH;P}ѳ[7*|Bӱ&(8ӉOHB,Y8nO2G̴{,\g]cGdQ 2Xt&Iiܟ 77WRd)'55SnDd̝7^ݺr}^:ԫ[jNNfuuW.+T`=EbnnNll.Yʦ^(v)T*@Fsx)Hb;!7"obme%7شug/Wzz:_}-ai݊n]>`un o&voԨAAϹrgAA]b% P`KA  B-AA"$iձ}u\ ~þt~c'Cٵu3}Yp~I#T%&Xr9} 8ˬY(JҪe˒Ě][6VIW:ٻ}+%cMMUstR-1/= %R=;;K'M:c _MeдtM|lmILJ2Fj>+;R'-=]lH[j=[Xs223u^gffT*!%EwPP ejj3mYUm#LMb/ l%$&3Fg Y:'KWe{vfF&*׭D DAÇu5\`=hhwpZb-o4 }y*6&55-rAxQ5<<O:5$<"Bgٱ'pTQ*9Pɉ[o3䛜1s-4X[[q%(cc6lP(0339naa[uZ͛7quq.377DZjUϥW033e:˟77 sFf&;v!!!{/Fx "w_upp|ru6999Դ4lmm-!~φ&Ͽ <">]JRY-<=<%y/7^>o;1TXAoUmZbEn)4jNt~#՜Ml=]oc!KY0&KAg/_hI.]KN=wu0 IDAT ƍBrٹ{/ƌ"I""3|7ffW9{S}PR%"oޤj*׫fI^zڹjsyB&MP<.]6(\H!g_y̯dÖ-89:ˋ. ?WѹcG:u@ԭhOn|PJߐR?rg<H?[C%FΪkh"VO?s7< JP}Rظy3Rv'Ѭic>Au{8qG½^=._սrD֮UkkB &/B5*8A^ >&RS|J:Y$))=>K[ i1}csPWΟwΖ  o7m/]rzTnʁ];;'<ߒ-YL\dl{8]_GR.k}ri֕þ(KWzU]xsHlQ}'66NgZ&*:B٭+݋@شq#<ߎ?δa%T*~nΎϮti͟G 7W9sq7!~}KE8۫8۽8Rv)_T*i߮mICW{]xsKIG9rgooOl\βKKK:ϯKgP7lB`4nؐ& qСBg>|ȭ;w)BA6?vL;zn|AH=h ݻvؘ=b $$$qVkzn. 8ujckkK\\[Ж]k֨E ٶs'`eiI^jq/> WW9{{nX_}k{zxPb$K)}m̘>C1iX֮CFF /Ư$ݟGB/}Rwn bc)gBNN.秹;hFJ#6Y/,ͩ3gپSI[R%>>KřAqUy4nĝùEl)mWpT kkk# ZԡC`݆GDqsqh1m6ed'N%g{=^ɑĤ$/]΃j2qpb~"==]{2Gc銕L?M[tOo!WZ]jfqm؄c* d|Ӧ}n.Y]ҼӼ9%6JĩthUAWJڶ!ٰy Bw/jrO e:-s YJ矘1}*}h2TL^ZrI_KGBȻ}+Nq׍ˈqqbjj 'NₑesT*w pVgT*酞bH~cǏӴIcxz4R(4j؀'NKtz###oXXX`iaAΝ053M(]hӛP(NVKIݻx$׭ ,<ݣ;!aa̝3gչ:{t7h%g{=~hGh_xZ5kҶu+N9#[Oڻ s}ѣR2IIL%pRHHL$5rofPkΠk#Բkwe=|jN=G3w5\XbGJ*OQ:4X=)գ;'Nbϵvlި\}UJ{8ɇz2oz~Es8hP; fv:]*9 rn_%Su#m}Y\{.Δ-S###b5gK$?x}G]s6ԮUS'݇z]ؼݻ1ܺ}|ncş{vag`LϣP(ĥ˗˒#*N5ܸLOpnnRR)gܳ xyWFraC222QdeeaanaPz0wO?s,$77$h&񧣗/>=K)Wzz:&lilS}566lќ`Zhέ۷_Ԥ+޽|ˬ$աTgk| kwCȱ]ȵߐsҷr)~4Rݽ͚q6n|+{poҸ -.oP{ 11Adzt|v]###WmPʗ+י/###\,-,u[YZKgkcƆW-Үu+6nޢ3̬Ζ!??zN5''ڴzu7232QTn=' 8s@IG~ՠ`s'a?V-]H2)RTOxųjtz+VҲEs=3r:}u(թ3g;};grZrھwn$A>66ƍ@Xx8M7Nz3TFF&/Y),-,[6I{f͚PΜ>zT*Q;FVuԬQ>yǬVy&.e8V.<<O:5$<&^/B̜T2 ު[GuEc&];wV}<RRh֤ ʗ¥_G`|_=Jgrؽi>S4k|ԧs/dqlX;YYY=DZZ:UT}]r#ũ3gWvCy._ϴ/:d3O'77țQ}z7oҲE &wbba_|r9Q~3ZJuJ٭+}z`瞽(?\}Ր:ve뎝!\RaQ)"ܾ 9G?|({c#no/˧cƕt(kWn|=%;QI#HP(شn [(pbCl ) ުW'Ô7CJ8:Ve`K ^q*V{\|lڵښ#GK:׆CM"[2RtaI!Mw Yf%*77#יE u(IeDAA"ry~AA '[  EH`kq,ܢEaOj%*YL?Aygգ[W} kV.'/}5ί~bJ%}iղeIR,f|9M { 3 Mâ&61A^(6Jڲe)Scllqc'sj5Qdd=^vX~HU*;0㡬c7HgcR޴:![B+1zpܬl 8&7=Ӓ؄ujJP\x1ÛW2 l П={pW}B3Cnǖ;8aMMM:xm[wbbزm~~ܜ_fhH76lRttxڶn͌S8tƏn:ddd`?tcc?`h^?Rbŀ~}X lܲ5ߺڴzAS 0g< J#1M W|p0P)3Z5>033#txi7.Τs6n٪PߺJ%K'n<<(o#CNn.>&X ##i^;rsrٳ?}𻔾*rՊ*y[k|,m ҷHmwծ-Çzcii\RPKv^5k`l۹{m>x0.Y*) ȻO(cΓr7A_N܂_ϐ\ :1hꆑ9sQёaCHHLd=AII,^)pue1$'?ԙ3|KXtU*W&/OsԡC`݆GDqsqh1CH꧰nڔ>w 'Y&L@Zz N|=m*wf%ؕ.;͛canRHS[rC}SzuGG3j\]\a ._J@6uWgXaU0{=I#W?Է.Jŏ3+,m;-h٢9O3Wwbb8t|7+9{(z R.W_MLJ!)ظfu#eۑrn;ũRJ|},_3):핞={#|tJ&Ǧ[%IJ'>8)žSrm[&&a >,]J!9EBb"A׮ScҦüp#2oZg߇}; uEEb՚L3țL;l:yhr2228?g R⑫T*?GR@o˽+++f~?8v8M4):Rk>,,#Ux M332QTn=' 8JJYe2l7\{@O >!A'})R:mmm#--!W_H!:Rr>U IDAT>rl;* ṵAĠˈ/WNݽn&''B9)OZXXV::y\|33SZhiiܺ}l`A89:j߻J} 6^B>j7o]fnncժS<xzY֨'d=$$$`o_H͍Hk>/G?|"5- [[|?Msrs  n+[?'>4h&~L`ʺmATBL^#M["'7NJ 7WϘ [pwgg#c/Xbztg]ڼ=ժѦu+-X({HaȶY>Gb%/4w8ICRK"׶segϝ[#*1hq=s\`ɨj6mƾjߟ;!'c!ҩZN>3Wnؕ.MLlm+t*9s|B>݋M[q-4R?pݺx!GŊ1m& e Ϟe/sد/oOBB"_7oҲE &wbba J"eԧ`bl}uꇠ5O5lٵw -68}+W%C by3O#W?Է.J1Up'&Dџ~smB¸t)T*6nb̤]I4{>}@iێE4)u(gm;r1Em'Zkg gԧ#wON9_7 @ިQI!kgD880~OI"ʺrasAAÈ  BAA( P`KA`KTpؗV-[T>=u+Oqz.0r 92|7[֯m%}6(5+pؗþ̚ h`KVѳ[W껿NSxӛ\v)Doữgn9pǎmV1ets^* E;J$//y)Nor٥#wiAM7>{'9R=ywVV6ƅ'}tBbR} Hi5 ]Wթͨqpuqᇙ3|*ʢ!ٜ:sVL*AJ}lր}cޗ֍hFI`@c|M&7oݲ۵X,,\` %5iѼ9`XlL&mbOpP YbaݷLf8.Ê]Bؽ7{#m'NQH];wupTe);wq$\N|ÇY5`;oW 9!K]Bm֌'Zf!?_ Az}:ł`@$RѬi[ϙ֯oA¡TmGѾdddDlj(J<ݝ,NFVT*:jG3*Z eާc2ٵ{/h4Jˑ]RTHQ^K.YͷǓǘxWw#.ɟ8\VJ+wwUuZW2b}A"=#.O>tӳ͂eP*8j$G(L@PPZ5o~CxWRTmGѾdddD[U;///f3ZơĐ[)A쉌dOdmqr6[o4jv܉UؕZ]fBDdtEÃ[))bVBQbUQ1L|'3Z&Y^Mŋ>j-WLEu22&DFW5yv<,Ĥ+N…x7c-s!>^UGӣh J^o7B$O_LVeEPVe;hxiJQ]< cXHHH <,v ??iUd:u j ڵ;_x439C??\Z,#.B|ϗg避oYUwԩ]'Z;o;YZ-^^^)Dʿ7!jd<۬\tخ۰?xS&qڴnMM6ooj5NJk]Z5kijbR:HTwjI{!z'ddfҦU+j֨SE!eOE.N|ϞD;ƏIba)6n1b83h`rss>1>{cǘ9uj׮MԯWf͘ ?23q  d9Ù<5/\Db)(B4Ws'&S}̿ QVl\g5uL&V[HT|>CSO_η2hӺx ;?C7GᅦF#z!\|1e4]·|Jsu2̧"ly_.`I{ lێVMzD#.ٱk7ujצ_^ ח6tR-nEz1!Gx{sF2R9},3zѣF0{,<.'6TX,~w#sMJj*jvvqH^6oa Td_'#oBԢa3_+0 _V,gͺذise#####S^TFFF-d0_, 䔭VdJ7SZ>s*&ˊg=]Ƿ~=DbO?s91׮[^`6[y#S -bA$f"ƾRLzu1l AH)|77rQnDmSgΔ|^p g ?2-mmP(|o89a Fq7u~^T(>Nɸ]1WsH0oPs7'P_X饢R9Zc:uTNqIInXj\.oCs|X 6C(]0BS]^E}g\~Ǣr#s>!t,P밲:v5-Q> i8C:F6؎#F 'bp"+ޝaCSVMRRRXf B1>YbS Ѓ%.ݺص{_-^b vpƾAÀZoĪ5EwIe%+W)v{`ۻvY3>z &Oi&t:/\ľ|XG `QfMVa8r՞XM"()a@ixyy̚uYl^!|tȈC[z1|: |O0FAϠQ#-RÆ`1?ۍzgɫ}wq߀_1m(f|eO4urq3_` ʴs6.VcFҬFि(r30Um՛( qVي n^B_w(oǐt!8%Z4i\Ա|s,>E[Wz{^~ǡ0`1v]=S$ Ϸ y;Q`.(kc0WO0N⤻MuRB1{bL1U FsT)'qNewc4$n[\~Gakc/\cW[04K)d@qC!:``Q|O8|4u:z[3stlʡGiӪ3NA-jkO?ͨXr2wA*E^B]6|EK0CpY"[`hf|\9ʊ^IOgam,&6bW&gϚmzխl)"9vQ3eZii 1 '?WWut؁FG0bS)ϗԴ4.^JFV&L8 =**/G)?__yk&6ldEx{Ӿm[4-  )&M!aC>3v>(R>G n/7,69ܢOb "b-/@q?y r%I01Um&nGaI Uij=n+c7proGP+7Qd=(y8\C-&b7з1qqҺiѨ? h}D[bC'.ijvO%.www|0 8@VӢys`L(.%$bX8C^(<؊.]pUܩ#$''@*wbkKXRd۳V_\ L&n޺;YZ-YZ-Pn e6'O($;qQQy9BHeX=DݷLa`b )D?og`sT)#RÆK?51s?Kt ˧EcRF OVKpRanudˢtKh[C}d 9DoW̮ LUP:n;㔓233ONv pB5âF.I'hխvN eeY.sˋ^;ԁR}`"6תcc͖(L8_$.BБRɓ?qFkCPǩӧmn&1yƍgd؎ JEMv8s6qv11Gv!,4OyX* l#[bm?ppΘ:vm` ?ŅÆ|Rn?б};T$&%ěӧ<, WW2u#;v 1G逵}xxxUE>#@z,(r;%,,*WH}7V"i(o}0l*4ƚ᤻JkDC)m4棼< bQN:f,6oێ5>Wm<'ńm;(䄽JN˅2SXT)=8'nX#!R+X\l;YR^S{YUPܡpY^C:K{yZ-j5JqJ*NfVyB&7w7J%/#   իU#My^ɉ}CIIMٙXk%%QVM.\^d䩢)Nƽl1R.T'1`KI,l,j/s/FC#F=DUa$Qf&ऽ&z)\rs*Ky).yPnEdcGI{K\w`qvni9U)\.mC|5o)͚G0CuЗq'Mɬ,.UbPb1  QZ9"7*va9;!7Wp3q[K*:=&WbFRÕ+W yX~äHLL붛XOCn.-YDM'ӭs'V^8겏&IHDFF3~ܫ>gHپbⴂ5nxiB(]5v,y;g:$*h5[bqDrɫ6 Pڍ@YTnk؂2"&Bh0{TJ9es)Fӡ]f֓swHfdixyy*˳߯<[՘5EzyuۑM]-*UZ4y]lZn#Ϸ+ʴ{T7nP_̚j5ѷm?_Ik۠'ڭxah1I$wX%9-&a!K*W#y:b}]/l20lb5!`;~L^N]c׈[1nsX\b[yM8896 B"ʌwX%&/,U1{Q1HbHLJS[>>#6lqHӧLMǙ2q͚6e݆X7nl/OO^{y,j+b[#]:uSiD=ц9҉CxN:E퉉-=_Jbf> ''>V͚ޝ2M5YP]Z5kRN|ϞTCU-)aٞ1}*;v <,WǾD58qtIɖ"Μi|i4nTLpfLB"פ?:t <Ժ@xh(:t 0 kDp;f/r8nvtn_"2-J_ ys6яih@}qZ̞"r1{F~.(]pNڅq?x; pv) nAZUN{"wy(/rٳp='u5]d\O|TT7XGe9Tзf~%PTgq=-9-&r 2n*cV[mE9׳ߠw]/8٭궷z)?[GC1,gxd*nzBGy 2gzT>r/IZ5[8w~NHpYf6G`Yy$\Nm;R䯓'8~}{ =ħ` 4?vֽܙ%Tޜ>6[َN,XS=ʨqSϝ+ރR^>VV4 a+c`-zaOn]:qlkoU>>Xe!#}Y229Rz5^0UyH9se.z)?GddJBPh|߿222<7Dْ{ xdKhI2222222'~(########clȔ#e5m$Re/[JmD9V^}{"<,BT*ܱN:T'<<Ƴxt;Td^2?e%gbQD/e[MFFƊb!1) NX>AAVd./d JYtl61򿌕7`k4 bܤ]gnf3NfO~ Zf=|xm׆>MErr2k+aCЯ/[mgaLEx8c_a@YZ-7nF6؎#F x{j=wgؐԪUVY[]%ү<3߸ɶ;jbAy=R:kWě+X yXS͍dIʧ2ڹ3gd1L<阿p?tDN1t(uA}>O}AvvM4J%|I[A= ?__^?<Ӧ[xnlw*RȮZ5k `n?rPJd-q;|f* u!~YGM:;k&Mz} 'd߲^ Bt \ǿTaj!8^ƿ=CWñepp.Ь?x]5v M4װa"מٓFG{ PL_[?ty R2eG  )&M!aC>3g~pR22  d gpQ[:AA89)t2&b)(|Kf:r+~=DNz:v``Q|O8|4u뭍Ihݚ3eV-=JV1u l[9dgذi3bێ,f&O◵kIK0Դ4b{n:kKl#glݶ/^ݺq~?__yk&6ldEx{Ӿm[4đ]&-k쉌cvthזW'N&І͚6 =ޟܺu?mTv1x(NZCtڕGd5;à  dA6!U!UA;Hoeݡpqh g$R(mk0dµBo: *o&TmބtX5@p]^u!.KUipqU7P3lI2u!~u 궄6Y˦ JH^GHPPׂm;C}8@ڵy3,Z:Q^y 3e _#Z,,X@Jj*Ӣys6'O($;u.p.):|ѿOoQ֘Q\JH>bp$* 0PY,n޺XHζf@>1 Gta4W Ė[cרW.ii!^ߏ/e l߮-[*i<پs@Dp?tDpu,ٵw~;!v%&]?2ek$\N` J.^dh%Kt/I>}$e2yR^ۇ{M|L ^UTP~C 6ǏA!{MA{8 BEx3P-:S8b ЧAj, l D5)>J 3O$Fx7Hs/(^gU1qq<٥ j5;ud߁ֽs>ݝ9%=#}кv~ʫNH"U?/`H>AFM ΝtaCYl)[7m`[ؾU}|ӹqTg[...pVhִ ؽz̮{7FQ]{hٻy"^EzJ%^^dff/2%wwUq_ )s~'XN]Q|=AkauGs Ly>^r~h:[_s[7(fQ jCB(@S&Nz;qvn6V=,uuB* P89 Pϝyx()8;;+}u>ٌE]tU\_'o+>(S2-t:wb5l^C.q߱'$:=&Wb lbtxyzڝĐ+*В2t'Y2:k˾B3,GBɞH ǽslݾ]]իUcN9K?x[))vU<0hE4ӑg4 "v9B\}8Zݠah"٠ŧέ/ X'kaB<59 \-fI_LS2$<ea\^t>׾S6;vח &HY>>bq)))T^ gӦ7B|<3gL/z^]v<,Ĥ+E:`fΘNȦb!!!09///D/JBU.ܪm |[ߧk!u'`j4. k^pl~-]3K뗏f󱱄ΙhT*gZlQWRSy [*_)#['Oޝcst5, 'N=:v  &Sј/nsmVY/=w˯."wl4Aϧ&Jw&$~G}1y< $8(Y;Ĥ$:uhϑ(IG޹۰?xS&qڴnMMF +1)׮1׸y.^.U) x%\/m5\_;te]ڟitor1ex}'Osϲp76!~Oi}TD^BWeJGpu| ?W_~^˖(4"'rӧLbc0cvl׫g'1c`ӱX,Wl_vqH^6oa 8},3zѣF0{,<.'Ƕͳy㑩|{i4v1|`y)RRRD8!Y"פ*M!CG\|1e4]·|Js6GvթSqc_suzXweXe!##ӦRN&>á9Rz5^04BYbqv=222ţP(xYS>oot[V-$'O`ȥ{xzx{VMFFF-^^l5"ᡡ ':՗d2222kDrү /########SlȔ#r%{Zj<'gʟ~?v^ܱewi'I^EeKܱۘ;rͫ0j5BX裎+!SYe(#9A޽-}"uȼ0|`~XLǟ-;֩TTϜ:v\~}zs3%g:DOexeddQԫWlUdJ!se!R@Z }z=Ͽ!Se(S6[ 5|M4ˋd֬];wf =i'Ҵqt:.b߁6}R)ٰy !AAN'@'OO MbKwoa; f uz}od,w|hw-b jYq֬]kO~ Zf=|x fF6l1b8#YK:u>ç ;~`iҸcbm+Ah]e(vG2Br?Bu~P@n]yitnnnv>&ΥPh?.|ѣ{w LZ5IIIa՚vL0G^KJaSG3rP mYwX,;eWo0zvDU]Κ_cΝl-h#ի˚N>1!Hч ˋV/ˎ] 0e:}FPZ?/ii,\L:q>zY3ٺm;_/^Bu1 {׮8j$+W OpP &;[džM6xvd7˘6y]KZmB5㳏rQV1;lQ4h@$^4 plN=K Gl{z[~LmȂE}۶h\][-[4gטpQ-uQe(v!2B?BAk湞ϰh20bIr]L8֭9c:[nңiՊSζm Ï+X::uK#1z @KaS!y_˘Q~uyPVDѪc-rp߸Q= !e^YYY9En]킭'vW?i;>qBBڹS}.6nˉ,UeR~-h4 <-[Qn]n3&bxX,Ã!/ o .`0JhޜX,LM&njoٞ$_&!.JC2b!v  www|0 8@V|LJPjB!&.y_Z7>' з-jj̄BvaS!883ùr׫G>mVEFhf<Ѻ5ǎKĥˢgD}#ΦjUI[ܽna`Ņеs'jԨJR,qE) 6 `{'L.'&ל{O[EcX5U*5mws3g>d0ζ'71 6;wҩ-JĤ$xs4EL\]ٰi '.JC2bGv 1^ܸi:99cR,KC.%Alo'rͷ4*N~ RRSmǧNSqqqݻa-&Ip>іTFMQ)Le!qZu¯Ӥq#ԩZƽ̮{7FLغ( )PBd%{Er+(ٌcR,Jޥݽhk(JQ7q:j6*^^Uoxyy.j~ʣ-ZBvm7g+K˫h׮ߠV^{) #}T>bd/^"r"3Nӓ=w?y"hdbpIٓcP`q89_xkDN ؟\;OztNԱc~9|||HJ"*ȼO>&ϘǡG $8(Y=1)Ns$*;wj9IF!y8iՊ5jp"4}(&)BBPd%{5ѽ;ie4T*Kҹ<|Ag݆|{L2CЦuk5m>,N孳Tu*yYf ~ޝ/v"}ٳD;Svm._~z6kƜɞ9ͳ=zIĝ;鶉RJ1Ŏ )CGm_;w2"33 1T}矈SNϜEpƿ2={<'INI//eԡ=t.%\FPЯw/vāCqrrVJ*?@jj Cӯw/.&$JŮ{^|+WA/a2@9ʕWu+ԡ=mиjصg//%ЦU+WfVHpV9k^;/^J Y3  Rl:Ukӆg3O= .%Ϣ+6nBVuqq˾uQRۅK96-yjn$'|J !>&F㇥,U{][x'ܳxWg=Ed6mTW%Bu:bmZc1_MErr2kf2@Ν=k&Ga4mNw-b jYq֬1yx'r?GVkZٌF6#F 'bp׋Խ[W^]:8K:u>ç ŵjdO޾\bJ6d0j$%%Uk֖.@ iI~=> 7nm*uZ֟ |XG  Ǥ/G1yxzvz-&6qsi/e(K?;1>YbS)73ckق? _}_ B|^jS/EVMv:G!0 gnm]|IMKcddeԉHOѣEқ=k&[mKW.fuf:r+~=DNzV!  )&M!aC>3g#F>(֭:^ӣv<-]F`F bKG>~LmȂE}۶h\]m7Դ4҉U(#'ZflٺCKҦU+fL6; v1u*l6mfAl۱,cIv-ii%8;;3wλ]L-b* !Ï+X::uK#1z @꫎Kl Pz5|+VB|`.;p֭#T@̶}1Lܼu e)ڄ#}Ǽ/p4O0obB*]L a-h4 <-[Qn]nK(J=jժ1yyjj̄B.!,W՗2tDi%ğR(~}z!~XqYpFBD">/]%!Ž@J}*TK5:x|Q(0rP/[M:oGUb;pps*͚6L9o8;;Hi{s'>bLsd[:BILJɉ7OyX2 Ne;/Ub}ARB)R(PZP(Ӷiot2SLAKmWqHu/Jض#)_2叨-}×zR'Y{y,ݺtf7O^^c"F](buaP*8j$G(PP(^7okbFT1fGaLf3j(}/^>a`cLf3vaFH{-lZ-g㛪?NfBF {Ȓ!\2ܸrE:u""KF Z+if-) xМ|9Ihߜjn{/`TUS"\ 7s/wA9ו`P؊y㡠xōݻ1g<,_mc0\Dy? n>{fVV %_@YdGz]y=~V+Q>"##;@η.W^`\S}]NPgrޯ@?Ri0 ύ _bZq\}G_`4}d̗"U`l_W~tfTRu˖=;EQ0嗞n7L4o$.5ueT ZVka!.XHFFqq+4ohݺ϶mZہR J_:yO q> F Fڵ|/l`BWʑ#GhPwhvZܧZBO6dХs3_*O`ߝ+KF@g<|1oݻ1≂/w뭤n݊czq-pc9=`徧ku'x+= V=z {<xlì۰2fH B(Bbb> O=b ep?= V=+K^@akܽIfy>닼7c|Gv~X j ;w1Ɍ1&r99|(?,[vg*Og 7Zz9BΝyѱ&N<3^fz<5~;>~׻~)5л7kyo"˪54nԈH獷#Xn^~}&&w/: `װ˟ϳ?# L}i0֬[ê0ͤ\$D%WSr\4X?W/z h '{ի?'*!>>ujf¤l3ӈc !.fmQ !ğYDx8os.|_DEQhެ)9 V&Q!rUT|ʮN*I︂U !eD!B(BbB!B(akOUA<|>+b/<3-c !>5Z6ls$6ԭ[C:B!F<{C_݁ !BC@aWϞ .`μ|V?k{A~A϶Z` ,6}CLq6_~ Uy7q\~q?p:9o(rrs|BR#j;b8+}BQPʕI?u !eZ9Si61G*yymYLDx8n#=Ȯݿdr {7̝糏{ؘXE#p|G?cyg2Y!?~_Fng[6mիWj*-Z4q:e킅dddWBL&S튢`0n3L4oħ/;vb0ڹlUf׽{N~?~̟խS>{QN2} !G <_%XT\ aAХ _;w5uҲysrڹ3w6?PV'%,,$Cv|g>s5mB8x0zFnikiѬ9n gQF ϜNZ?AkqnMtlߞ;nD П,ahZ,V+i<0f4 ԮUѝ;أ;~}n!/tZaɓ:}[n}?Umf˶4k҄o,dxttؑ>{[4yIRvӢY3}0w6NǖmرsmZ^":*)6a ^зw/WƲ>c=vHc0'5>ݿҭkn0wd4r24۷#uV [B!4@mѶ]B!_mۮ{# !BIB!! [B!!$aK!"$l !B-!B%BBB!BH–B!DIB!! [B!!$aK!"$l !B-!B%BBB!BH–B!DIB!! [B!!$aK!"$l !B-!B%BB~-G!݂jٮH-C}cbD~_U7WB;l(þy~ۖ` _ٯ(q VuO[q45? Ӷ׽u)ʍ)=95a<԰EpQq_;6{pWirÞ/\P=gD.EЙXc !" [KBQ[PQ(EpܕE{o5Ih?~\U[c-Žø#yknh8Xb^c !" ;GWU14X7v< UZ\ m1l60(Ey5){3ݟ?T:i1TwI@=y6zB!\ ; GzDOL=G@WuHtAZМպbQ-.x[=Ϡ+펩P| IDAT_G>>::Bqi-iA+E =/sX s5tԥhtlSϕdOS;p1\RT'v@2wa?lmvGwr=J/2rqM]8> 1m}岏W!jm:B!rvnߙܡ[C] gB–B!gg !BIB!! [B!!$aK!"$l !B-!B%BBB!BH–B!DIB!! [B!!$aK!"$l !B-!B%BBB!BH–B!DIB!! [B!!$aK!"a}$XƜO>K%BΟF5WO>}pD|,V}s煴sEGG민].A5lojB:B! [瓏SJ~ s}uBhZTT^~ŧMNN6n"̙s%B!կBaO[ֵ+yy+61tОM[.ʫ>58x<{E깒c !pتY>+930 ZBz=FF{2mҹg(f<6[ٜ/iݪ% g׮<7eT)Ӫe 9¼)3~2qnڅLf>}(**BQF -=o"re,V+wgq ~B!'{SF/]J֭>s<>9qԭh00QUG>=G_P:Pl߰7tk};999@e\q?Ǝ] s[2.ϺO|} ͚6J\m۴`ƫӻ@-1^o^[o;i~-7Yi<01c~ɱBqe, EEE4k҄6[y7LL$dvsߨ L~_h,_7 ]֪:'N"AоBٻtw5X,,_ɂ￯z"""p8a2TUe/,GGұC{ϐ}Yu !"ޠu˖חC_* a:^Wf?O?o'+;Z5kp]4kuWNGݿ?~N<%5j0>tUZ/5VLL y\.ˎsS۶חLxz2?~Y !"[)6oo7-7eعKP|ߠj}0 )5ôiՊ& 8l]H^n69B/IQQh4<1{j4 3rPRHjB!DThSOjM2#"f񶍎b!fe?_J^?^]5u`-,:~U%.2 4KNA\i駰;Fzt@9\jѬQQQdfedr_UBRB!*‹~1w=oAVhִ)/}Ũأ> M\\eyNGC$6O–-[/4%}h4TR0 m6f`ʼbj-9æͩt#"iec,β?2__&=/3ؘ2n'={CGKkٽg%B!ĕU{#wRJY8b=|79t#9r(_~5 ھ(^Y2?"jլIjp8/L:4k҄fMR䌗^YӦ4kڔx7⧟n"&:}L`>d՚dEN<>UUqJݺ{O뮣Ydg~#B+Cm^ARz5?v]fU4?=OgppuB!m*~"L&3NBB999{B!saKҲEsTU巃y ǎoVFN>͏V39tiB!?2B!m*>A^!B\-!B jQ ;ZVfGndR3OD_5Yzƌɼfb"fw~$X[3_kbY V%y2uZ_mOUG!GP7`v[aW[=W)fЀ,֦Lu]~_}CAUU;j g DBNP`hq]m\I7tLyz46.e=_*ྶw+Ա?mNΝ]a}!J (lobH s41rzrРI j^Kz3l|wQxCmB<%52x Dp: '̾@?i9|igPe_fى(d`/]35hos/րSsXNO◝XM=z ZgU6nGn,]:Rգ{wNQ4mڔ٧Ӟ{))e>n0NE(13xrg{0fHYj5ozǃdbv# e䰡|3ozt}۶_C|B!Y~>I ;.W1$\ܢ!Js|m'9aLe&߭2&-EϴM×Yvvzx%JKq5dTV? f]3^O+dJ>ΰ]On˥I̺_hzOu˖9zW(1=7oʸ%**VZ3/&drޞ5k)$]Fj۳G1/={sm4Ll}UV?Az nvEv;w Wfqs*|W Knb~mD[gc./\Dʆ:v!?~!qFVfz/.V5 x!qFvxD"t b [a G 8crO:X[PR-Ntч$l˶c(jl; ]LAm zʫiV,nMqpOS@a+::{ӿ__I^Oc=mb ʱ[Vᅬ [J|GznN>V{ዾ 0g^[v׈yrrsYBhӺ5RPUSO{ǴX,g8x>Lk%ϭLO>ha,^z B}IǢ 9.@Io>6[bv}%EU1'Ç)`(v9 <*%[wi{m.lDxv]NjӟdZ:+WawYa:ck˽1Mxy{VٛpཁyE~z,Gܼ<=ZmZZO JOORlLcݷ{s=קÇaϕD+BǯUS @݁#ͬU;o<FA٧;diʸ503^"#‰2/Zf'3hgˣd8뱜w\V(BүgÁdl6c4 [yo^=ob“Oxλ_L 7ѩCR6 +;uANWZA9rYnPqfpENB!W*p(Bz9/,nHNݣrn:MNArJIeXp7r֥"|'#^r;^9&_{kQ;vԏCya)[~A5Ld7ii彣ɄI/[c,V+NH5"fNgdTϋQmүOoЁ'NW_ǕKw BgRl.ڙØ[r'FPߨc; yz?9l(Ý߆Q>;F]ؔʙ39KTT֬aBrL!߇akqz wǙ/_=(,;/uEL=naLU*Iw~MD67 ^ˌ |is&O?aejoͣ2|:dX=o= ]lx e./Ieee'kTi0|!6&L ƏcO>Z \|k+WQZ5OAn"-KO>Uy?K]gks6nB!(h۶B;.iL[|2SKa? EQr' NB!(;mV JFZ<*}b Diwv!.ZB;v`;yc"fV^G&BRm!qF4 쳹s(2BU 8VˁyrSЪB*(B!.nmh"B!$l !BPPwbѲ2;ZV?u#/L .}+UH3fH}61~$X[3_kb}zP]cFdWsI^YoB}!%~O S`rM_W[=4}ʿiѢ99'OX>=wcOݺu&7/(׻Z/=4nԈw7bolG!+ j,ݣRvi+j'Xnҙi[KYϗ ?;t :uO۷ӵs Uf \.PB&ę)07N,)- T&뵤8wgʮuU[7?酜N#M p s9똓gvU6eBJqrJL-3u Z\5Tl;*庩G}yPX3I6nGn,]:pz|# L}b{Ǟ&!'# |ac*FX[tqCj&Nr~!]#ØVL[eOcgq Z&sV_Ϡ+iVr]*uLi&ۥ6?Q07&qzZ!SjFq zZE8po_. MZf֍$BbDڼe+{(Zj˯ ?ͼ ]3j$FtF!5$"L/> Ū&8$Bϕ|9%F&C}VFaN9=g􆭥T&b! [4 6ήBkDP۠%j[eSgSP؊)c V}*8h~A偪zo-B?Ö44XSzvW6 y7^lq2g SN6 bPFT5+@^C:Z?Qx=*[*g[<**`󨄗o=;u~%cN;UUwiVyTNֿ r~*⌓27V) CgCG]pc9,nQ:\gn^.1ї^719Son7kްq:t eG!e gzu!J^NE[%`CkSP@+G6sxu6ȃ{Ğ4x끃i06nNq㽏{ #3 &_v?B!(˯\31ONQ~Lm>zAll Nfɲ}%jZӞ{))Ga"=%đGeΊ*/,a)0ҍ`*x>\қ^u |oϿrt܉ [ӧ-s9q$ӣ{+9Gv1னVAX&Y–B22KnAc]Zʻ &yѳ5 d/\`>Kg:vhSyo.e=_*^ɱx</B#u!q&t ͶĤ#aqK@&U20z-En>ΰݙuobh^ٻ62x Dp: '̾@?i9|igP[DhW2KVɷ IDATgjkв_ _eyTᅆUf2znуt+BYUNO◝XԱztI:j4`ZywYBB|#_D#!U>a+L0hu Z3z\KsKJIS^1yvLUM|mgW5"mВ]SUy5͊ŭ3b 8lEEF;߹N[ĄIxwyq|enس4DDwy_Boחh׭cؽgO:W>rԻvsi _>C~a_{-I}ne2A /f^ *WR&rJ'<IoλfU>N8ܜrzYd(8T0^1С$Pa 5ݣNK+Vl߁X8]."#}G͜ȨP( ۶_\ߡO˯f+sBu fsSo,?̶:u}7ZLjոCvew8~F?O)N'?3yϦeîݿzd9P-PUÇӪeKﶨ(֩S>BL&SM_ٟsϢ0Ɍ~~XBM=A~Nky0ĎB#_ !lu1 ryH+9[EdJt UZ72l\cВkeT'ldCX#l]`s+Ѩϟ|M]Xݺv-}M8].f<?- iѬI}/mץзvLȜ8Fi:xl5A;wW}Yr(c]HlL 8D{ .XaC6/r1u¦TΜɡ\Xf fB!~~9j5gbR4 :hc| sT11UM d$f 7l,(bz-3*qoϙX?5#q 63]~b*,>{|-3.>ʰ]vg8M&2A^^>_̝˭n11:л4<BR>Đɢ'++?)]*cء񓏖7`UTVA}@[FR'}ο<ɋӟ%d򮳵9u S\ !SSE۶qIXfgL&{Zz ѥ(—?a7^pBB۶f^CR%#[-Nl> KrbWBnywnwDͬZ.M!_a˥BChgs1P~e3p\jтj9p ONxrB!eD!Bqq;mo!BQ1B!BH3j$>͊ŋujI^5Gj;Z8B!_+l3h@Ͽ kSӣ{*Gj b?V-$l !ۈХ3;)ؔ |9𢡄xy!_!Pjoj44iwɲQͧ1z^ţbq ,aPJ0+@0>ʹkv5 1'12 l,; ,8t<oehEnfgqeA~~*|mS4[']NN=HKO m pa=wg꤉ 5q?JMZλMI!!>>XRC_=9ÌW_i5pVZ[`2XtUÆ2rP*7tLmh XB?3V0ޫEr~=mAj&]zF-/׎,;3򋨤S'\ꚹO8`sQߨKuV,;XxiL62vzx%JKq5dTUc7ФefHҊFCN3[ѣ,]Fٷ/>/f꤉,YgG5>)3+@0瓏ݷg;b8_̝ǯ{rh؀V}oͻ@vصu)v6`T- kS4}oŎ]p)6vZ!W## &]?Z  #aiVyE=DqCqG XUr? N˶c(jl; ]LAm T&b>a˩fVTPb (lEGGѷwoKtT41q޳ML4yyE,q% _wQUǿgC % `AbW]WQ\qݵw]tW]]źUQA"EE!j 2B !ιwsι{<hZyإߋ|={(#?Im]kׯgQ15֣(qgǃlo} 7V `isf ,ں}BV@ɖN#Buʑ⦮&*NE݃ZAՍô8 ZxT`T7e`v2frh٫6Bȡ *e AF-*Z\)px1+b4QgS=BqI)|Wl6("Rb0j~ T+kQYBTZZJ訠ܳw/{zLf\w5<-VBE@VFFB%߯:>T;58#oF:xȌP.USVhk~bkdpU@{(Y- Xba̓ӫ0?1裺GezfFZu#rL&#aaaV:"#۬;~W8rUuu&,4VbyV><xŠ7]@XX!!&.탄BST@ɖţQ2+#PjYV`YPY6/ ջh}I/1k\;4V QUm[\6UU!u3w)S=s_{ l%߲yV^_п_6qfn_xe,ł&"<0+*U/QTNOg 7f E|OYb%v BST@ɖKL>F *lVg"^t; [|WiT͇^K$I GV1"T^[!$CI0-Amw8zR^aCpɬ*߭\ED.5/Fln_;yG 1ڴy 6o9B!NQ*@~8aFO6PN8JG'B\oYbBw.237e2aaZM!8mHv>t(]<FCy_[B!FB!,;22P]L!B$[B!Hlu ^uɓ˖񧟵n Ş{e>H!'I:c>СC࣏)*.oL>{Rxo66[߇L!Or 3{|ؿϺ?'mW}=ztv7K:2L!BʂJFXB?oE{\Ώ!$՘ 7iV1/kIgM7c 7r8R!' &ܕ׈"4*řVW݋%٬:Ίг0UBmڎIx$4T|[ۺi QfɖKQxĂ٣ї<Ή3lEFERW_IFAQF !PSP,'ЫT8vjohZYV76Ҭ0-Iy1t*020WuTGA9UPW!qL2WS[-{ ^G#j'mQXj1mݎ#Bq* ( ըШ`~B$UR8b^曙g!!4&=[B!H-!BN$ɖB!D'dK!I%Bщ$B!Dl !Bt"IB!:$[B!H-!BN$ɖB!D'dK!I%Bщ$B!Dl !Bt"IB!:$[B!H-!BN$ɖB!D'dK!i)nJ{tÉ ׻ӧF/ !BY@֑6:n\uetW_q9 lXB!8=[sGsv5@B!8ݳ( la Avx .=Ą׳fz\6vzQg7son1V=]K/s֙:x0N+\}@ӌ1?Çѧwov񧟡Vtt47;ƎhϾ+VJk%6&Bv^~5R!$׮ j cPYUO\yw6eG֮]X,JJJڵ]Rϑn׏x . ݁aT\uenjF#/S?HGTd$Ə *aCkl v2!te4P/OKɄ7WTm$=,l6{MII)C{|=kDFD"T IDAT>t(~=PW_ϒoso iq2~3(BO5~aC?qYjCɉNm6'0Pq\x v8*+/??cz2:=Y3.b֌({易պBqjk%.cƴi ߏi\_}MF\F ֞zX}u%ķ>*ж˨33ӻ7󮻖Ǟq(BS _pV> <,Mk!,,,zOz2w`ѿ_?"Ù>:x0TVUtr^s"BSKzVY˭7n}晬^>/7\7 nq{Xm˖sɬYt|LXXYvdۺ8o9ͥ:JL!B^ڝl9N}+.k3gԳ.ܼ<6mOz6 37t#cFc097&[ILJM]]_,w{?XBqjQB!igGFFl !BI%Bщ$B!Dl !Bt"IB!:Q~X՗eg, Glܼݍt<_8(- ~4 ]Ï?;Ƌ> h-k|* ul"t= VuJ<%B!CPZ~=˾[Ixx3Mο;n8*^|۟_HZNQX,F@6@"BSex:Jp%B_PVqI' ?0cڴv%[=ztv7K>Vx^NmCmⶃZ˅/\٩tK!8f]9|8lFq;bXxWj3__VYǕ_``zz=7\7gOKt4E|XbEP13,ΙCnݰZ-d3/xoPB׮|ֆ= KaWc #>F=eA[r}oD2l°3;aYa'v`rWZaߧ @m&Gpu-|/sXRϞ L={)(,xBڝlt&sNyoOa&G$󮧦e}O>IgM7c 7rHwK-矯ݻ*AӫW/zs?ѧwox!2wdͺgСˣWr2G@JNJ{/W_%:*c2J*j5j>|c :RAė3AaD2I {?Byc4ghMd ? uc!/ˤgP[Xp<]:;H>Tj9=TnƘ8Ү3uj]u[ 綨#cʫl !i"dKb211}z>`N?qf¸ GCc# ׃PV^ޢ@9+kќܼQ^~5v;d#I7f =8~h4׷oŗ~v]0/f0Tǎe_׳_JAQhK7? WlهM~BPx=!:eku==3X0 5\9** .8ڊmX{&\*ߤx@1vAha2ξd#*{-%-ڬlja.>8#dZNmݘ>>c0Oy.tk}OYyҖaX?}V>ZT÷ΔÃx{4YÇF/hqB!N=Ǖle}KY{2w{}vcɲeA{2cٳ?4r 0MϐkWP3ڿ}?A[ yv( >ŴY}m_9@Pey6Q]B~xmeDSS mvw@[3an>X| .4lY/W@KRaqG {?i7:񉣎my-! MOo:= [ѹw\*f/ư vq!lcvkۅq;'##NNŞ-oH< * }2^DSD&$!=[B!dGFFo B!&ɖB!D'jל-ŋ˽, nˠso$<. Vz/gbSع?Sz$e_bp-#Nt8aCδ3X':,A!*2ʪjV~Ӱ!靚`֜萄hW5+)--D#$JTb\+x\s'srfxoVHhI=QQYN{\C%Aivkp:~=U!DDVsowF<͘"b5+;=!N_͂ysѢVϿs_D }0BJtdzC"4ec*"ws9:SD,N[#~'bx%&^lqՏڥ߾Je6 )7PWzϟ`䬅tFn@XlOJZ&.e`mox߽09Kl%2܍٪aHaOMOst?\Qau ^ J|]WTp6 w?߽F nQvd-#v5ɋ&sVz-CqT{ɬԅ..zS4Sho~T*~V O&6ډŪa~('Ef{y$9oԲfKYwN51Q.y*73ˍyj?jwqv%)mdeF߮-׬`ܘ1lظDb한ԃ cGNNlL\.?q 2N{~#ѯOzs0b[td<1ֿ/B~ڶsh2(FݗMN^2T, cF >w$ FĀI״(2r*Ϛ1<Km)ZaSrAӽ5ԕ-be q9QbP4T73nĦ %,;b7<3}9EYkyP~ ]+ߢ?jzb#w?׏y HbJb /ϳ;?ZU1*z&ر4~ %*̈́u-JNs㥾oJpJ HBnq.Xʍ]-2a{+1ϑM,|?6V!.N5X y?4 ;敏Xkϡ2˵3JQDB wY^Oj9E&eFl`PXbuʔ5\~Aa!4 :/ O%Dz7'F-Kƶhk֔ ͏ۢ}T& q8*._{HWa *J* Z@EwbPru\֠>fBMծ!+y.%} ) -̦(2"p{|}+!Fn.-RzX)o'όۭE)6.(4 )~'[uuu 8D!:PaqI}j2wnAiQkO=bnzvƦqqx<uaƎ:w!NA'[bFPvsm2:Zm6'l8z|WoW5=&ʅQEVxza6#Z|8ʪcr0߫{(Nd+5:$ؗR3pZa:55iKQEh'}}2'r~͏ y#{3榲FϷcy=(;/SMx4]0Y%s `o>+&rUaDTb{9gL .$_8cO2ݻu#@欜^/U5DEF'a j4*td[B }4iR1ușwb.":z&&]KY(^Pܚ-o'q,ueޟ3ILtJzkkYTT44Zk^> BzZY]%V-ރ٪!,CVvK׵q8ռe7-W)3KM~}䗘בSdo}( dO[julu<' U;B ZuZ>\ ppq1nh_2xo pq{$}ו!̜^˂"zZsM1?/ ۖSJAg%`O%hO M* S|^P&a eծaSXc f-:BN }>2# ?GiϡI=<h^զ!}PLFQ|zxx*U{^'CpU;NnW쿦!Vc'~*G3qh"owTTV  c¸Qs ]( 2;Xf̥L8{QR#dA /PQQ<9Q q~͵,; - ~5@Btl[\4mY̞17-:ѡ!@Jv͋/Lb׮mB$kWkl۾D"kQ!BÈB!"0l !BtvNh4tbrd1Eħ0q;mwWW"bt%]ϩٻ3jH/+[.,:ΰ!Cxg̙X4-}zZ;vQ]SlؐNMh0P]]UkNtHBgkΕWRZZVGH1>x=nJP_vS-;wr0'k9ѡQTö,:S>{. Jά=,[[~:!tϖZ/ͷx1Enk 2Wvz{g_`|\Hi+fsJ Eť\UZɠ)sUۿkCΡ)"bq)?7}[eČ?Դ^Wo͆](]Tlۀ<& IDAT ruwՑҥGKca=+&kś2J>N*sZ֬H}5GŐ~fL/op7f;#=i07=1N^|7kόӥKfզ.-ڟ=;`io9P*\?'Tbհ??|ГgO*HsPߨe͖.8jb\Ta!n2_}DžgV̻0zMD_|7gWFVvw/*k_״zyƍÆDYp8ZmvaI=0v_/]tr?]>dQQ8.ed:G_T+.h1V=OLt4EF!hFaȠ4c2ih4w_69yMKUsR 8P\.7l3NWP8Y=8f( isGL`qI tM2)#9cԖI6!/c)n [}q #&ˉ4]Ǵp8՜7Y+0[yF-C360fh=P獯Z_֫ b!;?GBʣ4p58]j{OesBddEP^m`:e^s HuPPbdr<|yǾyQeLfo~֣:)Tciȟo8\ƌ}C')^OHںv1a( JXr [īzQbP4T73nĦ %,;b7| 4Lo_NQv"`ʷ9d2Ϻ_%|>Z@uoΥ[{پ7P\n[<#PkXllv5ZB|gǓx棯7.Xf J(-+od mu`[fpBJNbcb(.n=,;.}Q7Kx=nzؿ0<.suǿ}*Jt'Zl<_fԅ=>ʔ5\~Aa!4 :/?ՋWa *J* 5?E㶨 c&o[p} ) -̦(2"p:)VBn7]Z?N}MS[y] gVܢ-[3Di8I >٪c': q-{8ζ8j5q1dlEeVG؞z7łc1'xa@۪ohDR1fHr i֮-ꇎFPvsKuF_2h37F?RAVxza6#[-ӵmеY˭bˆ:/ӑ\[@oy&n9D=Y+5j=w?ߏR#aDU5 Mh2|@#&M-ʃ/ m]@d<N'zɝ__tT$Ǧs^K.bNER1tpCx_Ou_^mA`rϋ}$}PWA$+fk4J-fu=|7ʅgV AoMV'<9cjp(PPflQ-)ILp*)ɭO2ݻu# oNeҙhl49_륲[ss@FbQrٗ}Նz'&Mj1i*oq1r杘 efM/ZǷc),3 >nHv%! 8]jX;GnN29&BC<<xiVA \0EϺ8{T-5kzr ?[ϻ#xy:Ӑ  cu+M]j45GZ[t**~ݏ1a(ƍ9qwV7s`.fı݋Ҧ/UWWOȨ8$vgæTVU' PuK/@EEO>LD%N)ojb4 - N57B;22ڷ6oEӦ1 ehщE!i߳53Nt,B!?Ď qg+NtLB!CWkQ!B&[?ٴi'}\r g} ޡ㡇#ڻ BEu͟o(e|㓟[𰋆y N={Fy &:0j?r9 ?<x^S~sQ|<3s;Qsz'| ѷo_wNvv6/|߿?W\q% 駟gOeRSS۹{Jtr`,]ĿK/rck~=;;ٳ8L'ʫgAL[ż;< ~~v.\ѣG8ziiidduj$I5C;'`ժw"W5jիWsI{]IIIqH{aFL{i6mސL&#++|wE֔ luؑ8233<.wҥKl۶'|O:EZZ3f <vM[aϦSgrk/-bT%^2o+JƘ׻8Ȱcq.QFS:l)-'ܦCKFrDt0QQ-e/n bq&sf?35YHUqotr:jq-1x`Ə@HH%%%|NY[9y j[3:_5z@ŽGRU#C)`$.-]t 4d䮾|q܏J r˝:֑ێ>;f-hX+upÑqS%Ct7;.Ax8 KƵBJh.^ڵk_ҥ3))9z~d2at:Oäpn$11F&^OӟĪUP((J)KzLkl1a&L'ʕ+mj/cǎaצ&[*cZVknI\-uFh4߉䦛þ}ߒaKyfkcסP(PFC`2Ä7PU#ѹ B/c^`~}F '.Y9Nƨj|LĖ^Vv: @9J 58}_ vuxU7%Z"j17H@ٟ'ǖe&c}c^f@5:mLu3\omF&6 ^ >&J`#G٥VٴG.؜RQ]3qFWlџn &% S`#LS(Qc6ZN]aSynua.z:xW8m#ϓUb ssj9ჱVnW3ر$1z &7andF:Jq펝9sc+(NW?Bň#$? ݼwSTS]]]DtFee%11%[:t{ >l޼ٮ ;Q(.,cղrkùСL2`;~-L<ɓqY;nj]ǠA5kvmk+U5JYϯB(7DWۙuu y`m[nU^x4y:g"3*m=Ǫ*s4X+LID؛\V7;AML1Zo\ԧf ۹<ѐ>aQjGYCak咵n_QærEs*n]?30{3o0 ]BKu0T\oHj >>>hZcZJL&l!d2|}}¼9J_b6ۇ3gPa46 (J;-٢4]oood2&MbĉvID`` EE;,cղᲳe0 7$s)h]׳sN7>4 /_w2~ ӧO7v+N8+l]7')*e:غO5:kRRf6%"Rmӝ}sg4=S*ɚOiiWTdx+-o=6?/32~ӵQU#oPl#jG\m2S x6O؛]:,S1>J B\hNzݞ5'Pm"_ӣxkk[Z:c۬oaӷ8j:xEBBcv6@xx8AA=z //!s+N0w;XISG빵n+b'vy^k -{%58{[xV|.hdܹN:ѿ֭[۪<ϟ?O>77#IyGPcǎڥ e:,]w^<2d^^^<$͗_~٪-Ӝ-a˖/xG1MȣfŊviϝ`dJ^^v7(ciig>ٳgڵkٷoaçƲt2 u_ l^f̤_Q΁@5&YIGQ-$EaeB9(q8Ӈ7FP e|̬:Fj\~UQ-WS3k /C*QVZ.Ql{pUu%/n]^Ԫ{=%C}U VX-ĭtXtCڏE;h7ӈFrT鸫of Vn2:~ uZĉY=z$pBzڵ}ֵΞ=˼y3f ;wh#&gHMMER8!I ,`t҅H𧻓l 9Ǖϋ YMBBBX!"""XbE ƢVIM=֪-Ӝ-!==_p|p>GBB"ǎ9~d߾o:nz/;;Xff&[neʔ,] \'Mے<к! k.?u *޳:g,Ξ=VQ .љﱐ ^zmeڴitoަVq-ڊ'ʘ;pldggc4g@ fm 4 WKD||wv&92df˿7j֖nP@ +.^ƍ n{r}V[\liD@ P7[&@ 놞7̉Fy)@ u@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ `io!@ WIJRQIENDB`helpful-0.21/screenshots/helpful_aliases.png000066400000000000000000000061321446151713400213110ustar00rootroot00000000000000PNG  IHDRHqsBIT|dtEXtSoftwaregnome-screenshot> IDATx{xS9i %P(-Ȕ=n&jq(.*:nq|7'q6':sh k?emiiy>pr~99y$gi $ l($(@!D I% PH*QBRJT /eTl ϭM%HM$IeҬV<^/sEQ1}:Z(̸s3[c'0/74h;Gks;/`MT+`s"OnBI#TM7kuU?ELY8Nֿ_e9Ήcٳxoh--'Yr늈.gŌS@͆jN~:;;-˖2g5؇ vsh-O)Or]7.dL;:|g])0=KsTU7F(9a$4P[[Tg̤L.w~$$f͜=+bn\t޼ ʎjZ[[TI1-fr ˤqRRRẅ|߉)0]GŠtF )ϋ`{U۫\V#k{ٲmN5[˕S@eEhbrɧf-df:BM&-rpQga)Po!1->ZOSFf^=aZm4'Xy7mώA.3@0m+nrV Po!1-OƔRf3.?MDe~ǔ`0=*ɥ,7ro~{ǎfzJ'c#$FxTe6pN$Ij_U%Ix8}77mgaph->4s?neK_!1λ;NCL)+餾g8Lȑ<أ445r7(^=#bdw+惤sqIAo8) ay!ۭ%fJEW5$uzuO[v=@eC<…KJݝ;*d 5~\SVH]+*#Vp=TS{-@c?k 6l@5i?ȆE EdSñw7?!BT+`w\J۶T? ؤ.jHIx_h$Ŏj8F{#$\ThhĺQۖj>\5p¼i +z= \xʼn9|g\>߉{`C `/ʠH`Uڶw|>ɠ"b/;nT ᗱT=c< TCJ}@j[W+2i@V؉h!λ~'+TPH*QBR*aaKGt交Vԭwb{m>&lLa9̞ߢ%$%O``5&~ %-n%eۏhMUg ( t]1|c: j5uE5&=Uo 0.D :&a r{-Jz>X!jaŧ3`~Bkm24kr_t1o}lڲ ֍]g0IsŃ3KQڥ<̜c8Vy*vRhä}qKp]$! I( RMyTS:,*$F:16nF5ҝ?{зX{/hHTK K4Ľ#Z 1ռsK]}vDGGj'5=Gij!! 3/Oc޳s uoJkm49-ظ)"jӊ3CvEGv}› R d]"YWk;3!I2RB\ P FwQiXv>Z>uQ3{πނq##G5rZh(X?jHz~_ C˶#$}щ!V@!.nGT($(@!D I% PH*QBRJTf?nIENDB`helpful-0.21/screenshots/helpful_bindings.png000066400000000000000000000266461446151713400215010ustar00rootroot00000000000000PNG  IHDR[[sBIT|dtEXtSoftwaregnome-screenshot> IDATxw|SUh6tR ʐ=Dp!<(Q",Qp #e)CdȮ([V=&͸?҆3iK >lXb}edeg7nԈ>[`g'oA_(NH@=UK-r5J%AA7;g9t0﾿) ֿ+%ܗ pwp+֪ 2A3}z3_?j={v&=S&su>7񚯿F i23gV%;;ݺ2)211 1u͟G6i݊ IÇ P:X"]^' Tk9uZ-lBN=L7 ._'O7ǞM&j"~}jШAl6;S7RDEG{8%%SC˥10MXVkw/~ʱmkSi_;<≮ISoJg^L{o|@fM ]۶[!='*p}yxxЫGwgӏ[h狾⍙yq|WoAV"$''MҶMk6Sb,xf Ioom@Gxe kbXhؠth_zN9@ll,>ԯWL;f6i+f0^^^L9e+,Ӻ]s_7~&ړLbbRA[׈?ڴjE޽>d0G;7U*kwotZ>rĤ$j޶mhެ&S{+SyE09;Ιsܪ'l;KjHJJv KHH,SԴT, I㊕թMvۻ}{".>oիMA|n%pyz=уV-ZЪe 8IFf`?s=b_oرs<S&N@!I(I\t[Ӵ=n'⤥r<{W\lx{P(lY3 {e@>4gİ!y[ p=֍8v !d^O^=e}|>d0z鐚5ؾsgϿzaa]_ׯaeӨA~ ?M&Z-]:wg5Z6o7 l޶OR# % ZecUt} m[yfVZaCd??^KQ轼$*:אѣ&sвJH͚L&;YyӦ4o֌g4o֌͚>S>rOO>>|ŗTԾ;vڍ9njM7 2ksihwsXa#/AnRv*uj,Z|MHHM&N'+sF׋˄3CUqt ­Pt̛; jŋ" 31 dG~m_ ݧRUȲ x;W8>ڡj܈c,]CAnJA2 AbD, U®ؗp]i9Qzfz@b"_7q^Կo9v-|emwZƼSiV8|dc0u$BCB[pݕJ%;޽N6}Cޛ[tG<{Y-xR2~?__Μ=˘ 8mګ(_Z W-r ۇvDT*&Mwq+x(3h3թÄ):Th(Εve˗1 ݩm oq#鹂kƗޫBƍ@>Z)G:nm*,'mm2ʊnxOb}+фJy|ʬ #"FƾKdKn gl6#mwj}Q /X^BCkaXJ}nm\N/C%$#Mu*6f^tk}5#CIl%l*rCuK2Ilmu*^ SJ+KVƀ}8v$cb곅gz7In]ټ7>bum[F^>~Wvl8n,4j{x'h4c'.T&ͬo1m,˰xj@6oW~8+ _CӬieڟuV:t̬2r'Y > dFvXq#/E_ilX6bF ulYѺwgD||[aCҹ~~\~Uk~bt:=8(Q<ݳ%|;iAwW~~|Zb e⸱xoM黥\R"al'gs`aX*~Oqkgx{0IFvgѱٵ[e~OYW쫙( Fkęmc "SŌP=I?݋U |}ЦU+ٲ7L6ldgv} 'N`$&&ѲysbԩʨINIaoԫWz/WhoɓS1skؠ! ~D:u5b8 ޾rݰ:$$&HMOQL?T:TYӧy6>[%jazbOd݆Z渋k y8ڟ?YyCM¦͛!oߞ^!#3={\au0i]_|/? :LboܨS`4))զ=f, F?Gu?Bjْ?.Rt4h߾[ow8t8 /q琺sUNbhfKy>d0K-|d$>4 o=~Uk鑎9#G8u'TE{˥D<8@δ?',hZx>W9ea5{_T :%bBbjt76va%D%ĖԛgfTtDM=ۻ>>޳qL4nu7i<6m̙?eBk"11Q#s%f}Y9^{Yf0'$piڶisKC[oC|Bvh(OH;vvp1iܘ.;m~RTQ/L{O'6UƽɆ_~[HIMu9쫃XV233{?Μ;LJ/Pğ4_s$WʸQC]Ӱ=.VɣPHk F\\|ݚŵ'>!l#e<СeϾy/],VqqוUi1WU./KЯ{w\j8w>ܿ쐞AzFiii۫JMHbc )/S$s2͌:=wdfW_3}5h$ ,$WC*$TwXVY˶wYtW|VV YȲhDաVhެ)}`'O1فj17ł}("Va*ZBy\MFc6bbb?ǎ{xxx~.;QF *J؁0y ٳ9{ϾnvwϞ 2Y^Ywڢ45dS.e=VWD_B)ٶwΜ;Ծy*x{{;uF_{ d*OlqmZEV-1r Rg,y.Wg~>v!]Q%HDݰ0IID_~*KeWy)%YGS[]WJx]ņV! oSs3L񤇟b ɶc[?vL%1`4zZM_Y6t^6JO/OJ% aCoH$ՉqG&P)5nޭcLt:' dx/[|r;<|l63j0|} oDIԑc29uEĝ(ηeddhP*XVʸ"E;χj|B,EJ>>d0Mn!a %2daZY+ #!1嘬V+HoV)`njz}Րe4ΝXrmsh{/+xHDvݳtkYz ۶^aQ*-UszZjM&GqM%vݻƎyq>>Vq⎫{ǧh~/222CJLE՘o|E0 -h}UZ ,6µEW{:B{jNg;j{D2Sػx*$x9] x\a4شy36oU ۇߚErJ o7fΜ^XW*s,ݼ&IrR|_HH5-[6իf$IBђ~{KѢYS.\tMa!1#ǎJky Yt"9>6mZ;=׮mGFU&OzFu넕!+o'8V֪S^8v# C:Ey(M]NmȲLTT ;jԩ]KѕGQn*+2D#oRY0ڊ-5\2ZI؈ɱ'eF>W^N7ӱ^*lYP㳩QO;רe샳e$V E1g%'NrIӷW/+>fаA5lYo=rݻv%a_D sޜUS]/ݑcGD>ĄcP9)7;vb/v)-|N`D)ddd8]])L`ߡ͛5cܷwL'Oѳ{wxq\&991O'y/RҸ}{jȱUz|˝SWO$aM}5kr)*ڡlޜ~ǩFBi d1^pPImZZ̷cKL2{'2G@SD\E.%_RLz(x6@PɞdhbJJFM{su55tk2i4l+2rS[ ,I|\30+ԋMM|ɶɬK6n)bTzWƏCquޞ7Sػ;'OQʮ~/f͙sÆdr=&kv}dQv1uݩڴo6u罗d17Pk?V,[eZQ67ciB"Al,V SzaAq-}4m[)9rfR_j{o:$A--2R18@BF_L/t+ BÆҵK YYܽ_]2uM P1nd  K$bAABύ} oڕ+ vP/hU Pi**/6l v邟/7ؼu+׬-WwK< BUX"~R:j fK/\ *293ߤeb%׮_ax8]:ww# ]%(6-ZU1**G~;獙+pٻ.,[< B_18@JIFT$m̼ 8}5#CIl%\~t .'YMLX  ﾿$\Qt҅-[xgZ4kFfM] -;rX, ;9ea5\{shS18P4ߴZ!15:f0AZt[RMlIyiNEw_H/yy8,Wxe\ĤenZQ<ٿK8} ?\^zA%NƔ %Xl\0~IsS43"yk&c D fkH&YF#I `j TH6T,TZBoXLwڟxOfYDx$I"<>۶owlOMK#2A_Tj"PKfq+ [Ri1,6 y ֝o_DYfr'=4|cLؚ)]KRHMKǧrݺ>I8۰ѩ,ص͛seZػ"{yVHp+#ӽ B32f|TI[)[DsUƧ@Q)0d^eU2jX|y5,܁C<8& ^Q#GpIF ž}^"40[,xXϴ^_eq *u ϶p^xW%-:Ylt_tsnFIfz2Zɑe$@wTHrוx\cnBjtzS8 I\|IHM$w_Ȳ̥Khݪ9oooꆅ P~ʠJ+datV"B/D*08R-qf9#t7*3@!Z,.tVI[TL<.ۑ[zhW7݌Rbfm=a ,IDAT% f ,XR<| -5$h1Mnv{'xmsIFdONNC$IT*^~yr*tA|\J$lv堐 RMV|U l$/eX~O-Il6K=!?#5hfyo'BWky_áL3r[0݇FsK ۳g?p?{pG}WZ</E!IKNBA\| Se%`s?&d;n)l$bZSAf ­}ĵ<r8LMo攊J5yc=~DGPMg]U Ev^*hQHp6nE-[T~(J"/\` $ ZeAbAAM$bAABVm===elH2I)y5:iNؗp}]i9}g^g]T߻LK[މ<<<6x]tϗqql޺kVuhb?__Μ=˘ "AmͶJ9eekjNX.d _RR:j foaBE3MZl+Vruӥs"7iܘ!䣅r|$eXZs['/ndUu6K-r[BJQF2䑇yclFDIRVZ]őUZX,6kU" \JoSW\~2N2yDW0=Ϧ23ԋV*2l2ݹ}5#CIl%|?]dLs]j&NIEfyk$1%ld -e_ R4ݾc-Mn\T״\$#nJﳩ4)n5brK.<0=M@ұ#̭gmYtfvm=V%gΧ^TW^/{;=㲩Q2$ {j&2BV<ؘl"&L ߄{G?Ć|GǙm8_z+26V&UCdždd13ԋ%ěm.~^ u\6,NɶԛP1(3x?@Ez,2ܝaq7l鳮]ϜA-Y2.EGS/,۹7kʵ2r gά7׭\cKYf-#:rfsů-Bq9e1223?ăuEnJ6*wyh9ea5YT :%]Y6Ncu3Z t@#AR3ؑ[ 3y+,@L}?پD\y:v%fWJ2SH$#',LExͥ<(@ˎG?~w_1oI5%ffTtpJ|CjZ[e<СeϾy/J/PX,>+ mg@~n% 32HKKYF\qP\ ud#3ߙl e,붩? :iKj xٞ3JIMKǧrݺ>I7~N bx{{,SfrΝRۻii=AˉدZ_l_c"?>~X}T 63U^Y*;.[;bʞ4*+Wc#̈ 7M]^ҎsU,ۓm~Wv}5,f}.fm'|y5,܁C<8&&i{FF~~HTd76nA_r^|݄U\4YME9e^z5-.axZ%Q&k2-h]|<^_Ե^UƳnXNU՘,6,DI,F u7e47WfS@J3rOD/u9ܱk7!5k@N{yz:=NIIINۏ?FALJtz,׈CkP#0MVD_~ pgq8&n5gPq_ o_3|%^5fz;LjjZ{LTޘegkMe?y {O'=YW3]CG?-f+sFLL3/]J`1뒝`$l|P^/г^5}sj{YP/ VǓْj)!^x+%V&y/Wc&B ש~KqU1Ϲf`VmYl"b#L|vYCY1- L^=%>!nv;Ysܰa<ٯ/~\e5ns9N:bZYrnv= y\Z|.հ`DCsxM<А&:C vS\P⧒jj{Ls) Kcu`lI$&A;K]ӂ  ʠ  XAHĂ PD"A*$  T!A D, UH$bAAB" BIENDB`helpful-0.21/screenshots/helpful_compare.png000066400000000000000000002236601446151713400213250ustar00rootroot00000000000000PNG  IHDR6sBIT|dtEXtSoftwaregnome-screenshot> IDATxwxf7H酎4E4A@""JQDX'H! {"% E#R @z#%m7Y䁝{Ι;eO((|w)!Bo Ȇiggg֬YC-DPTZB!rhZrss W^ܼypVW>cfΜZFѠh*)l!BQ033CT2qDΝ[X>cf̘[h4hʋX!BT:VXl߾=YYY*mչvB!xXT*ݹqf˗/R!B*??_~(xF{u+7*!Bյ`$ʪB!$R^#B! Q7UrB!$IB!0$B!hD !B*;!l!!l۱+ܸqWxzxҠ~=w邃~^^uOsvNC>NƍpJAx$RXcڬY=wW6iLU''n <"!!q_͞Eu*;\ܜprrP)I"ظ8EfF&NH튼Cz. W,ֶBEhDC̘c/iiLw}ubchӺgaԸw?$x|ToLj1c+ }C 7 -I¤##Xa#6l+ק<9Jrr ^׬ @FFN}lڲ+W ==WhӺUn~_Nлg,,,~{ѭ1dggӬIE7Y??͉OHJDiڸ1VVY@fJ\wI"x|̱K R0$BgniJýy S?VSv2L9rf̤a1l_pJՕU˗/KQ( %YYY%WjjZ(Jl177z[W33Je*U򒔧߅ݸyc2UFo\jۖ{"&$6ncccS6CDh ]YZػ?M1 S>76DFEqY\WZL ^)8yn̂p4Zt4%EjZ>zι ϯr3g BG'ȅc"U}4BmH¤|}i| dҧSiؠ3㳩jZj.^ #84TWGvQcxdffboo'Ctw''7 ]ɱ X[Yqq>.uĖmdxyzuNvCҍ1nHwv~7{PNy)ٌafJ%_O4l@T*%kw!ģRXݨo1]\%BܴO?ᇟ~PpG?;ԣ[W._CqS>/q^z5gN/jƄqcGsѣa N.-oaP[+`D Z'ivvi6jW lmlʜؗ>bh4|6s6{~-1_ΜAm(NjK˜\|}|hߴuq tvvvXZX E=L4Z-7o|]mefoa8tV/<@l\vbޗ_H#89:r8$Hr3#"p(6Uk2t`ں7qvzh| ]`pt#SZR=L`eeJ,׾0c@߷Xb%-}JɲbԭS[WpSR@֮￧K0Z-!G2m$4n@jԯW8~? B3*,Mr /B,G8J6ԯ*T/UvN -}qo?'xKwlڄӦVxk[zBJ~oÉS-ҳ7uNHڵ;r85db㻻X*Aڵ{x,Xמy8ȵjpcQԮ^Dj'zvV>(I[K));_ {fݟt.\L:/E}Ŭ2bgˈv?~[Apw+kU~_}#[wbێԩ]caKҸlтEKԩU B%עw+k_{/)lshY+`Ty+YJVVVeW`kkOKͧgR7n|f4vlG|B_}1sAiSYb%W0I&+;'x6_ly:u]%Ւpv/+egݒvS?ɑbYXYYVIq#· ݒ|7SRr<ƍ =GFf)&B)//'OrimмcF C0v⦕K1}Y\ݺZd9Er(2K_yн-hz>BaF=UtG3bNHHH5quuc9rx6$ 9KMM`F?<=fu/IY%ߟ|y&85k`@ff ?q-:O]:kS^Aۯ˫ݽrZ@Q)wt79ԘBdDJ?(_ZRokkCjոߥb+Q4Ake:+ }2Уشe+ÆGۖ9 ժ9΀~ SghڤQ}Ann\Pb֫!Gݳ~޴qc4jXO>?$quW9,B꾽l{.]|UG΋ˊoal9&}­[hP}kIIMe_\|}p NVVVx{SpY0b,X'Zts}A{ine}uڹp^x"M4j!GhE=рFۇO&~ijO?UevܔU^r? (+{ϹeBǗ'?)1y|Xȟ/xe7qssq|= B!ģJ"B!iH)B!X)B!*'R!B$B!hD !B*;!BQne!B!!r9[!ɍ>?/]u]!CI'h.bִL4_Ϟ =5Œߖn]:Ӥqr`jGTg[4ATTqa;i[م]ue?|0T?/'222ЯoHm-)}m1at??nم{͚(:80i7#ǎ W}旕Q|rӽKg4zxHSţj"33QS~~XƃgZʹx_nڰ;M1 6qt?Hf I7nJ4eN~~>[mPJCޯ0zXƃzPͲ'?عlF% ߏڴ11]'M~}uU*65k҄! Ϗt6lGNί>Qct_l݊~o{͚deer; ٹy^\o_Yq ,ՕԦ S'O=ꓙEAFNڵ=u$lTݴ٘xʚ_W>6cmcيRvm_u6+aflmlؾ;vK÷!̲`}̐~61h(҂FUUװ<L s=_Y ŵhm`}ʔک9O A] 4UPf`y~7s<1'e~(n{Sd7Ʃ6[Xs+ʞQĨ$OиQ#VH|y&O";uorj.ѧכ`uu5l_pH(+Wfr2ˊ]֭Z4<];^^|:i"7nb89:BX[YANN7y_!87$YYY?uDv%KpwGsy)$zҌ)NYc(o/'%hR; #GBph/QQ 5fNʩ3g:P4Jch??3L0;vpP}i&CzF28fC1dLuҎr?lؠ>114??OmTLlܼ{bמ@#F? )1̒ g01hVswy3?arM[4,?i8+۰g>YLJ̲YVQ䦠Z?Fs;^V*d0 ~7hlVemTҔa~Vgq&(rn`qIIMe9LN6z^!Jbp٨aC{NC8z8py{8t_V KaW:Jx8͘V%fOaZz:i餦VK|1ì]+%?V[p]j5Ŷs݁ikAiԺ剧$jRaYcػ?HĿ'[/ii,X_4kԨ/0U CG\x. Q|ѭQ_cz1S1~¥ߓɱ7 IDATaמ@wjtqVxw/.2r0s=+a_P Q&{oUF/\J97'| >zjn 2 +t_(Ϝoޘ6&2* 333>?݁qErrr^G0e?‚={Rָ`RT*9y^ظxoL}Y)Ԯ]\=~_z픵^:vlA3t?{`bX_{Beʂ33j`me {,t0>f(Sw}:ArѪ|(s@mSE^EVeSXZ3P%cTpPXoGyl'ߥ1'w[U¿4J P2M!HS:}0νh-f-ڢRHKOMKR;3;  ՜7(W>{=/@}̝ۉ (S)>xom_l~bXyyy *_NFA4*fCe Et,-Q*'SzI(0[:P*88ؓfZ-`ᱨhtxc%7LǠ!UPd$nJֳSt}E{| H]RroPd(ZK'bV޼͡){VE.laL-(d7Jv(u52b jK$2=='G'/<8#y\llccm7͝YYjC~=) b_P sm%B%݀Vo?9)m֬^VoKKR(?Ceȶ0Dff&z-˽iH;eZ]f;Cyh4~! nj=K:o=:h N$K/7>ݞHgh˱D654f=XDd?9UIKQ0j_ *RnxϔB<~ ~OS-wMZz:%̙t?7nԕiZ"""MS9/?s.k׊w13+?7m&11j՜gfamm]RRRtZB^c)BҊԴ;#A<Ѡc4tYlBKa4mDoZfMVniEY^:v i2zĥzu禍uդOs,Ƙ}QJҒ<6MCkQt]yMT'u?f $TV(rStS*];f(H-[Yf"* 芺~&hPEwD1o %D:s#ǎ1q‡ԨQ<=VCAR%yl0YMe^4"up˿3f$aWBE[ d?!(!1ߪ՝j 0 ALhPcnO}25BR2.M ފZuպ/ӴIFE+h0?SykcͺuED:u!ٱqV/\drvI0g{J3L4A1udd.ޝv%"?Ϙ#&:&ssE.7>>?2;b{2!lݱ#X[_21+3O>9B6;Oi~cFe}n30S-msȑ#f.}IL7/Ҏ C1dLuҎ1&Mȴ/(Ȥ/rY&OZ5kβ?z,Ƙ}4fG4Jnh,n{=-"wր'Gp:}eXC Iw9X^ތV:{Qb6K Cن>sҮbsST׍;~T ';r6hP&_"uDQmԼye!2aXx#Sv-eO2 +k1ׇIj`ff'ۧRѭ+C|/oLJ}AӼiSR捛Dp-UsޞȊ]mP>6@oǗ^ ueTsZ_eσvp0Ǝ8zx"ctd&ƒRlNqFmᑑz{MDvЁ}f\ O74jؐPV/Odv)+Yn=[A7h0yyyvtDoĂŋqrt-"##zzb:y";vfᒥxhZ<ϗ.fxcaaԦ5իUc9D`kcCxDq t~}YotYR6@Bb"vvv FĝKu^ԉ{_NժNT@DD#^x=IZZnnU(xirS222z'4$R!ьN"k׮U2fF 30u {UҒ\| GDFvܜǎ3~(y{Yz1]a y뎝xYƏŻs^^/b_~\]];ersuښb3$r%l߹KWtQWoؠ>?/_KN9K>177m,MdTfff|<~ŋNwa~.ժ9AxDy Oˬ9_Z0rLLl,~>ܸy4ۧ7?[[[\]\=KLl,_[8 3TFF#ǍitlߎW_Hv/3bx""#o/V| .BaaNҐx*KjZuj׮B>j^Vx*˭[0B+ |}|LJעu}ue5l+Wn,كl}43rxFVK5窨T*<=sW;vSD^d4ذi F Ä]Ag+; t#g]+Sίvn:=vT}i\]\8qTQQn!Gpf2%>^O*K0q1NNNDu1\x|.[_ {YxxD$.իbV۰QBii8;;J*6)dv 77ZlYO~B4#}c*9BBBލv*r{{"˺}HΜ=GFF?@?LBbxvvlܲ Ǯ@\:i?pO'uNa¤) Џ'GxD$ۋ.͕Z>WCdT$H~,2rJ2՜Euh"{1#_^Ϸov^X[[;5[]F.z_HTFǑx=y$fff>$cH<^:ٕW!4 @ۨysf4w OTBnb)BGǍ'իir[z!akc+ڳrl!֬iLI7nAVV6'2!}^&q}Pe"!UB!&IB!0$B!hD !BI)B!&IB!0$B!hD !BI)B!&IB!0$B!hD !BI)B!&IB!0$B!hD !BI)B!&IB!0$B!hD !BDEО]0p4!h.,HR$h.ZlY:e6VLtn6DtL kɮ@)!888|=kˋ? qi&ir[ټmVk]!;}q# Y&hh԰!_1!\;^ 8/+VvzZjɻo`t^OJw)XoTF!ߟӦr$co/'%hR; #GBphx{y餉߸ -Z`meEFF9997y_!87$_YYYŶ9uDv%KpwGNqFmᑑz{XxcH;iii]ۗȗ_zDN>ְA}cb;h0~~L lߵ0vB!D*DR!##p%!HjJeh,X_4kTD!b n:ԦQIdZ,GڂdoZ;V(;/6m@xD@A30u۷)cu^xch;TVudڶiC}g?kO ݻv%B!(Y^SU*6OЁz_Ϝss,XqzܼLU'G0q<-=;6o$pVZ?ceee9x86i܈[EP \;NZz:mۼ@zuYҫŰ0|P( ! D١VB IDATR;3;  ՜hzrs h5ژ,B۰􇟸F^^DZQ]sF7~jcV75)zVe}iK۰_|.^Zt^{.geeT*qp(xԃBQH__"hBVl*ftzRREe4SӶMkVYˎݻu,-7/(}AAб};03gm"ZBotP(M";0ݺ͋['8TF!==C !_r;M6=5Ϲ kE~;V^GP`iiEjZn5O4_2sfV͹hyfn?%%ErBGS-w/jE1Wd1۳/(HTܴqc/3nwo/^}#>^^7!qbH'^xe]:Ǻ w^̝yyV@k0y4舥%fʂѱ nF=axZw5$TDŒĞZ*(UZ6|BKk.(j)f!"[E $9r=79R|'ϓ3g;39sf8s,oKXx8&0iXLMK&9~L:oC:971)n]p",tj5:Ջp|uHJ*ĉppf͠^z'$H .X(<Ƥp",,4/`iVTte˅uVL?e+VXnddddddd('Wt26lM礙#eh4'$߸?> S'NGt:RɳL2ML^^Pqrt4*+ tܙ)'`eid,ȋu#{q*}Q2\aX=1Ϝy9lox5Iq-۷K.1 cc:\f9lyV=;һٸLU7;{_ߴ8֯~t2222222l\FFJ׫>C :@<=NS葾t҅g ######!Ζ1NUى1QN䜏fؿs?`Z [׮^FE 7¿=—]۶_e}=TFO-$ia5z}dET؆B h^BQ>m;`ф:AH? %ӧw/&W昲kIU(ƌ*<mYda8|o1#G~&HkQrZĤ$rrr4m!,]1:{ [oEW0cl S'MTsEݝ3TtjqP^-R:; am۴ٹdCTr9:߶MVjc1~YD9Q/u2͚6|d$ϜpEEE¿Gh4ܻqd}Gݻ((Qq7 7hTq%>A'bemP='Rt%I^Z->> wϞ$&%_222Od^={0z/8wVSTT@]pO/,e. '77KQ|d)٢dhܨk_;kc76݋|rn*}ޣyrMCjժ1bPzxvדٺ}AAB̙=6y"͛6#''e+V eӫ n]n߾PsݏqڴjŘTdc7nO0C刺|5yx4jHpz =*wSCHQĦ-[ٵOۗ#|ys;e2wCeݻtS,kӃC1yڲcfZ  q]YL"7m*Ύh 4޻oSP{"\bC(s4rwg'?;rZT }w.rP臘=t8fl^Ki8;ɬ\]͚t +KK5;;]=ꇵL<߶m#5..IMeŪ5ddeݝ'P!>-Za&Qо};'9g?WƱACfMg[J:uv6G-L}_}>afbbc2sa!N |!Nao8;92w8i l;^ 1 !1Wʤ$M ѻyһX劺M LaCwE"Fʞ,r1C}gqg+i+++aClj|GwnݾM BGf\ONg(U*[n?gcb1ޥ1C)JI|Bq PZ54@nX6-Ftՙtp5 LEúyWnߦzڢ"rJbyg0<6Iv`cƦ쟟~L|$w"mll;!>z_MBC-,,$-ڥi԰ xkj#gן{=ٿL? HMC!<"?sM7g7Zxx3o>G>S-C@س@8h|аx `րlXmU'(\ j z yg)dddffVKʭ[zyZ-W"//wy钎#T4,WTt4t^ѿws<4L(bnRz.,,$;;LKQb  bsUˎtzzzt0XFאCր) KjcS.1!7`bhР+ B^^r>2Z찱‚|TbnnSL>DEGW!yrssj (~m=ȋݨS fff:t[lA~};vEy4^ 5d#SΜAcbbt|ݻ$&] <7uu"⍹lܸtAbJbtZU劊̨ðF-9c:YYjN:JG^>1zR֡!OVӳ;wY&ԯ_:''|KabbL2ޫ~ؘFeQ:k@|BER*ڵMd/ޞ7P)]Fff7SȐ?:uO(^[R*Iqj$=146UK;56UeoǞW$w"/,*¢Qi]aޢ z{ K¢" fɲh4j=h% D[T$*} ={Qè֖tV%ƆG:TXZX`ffX4 ^:Y6֘1r0F 0 &&&ԶfJ%?_3)00J.FUU11Rhղ/ҺU n߹9/{-FR]:4F@!z҃;wJ\:}ř5$y1GEن!Tf HzF QBz{yAɓXYY]D*G1E $d8(e+ѱU6S7c+O!!Ì_.D^d@-[uҵԉVi'|eh]&''5tjԨA^~($k+kp׹9_';oTG ѩ)Fի4nN-Y&Kbbד:!F ]٫K k\]\e6l'tmmm)**BT2ލ1eccln*U^+vchJѶMk]Eu^|Qnݲ%IW)** wc'Kֶ<GL؆T\v]8ZxxP+fyq=o&MgL6VKmZ( I(ޮ3Z'GG͉OԏWQ XyHv*S~1}|&ҮfM,,,05+_k|ҤIcN"#3PgEH*V̹ѷ/a`¤c15#.\ ,<~3W 89:à ]Ϙ>eǎcx4oΜ {2i8;FllGbaduYs 4,BCww5tg/1G թT)WTT4S&節@Ξ;GbFeHƖ=1)n]p",tj5OWiN.~nEJwn,]\/j ӇJ$NyqĔKL`7RuLlYx|B"u^|^f]%deB!.JevuN:AV"㐪ŘSgѧʝOcԉOj+ tܙ)'`eid,ȋ%gɷ˙>e~<Vgoμ6]͚$߸ɖjٜ cKk{svQ_fQsf͢Z5s9݆q~>b̙=D?}A.+ڎK1*gжhCFF9o2w8o &?=iSq_3!dddddJsԩo922222O')##2z/ݺtn<ȯeddddddddSO?222222222O)#0ꕘ+U&222r[y^0ډ433#$_oo/y+z%{vM|GnP:Szrҩ@о=li-ݻ,ܛ,BpO|Q#˔wc|?Z4-d ~_vmBH?wlicaЊmq"mZ/$Q(KH?5J^ooކcnj&$ПG  BY<N,!$П>{0y2Ʈ'up $ПKY'+&OUQUm*۲LYjILJ"''I@>2}vkN4oڄ+x@ƍ7cC/ڹ̞Eݻ\8|x?-3r7n6M[%`018;92w8~HBb׮_Ӌ֛9x(9pi f޽{zqƞF `![P.E]E4NlLCwwajҕWuf0FT6bg@g1MwW%uSUmYn F;EEE VJ_XD]fJ V˱'hڤ1ރq!"Q/uq7k󑑜>sV<IHL<pgvtlFϽ*li%=#i1$MY 6|8)U\=~}xhRz$/w"iPzIbRP<Oϙ2'r[h'[~}Ƨ)w4iԐ# 68wVyS @V-9t.hZN:M>@n/3wiP>9D^%K6:'%\kcOV(o`P1_V37,fm3uH`IQ/3v@Z3$HS7>ѼYSlmmy&[ߡs4nԈ5/gΝ{5vkk@[XZZE̘9<@\Bt%5.Ϟ;8;q 4 8vqq+!i׺5pvři\ONԮmm$$&%cEV# g7jq=9w$ӫ n]n߾tt!֞˳Uc{OO̞ψQL_ȱl=-=غwܕ#|:bB:R٘yҩ3wRSYj Y4rwg gp<4T|E$;;]=ꇵL<߶m#5򘛛pXT`RLM$>!8z  V g7^]T^88ښnܢ~zaƍտy̷nY+-*"dޢŤO>elظD..oNpӡ3g~ cM:;[X:ClV+̞~_ (*2-b[?RlnRُGf\ONg(U*[n?,Nѫg'ݹu6/ܖe*h'[ղ|*}.Ѧuke+Vhg,r1Yj5aQJll{`mE;?uaЖ5Sj繰l=gZ,o ϑˆ_eۦ_uΕx>Vˉ0Wΐw GZMZMff&4R٘yC!<"\9{&óxv++K̞plЀԻFcff܏fcoo3t֠C1)UG MU<@\Ixy1V-;lll /?kqDnFs2'ObUБ!6 cƍ #8$ÃtyOұ]fɷ ;JN{6diAc^@GtH -KicU IDAT?W!''ӧ @@P?CZvy=== :lͿ-Tq3EHKK]M҈ψtؑ_Sd/E &rHLJԔO# Q:ebbbԻwIL*\+x4oOtH/D3sssF)(#NU /zxvN:+=5\99hZ򰲴2ZShղS>$33K7F B< 4jTN'@Qa!nnJ23vmqS)YKA{{{o@tnZYLIg73?!>x=J$ FeV-[ǎ넗Qydǩ3g~gN>I?m b.SBedCUe)zr`tl,_'7 ?EZMOl߹fMP~=e!۹iPALq:9tҒ-dᢝȒFeee^r#nl-Z̠),*`,[A#7bccB 둯22m133ca6a$LLLmo͔I*#NǾ?=YÏDRPP(a|'hZJW<%3S11?z$'O޽^osVd?P((( 55tG3sEzF'Obee%}- DWz}N^ޝ1\~]wk[N%ՓƏTG:lz{up޽{_eBpH1BEγDNN V:6ssr),, w3wRv$cB==yVXXX'1l>yz%ރfaLp7n Th4^+bhJѶMk]'kTJWN: @pBO?W{ɨe3AVcWN)lk QyFI<-,,{[b.cǘ3+BUe)tRTTZpmw@P^Bn/a^eTXS&...MK3#9{.vm`S411}l9c7oߦvm{.{.CJrrrfcŢjIHH&8;9  Pp5V˙sgyo_1Ibjeǎcx4oΜ [l{?Ջp|uH*՗)y:8ЧW/I>VKUe)~nEJwn,]?11 3zmΌ-TNӧѱC{z H*Ԧ-[U>= KKKnǟm"J3o^ؼE  !E>[Qڥ ))),X 5@Ν2qVV\ONf/xe z%{m}~XwݯߋSY43'?(u3kժs܈#eh4'$߸դ11H%.gIlgZQHEE),,%KYr{o۶WZ%e\s&&%2z/M![ߡtjۣR*)jI@tu@!x3eлO93r0&v5k|&[?<]DX^o^ݛ۷<ʶXq}4>Þq"4 +K׺.F꧄u*BUert4##7B6oZޖC+-T@ۢ]Z pԮm&ӢT~ӦX>g7;{ sԩgabby4F׫>C : ;2";222224Z^2z/ݺtn:[FFFFFFFF(Ο:tn#########t#;O!?^B^m ####S91EIT;fffogaв3P(KH?5W{Mо=z3@5R'|`7 gy:@ ۵UN)@H? "?iZ~«ҞٟV-[<|IWϕe5/i{t!S5T )9>Jm4?'|1uEH?-] k۴M2Ǔ}2ī^+߳7oO>Mp2T/5NENXaOkeޜf)g}2DՒs@aa!s,dw8ʥ:'ʆJ+IWecm}_/-RD-l жMk\Rz1Mw8#ʔOU[o 0cl S'M0Y.di )Pۻg2z,MhR;VQᆱ 5_xLlmmqS)=%KȽw/^U~}^e)ص7aooOΝh޴ W '3o_ѵs'>=Իwy;vqHޜ 3fo$whߧډ,**wx^=7eV4lЉtS{bemP='Rt%I^E8LJ㡡ٓĤ$9?GL+lwddESxiF\"\R ]L0&r‚nj ճXw{K!R_,YJvvQ C[b∱ 1u(&ĵS)t* qF~9wkX[ӧw/UFճT6/x򢑻;1qqeym[LLLԶ-۶c@NcOУ[7 kN=DJA:u8t^x^~~OV-9tZNӯϫo('O>elظD..oNӡ3g~ cM:;[&͸ψQT̛1nb1IM{pLLټ2eiW/؉P6l g'GF'-= sfd߯ZciH⸺8s'5֐I#wwN@zzCC%MU|X}llxcTVMxvm/ZR3Pkkru;ԇu7Vz޺}[g]\6223h1iF_!*}cæ2d;4tsБBb /C6'4,r1CÄBsKضSĶ R2)qƂsp#Nz3̚`ʕլIN40׉Յ֡!GmC1qĎ%=4$*%^t W!''ӧ [fphZNQzu3́0  hIC!<"\9{&óNREPp01qq4hKKKTޕkA{yHjղK QOHLfJ o당9'O1}$A^ b0fx6 1* 16րlXmU'}1,}1V+tdggub%ۅV;D^D֭[L=QU?f3 a 1C1#&Sb/Jx\;JA;kϻ܃%yfD]fq86h@j]  b~XwĄvmp>{W{.G01ƒUy5ػwShYXɢydՄke|66ܻwO'ރ6F;R_V-[ǎ넗Qydԙx3H'zrѱ. ͛Ӻ:_g7.}TnVރٍ:u`P`ff&bիS= '$PTX*,Ҋ vmqS)YK23' TJW͔|x3ONVJo;N$ ,,-< 48ջ1WY!4 ..:R%1i_RXENe+):iiԲ ^RsbR|8}A6j}C1c(XSb[å)o ޞsssAjҪc8y4B ^^C77j5gΞk6iKQ:q>q=vFNn.}:W'5 )qW1N;ҡKtЁ,Xh'xLH[[[3mll:j5 գAsss133ֶsR6֘1r0F  &&&ԶfJoJ?)ƾ?=YÏDRPP(a|ԌcAAIWѨ;*WWYHo//32=y+++aHmT bŋHpϣ9PlWcc :*{)+ٵO8ջ1WY!( =4Z^1K H۩ beE(̊HVϱqWh1Ȓ/>Yl9GzCl_'bP#6ԡ8pt !gV+|,+*̬c(**"`0}zYGڿVhH}vzN'ԡGj4)9tҒ-dᢝȒ+++㖞²_T 7 Fk;*cOF6+"UJWN: a = \I{Wg''UNy cⴇݸqS/Nen UmB 4uX[7%UT)dɑCB >x?\ @LCq 1uh(uX^.^ڠ}=MNabb_u?Pl߮ʽwj,Č))yQ?ޣs!w7-M/Nen UacZnnkiij_XNNӵrihRsirrsnn߾Mzjlmmؾ 1C18W ayqCHeUݷKbRWyGw!ʊMw_˜2qtܻƍ?TCcʣ wҌSΞ]6!!GkN8:: |= ZjXb_1j93r0&v5k|&[?b̙=DWl_ƺu⁣",v9ӧLb/?>l+^Y|`Pz0rEX{h4enNm{{TՕb]}A}|VyһH n]̚Ej:rD V=eu#{q*}ބƴ]g1\IHkL8+K+''`D^6nPzuve߯uP.  ُ8blCLcL1:,RٳT}˓`!}p2KڑVs1F}gmeg~s;oڲ}^ѣfJ k~IJ4f["/^;]‚EpAtygLmv,CiSq_3,O1,)kI(JRO=BFFFF,Ο:%~Mϣ9O"# xή*Z-ŐyۅLyTlguLH%' IDAT?^)|i.#-/u׮mOо=5~aiղE&OϪߖyO٦6mf鷭ܺŋkӯϫ'r{r<"|4z.yy(JM#FSԧW/Mݺܾ}[2yx4jIS9Ҋ1Jjժ1bPzxvדٺ}AAT y͚bkk͛72 9Aocaa|jQo-Wn/3wiP>9D^%KiՊ1#}qSRn6o&Z+++ڭk?sZF;wX[[Ytu*6BLC6ӓ9g3b&OyflJ=*:w2l47oҗB* \^!FQT rP9yW q4u_^q5~&bqZ5އLNϽG~AzqR#?B'IRURΟ:%~HF60o>,f7=slM(۷oFe%>-Za&Qо};vuqNj*+V!#+FL8 (JII4w77̝Cą 9+3̚`ʕլINs |!Nao8;92w8iȡM[rRަQCwm%E[Sbm^*y q'5tIljڹqWpM!!}/%9]bq7ÿFkiG'ZDj&w_;兟)u!XLR^L4?ٯQ,.ok5?~ D\[tI+5F}tPC 瓜L: *:Z8<4$*%^HD?:0g|,0+j Ϝ=GƍMZXE^^!%ڴn-٨;~Xyׇщ3w8WtZ-'¨^:C$zjܺƆG%K!Ӆ#Ff1X:{hT^wکlJQv:cRُ8R (T~rrr?} 0@p"E@րl(>yۦ_ud),,$-z-y=p?ڟKQQز4@ PK%}w~˾ҹ{ї[ k:ųIM^b͑-$EkQfève/4IH}R*8ByyHQ'Q[hZ~l`$%4jԐg鄝:sF V^Ghղ9zNxaaj޻[wo/wL-;;ܸB^^pN-˜)NF-ԹoPѼ!G 8;9ann.L7n =#CyPv) 1:5Ğt!66%RӧўPU6/ƞCqlדu>U^kbb ""wt`T>sVIBbHՖJΎ͚иQ#"G3*ceƃ)?AJ79:-txvR+U\g 7N@Zz=j0Zfff:ge%=#]ϑ-г'X ;/:y:E"fZBlZ8' I P"齆*UPT`)ދEbA^ " P"U 5$BI @zϖ% I6y<3gμ3gv3sfp#?Bo0mNjǎaѷTPTxVJVS}z{mf<%mZVKc.- dɺZxĽ:-9uUƚ ;aNNj֕ nh4bgJru8;c[º2qZ{>$ҹcܱ/\P[m S8yϒk0걋 `au/[ T '6Y5Uルl+y^8+O233p@R!ݳ[WV[Wί0KkGx8;qvro`LzQl޺z=?.]ƞz^V)ƚXiYe|%6X˒xAiw0ϖf̖<~V 7Z@ffV$t89:Mwvr*e˺b1p%9Yq}Kqާֶk^bsYKw7]|(vm)7ܖ3$ w^/MUXsZ2>s:--ͬKZRQvm:z,{{;:w`6u* {{32Lyi버a $''YBs…b՘cK`x,mԒ2剹%ǼRSfwi{Ҷȱjzx(~>TVE'7%.H\\5MsppV͚סd]5onj_zz/gdfjK\V>/+ ycktԱ̺J=ԨA.U{ʾf./d_t*B4W#1E_XFۊY-KmZd}%jOuwyݷvmش)sTȱcL?`' Aaw9^FQϏf\F#a`@S&acS9Ozd OFqtڵiW>z뚟.^yQ+d4_ 3{;OHkN?pT233ͮ+5ⱴ-jSKX%maIKyKX.%2f%OEUkc%ǪZeqXPP;|Wnt֕_,4ͷvwO̮{9s,Ǐ3<c.wq[+kN<9d۷m;vKbi#ԓh4}nx,i (M-)cI̖%e,9ԮMΝXlyǒu)qv4Xx[{yCǪKX}ѱ}{ 8ɋϏq֬[Ϫ۞\fvYgcc9b8/^ ++Hyk6LʓCоmaCM’uCR1l ztJUEǡr|BZWW~j8Bј)yWO6iݲ%Wb%OyTRmZyk(ͳ,ZCog  v8 [IMvcAAGCP6Ov]7M~feUR+{FkӚ>x}RT*V.]̪5kY=1KSPC@ƅBU5܋#ǎOp}3d+JcMw(L(.^hVƫ}g/),,6=Jk'Nࡿ9|iZǂh׶=?zxxT*~I{ !,(ZMY+ٻFfVYB:uƶk]ӟ.w_4jؘXWM$_!55%KZ6mوZmC]*\tvf-pvq!dfh׶CK6*#ՒY='dt%Z26*uZu_n]zȅ+Ɲ#6IHdv%}MRѤI O,6k,y v^Җi4^ώ۸p<;·hbeO>E&Tjvl233tVQn/!e*'ݝսINLZڭ5o/o"#%:GbC-kI{`T_iQ2:@̏h+ފS뜥SvOceL Ow}kR\uz̢6E㸺j#<cߥs7ܹr b8CߥB\|lyFu~Dxɔz.^_qqt ?ߚ\x*Xܭ-ѦU[V$??=vDQJGH_?u鎯IzF:jS.L{{{,*s5zMӍiyXY;{uk?ńǎR)Y8ht{֔)5H jtz^N=)YTZֵIptF:uy{@Ѱع(qGzz:؄iddd@u+V ԴT|}}+-UYmV7i4?(kпdd}+Ǐ4lHq,cr{ !,sϯ<| `/֭CFMpwɹg1ۨLnqCWH+>9{?IAuuQb=j=sO7bk4uzF: 6<ѭkԮ[l^bE|||h~j|kr)Ruj=**+xxT!--ߝp)9BXFQO3ҩ+:v$S.{*VǗ:wnRRKzu7nBTt~Z< ru6 ੀO@o1 n%@b+>.O.djx{oW}3Yw6E_$ǒ+Oyotԕ8j˛[t:zcGR gw /K\rKnz@kljظsf\]4l@vvV[DGGѸQSzv6 @YbL8t8\0+-qo`0FzV'[C|9F h؈ԴTrss̼C_12z"O;ؖS)Ur*׋)9ql8~:+}ΟO |N|j;~~صOW&s)"ݺ 0Yѽ$c84 ^5ͳWٛU=p01Xpq)JJJ*VOFf!quuW޸i57UqO[[X"))[6ug`w퉯o3D] 4f޲{Ka`;͛ӕ);a]:wMVgXOp?sK<[`}qvva5}x{9dcyXY-*J1VZ{{(w=D /p)kphŞlgkG@F`=3kŽ"%ܻAaW&s8k7e?U8g"(vG5^BXBNg !BEVZ[y\E!B ?__ aI<PRb")g555hJر{{Uk3gZviݪ%#G 7+kM̩SeO3-;})fL /Cx˽.!B<D@=HmG## Ô`ؾ666 CM??yĜ:;/qq 7VZjj4_~70x̜6Q>o6_`6}"?Wg Yb%T@D""hӺXvvv<ѯ/}ooN:]' <S&شRb"SgF*J^s6moiJzF#Sf3h߶-u&6.BB!IԭS1';sݻԓpqqƦ+i5+rKh7--=M[BJ\߆?6B$֯Օ6l6qvr~zD);wc;+_z'''/[nQ>5qvv&?M[B0]$čܤAi+{Vs@)xa8SY H)B<';wakk]ԮonccsߧE%.gkk J._bRSӊMsպƨd@ZZNNZA@ի,fi_>1%h4Siix]Vj¢o}3QtVF[j4nղ|GGJG!6IG9|(YYnSعk6*f|Ff&ի[>#œ̌:ܴnh4t:666d.:,akkK፤.77,>888FjZN7ŸipЮy,^lB!]V{b F۶Kƍ; xh+x]ddfboox__sգVatשc)|O'&].ݑ=u`k$tsn&o$(̙bvCy77-cG=:Bj:9sh]]y=wgzX![6x0>>5deeBTt4BZ.Nǒe˙6y3Lf1fU @AaitS!uxzV}oDHhC>/O1d>Jr2^իϹt׮܏:q?f4m[2]DNn.+W1yu4Ə^ӳ*CqB!`Ȭ, 8{.6mJ MV^Áj5.zvKr.6ggg寫ɩ؈p!Bp.SSr-?q8닇;1'O2i+:xq=YٌhdHMM59cdz^70n,}Uט"o`x5GE3Lӿ4o~90u :X 'KVf1'XJƚmÆUj=:t`q e~Z`[oңkR.+{i|޻=\UBʕD2ɊHߘFaYuigF`ᗜ:}r~<;$jz5kZmkk۴neDT?nnjW7M,Mᵙv:,Uҹc6n(ik{sɎ?t؁~kׯs<*k=Ygb[OV<}"*B$RR1d@\+v:yyhrQ|tqs)1>wM"hP>ѺeK<=ru2Ѿ][l C:r;wl4m݆MO##t2s?i(#Fp<*'9y&šG*%k Y }E!G5'-q/MK ?[W8В=WiZ^|hn5+٧#8cCCxF!<4-ܬ\]o[ߞcz i~~=zꤗѭ!ylw~e%];w6s&lx,#OjҰ~R˴nJ?-M#Oܡ{W%WΕ+WMWTA9xF%k۽wnj}C!IqOdˠ .%&Vw͛5c򟉍?mڴ"$,ޮ>K^3gxhP>;wi*,0O?ԪDŽqcIIM%$(,e5tҙ1zGתſ|5ֳopwS8:8E^^#G`'3 6lE`,\idegg~ٳ%iӺ/]Ӧu+6o5_GihZ+RLM:<㍋>cLI...hZ.ߑ]rGGG<<nTqu%6d姥8u ̬̄qc9hdS#LIdFf&X,ihؠn7%7W@Qz=YYY%saXF5t*k?'l YXSTjwБ ilN:_3N̟qfeq_| . >GFf&""XwNĜgg'bH],N"Oe9GF; !!Id*$&].q^Pf{>鷏ൔFu~^:PgӖkׯplM%f=?ǣHlmmz&>!WS}5>x^GmkP:rG2|7 DY=3cdZm᭷1YBy7gΞcЮm۴C۶֍}ŽpP*eF)+9ʒA@Æ5!'I]ɗV%5-*zb[oXwh4z3MwrvBV13V! Jgժ$].9әxyxr(?;Bt7[n0*kgK ¶op0iӺFO3g0iժXw~:m}%'z*# .v >p.Ǐ8i/Y. h<;;B$233gRy{R,Hp>_)dvv6:NNf8;z^ǥSBkʈ܎pvDL2ǣ]gFb]wU'$4};nKӦu+T*-5ުr^Utj)b/MfVYYx{yM"77S֊W!rξx)oo=;v0^5hRדhFRRhӪ%Gxjb@Uj}*Pۛ .B!Dq{"NJ;v8""xcԨQظ8j,0w>kV6x>ğO %%Uٵw/gΜeqcK?# uq;ӰA}fyu ПF; ik9|x2@Ν))dff:$Gcq @D> ]d?W4;mcƔɦ-oZ&$4'L Z=1[[no~5epMNnXYl3Ή^Ưkc7{ܱ~~~|mRO fL_cvoeQT4hP˖+!ʑDٻS&,0cNj?f{1<9d0\JLի۾3Lz\]\X|s|;;[vUXqfǎftų)$ZKv..;ʴ8:8r%>1Q.^Tɇk#k֭/uIIOİdϼD&lx #|?W4;m߱/iݪ8i>3ҦuKE8:9:<)3,_~>4q$]w?t۽9bp;É>d¸1tԉ˗/J<ϽԸQ...U!`l֪fp5>ʉJQ馾jkTB<"T~M$+ݫ'wZppvr_,]!BQ>j/E ]~̬,rr*6JUqt2sCBXB!UB!&IB!PLH!B$B!B1I"B!bD !B$B!I)B!$R!B(&IB!PLH!B$B!B1I"B!bD !B$B!I)B!$R!B(&IB!PLH!B$6[Š?PB!XZ0<4^OjZQQrbc+-#OWBNw_B!Dj\u !BT{"o,F#2Z2%&MQ=Μ=[{B!($2#3x>oxVʵzr^^^$''v~[^<ڶ .f:m`Qߟ{o`x5:5=1WOcي<9t{'1)?Iġü\z=C dؐ!"-=]ǟ`xEVEPs׫Q?c222_?,,-ijX`S{i4VKVv6oss[o}emB!DE{`MSn\_ CiR'O♧Fspp`O;w77: c`~cз/yyʦs<g~SE 8991|bu1|z=[dM̩S'bӦi-[m ={0sTEM|BBm~.kB!*BqOMFL<<CPfl 3ʥiKgegEVv6NN4l'GG u:^?δ͍o`D )) 7VQ,'bNҳ{7zr...u١uu%-=]|+mB!DIʝD@fVZW3M iii8;9Z4fsʕ ~7j7cкUbe^l4@KiѼym+0N*]B!)wٸQM TR7N dffEsW/nfSkh4 S*ZOfVUu1%vaRioV_WfFvB!*n)+5s&1'Or=%x223c<ѯ/NNdp9EFE649o:n~#l(@9s|\]\t77-cG=ˍ^TKf6Ź.mͥIƊ뱄Y!2)b@ӱdrMČ)0v)Q[bi搭 <|Bpqq!*:M![:q?f4m[2]GDNn.+W1yu4Ə^ӳ*CQ}YYY\INƫzuY9.]"5ޛgΝ##3+2SO)>޴<=gݾ7nh? !BT&=z))sg$ Ӽ7ٗ_q)1EuLNn._{;֎sqlm`ͪk8Zŕ_V)-Y3?_ȹ8\qrv".>&'' =wgg6QԬ,.k^`/vcİ,[R|tqSe ^xIqB!rY|xYZ ^lxP/#tv|ZXxe?8z,YΤgccݫ'B6n%OF !d*vbyE666 Gsٻ/Z50n,)ӒZ]:3q8F@aaޫ׮1rXl6XSSu<׫*J8-kq5^i4_ӦƾZכofz~ tGc,d:ߊ3 6lE`8w͛5c򟉍?mڴ"$,\Ēz222{0K"{ΕdE7M lڄ.1j׭{sɕ+WL^V !tJ"333h4899ńqc9hdS#L_ddfF#\)V^+`4E|*Q'Nx$Ғx,}g8w^>rFUQٰA}{Ƣdo矻nۜ[CדUְmc#qEVm7.S#{y)l P))֍;L @շߑMġC1lSivB!D*t_*V!iw6<E5ȪJtlPOJJ*U<1gYwlްMХSGxx(7>!WiѼ9/ʽlPfCW4r9qLzv@ƍְmf.^d6ԙ3J/ǡB(PO z\]]P?;GGrןfv]RQvm2GEbooG̦t-jE㱴3gϬʎbe|}^uM?f{mZ ח*mB!ģDQOd5ҩc"))տݺu/a(w~ԯO==6 o//E;h# ߟP1}$ll߃Q8HZz:ڴzu=Vl|B];wbIjZ:rǏop0""< <<B!9-B!x,ظx]xF/+q@ɂ_rrss3l u=(^c6ӧV̽Vݳ=kۺmZb?_?nnjWѬLNn.{4\qϏ{G ?EIdɓĜ< @ƍ#^`` /{ya=~~t:~߸2^B(suы̽v)1>wM"hP>ѺeK<=ru2Ѿ][l C:r;ݕBgQTpL?'''m`0/˖ʾ?DwgLJlNdeeYC@Æ|BVpvro`6o oPn]ƎzMjIJJb՚lbVnjGTKZ0Sݺ1g?WgLi&dggߘop0=3oo/Ybĸѣ2h  JI Qn]223hfDZ#!bOB-MPvZ+TU}JھkoJ)ZD""BD,M"{su9Ù3w3<393zmȪ59y=g.u,O ɓhҨ!BBy~`."] S[(d5UK0alVu:o\o޸ѣ$YoKJGgU|}Ns\oo~[#O0ix{yq>6!ƿǟ[>>p"=k&[nKN)':t`lڲiSgdwpQ/|*0a4l=8-8]׋ ƒ?5%ɮ4~O 7`:ID_ק~BLůA>3B"(wQP|EqÆEݰQ²V޳>z,_|YNj-Cgݺu٣Tn\~'a͛q91Q'ڵkl*9/"6&B$_"!JFf&=u"ڷrb"O@{k'ޚjՒO~aFCJ, B3;"jD%O)o*c*%ʜyIMKc޽thO֭h4r5Eڅi%~{BK%?79ٴe QgycD!9&;wsw9ciҸ1݃Fߟ':t`y Pb-x6`^7 0dY"Ϸ>CDЏ}a|bm 8ƍBl,āp*UĈaC͍-[z=`2q5g E_/!;;7np ȣEt*LSݺDPٻ۷oӸ "w[y7ҳh$##H+Q̼fS')) "x.\dffq[3h)bc"wEm" 9l 3XwV4 ڶaݲ9r8z>}ff&g19-*-w77Z",fzOHNQ}RuG[m)M>WvLdw"\BjZ:))*҈OHΎwŶDEG KVV5,L&u4m5|Rzujt[lANHh4{ӨQC63t1*FC=7Ohvǿy3~XE'|)Fh\IjTEQJ":-|EEe1899ҲE f=[:|ѻyW mq[z==u4kڄڵk-xEˉvEپh4Zl^Wؘ` 9:|5lO;Hhڤ1DYwoNXTofV}0"L_y:}JQ}SyϏr;OEq'a}c^NN҈9/`,yyع_-`0Jd2̯ <민LnA|DĐ˄qc\иjQJ"Zzj:aoooNt]\]j8v nj.ѠhV*IW E$ϔI>t |~Sٽŵ;yyyhy.|D^>#bcFQ6u{0/ۡhXr=][e>o~Д+Sʳ}~۱"5YYa#ׯ_Zhprr&-sN,<#];wNΝuVaڴ\Lp&8Ü[|k0p:*>>\tOhnY)<ջN@4_gΞU˖TʉSwm6>ѻܼgffG|b_ 6.ӦƮ8^ujS׭[$>"yyyڼ.|nK6JC|Bbcy{7sNGMk߼tߟmq6y?ec%T) ںDRsEoT)HOJGej֨`^ۯo4i̡ä1 իsxL&?s} S&oy$̜6j"6.^^gG󅟷v>!ӧN!l:v̞,m)'FLy^?''%?, >!אK4QC?f͞#,} "zթR+**oLm8v8K,[zv@x8))zRKl W^@x8鷬Ҹs'9~k[݂EJټ.|Nӊ˳EL:?Hvv6nCϠEs1 O$e=@x8 Q#Os WL͚58::;4c9IfǎfY ųyD%%X9Lh5pвp= <.;݋Ykr`ͨr~ӲɣR4jTyv"M& dffyۻӧMTZ;Ѽi)Bƍ; ?-kN?]ozJiyթ/_V@=P>w]/&˲˗ߏCΠiƌ1/s1ܾ}*Nyؘ2hPIx[{og<ꂳ;d'78{2,|_o^pGg7ŧS aдA3IP+j vZƹt|:L)?f4݃D[qz #Sf _Ϊ5YTK19{Pgd 4oڌLz1{5SxiYg ]ñH(J*nuFktxzz*7;7hb<=<ҩ:g҉f| g:sʒV^;\ŹF ,Ruz3I ^R>M1~wP6$6kD ^n4|a4[0bܾAOǮHuPBk$]%;$%%*҈9Q_SǎthOG >cWHP4(&o-xQy|Th4Jyk{{7e?[8yQ#`1ڨ%٘zÆZjGqrɻOYdA:X9p_8W^ ),_RbQ/1Qɐͣ6AFVbC,yʣNdNe#g^+);=8;;yiXa'2^Ng)>ss?^Y'رs Z~ꊽ=Dt]\]j8v nj.ѠhV*IW**WI6&*R:}-orss0n 8]cg*ݑ6NϐrArZ'w\KSWmFw F\#_h4tU VtX'{iJlow H ZAjgi1^'Xg*33w770777srd)nFY{wɻrGGA:!M/K'7S-c%Mr OF=O'i}!אK~iuyC??5c9RD䑣0L=~!<" Lz;nf>-B#]x I:b,U:& M_w_"dʧf n&Θ+R^vu #O$<"3ަVZQˋh> mgɼ>u aбC7gd+ZOEFqٓ?,$jD."RO5ݳ'7n$?E:xTx*Ր<7{#9t%pcM~Vc.<6Z>:oya7-_KKE*N;ߢc7&toEZz5}z՗&Lի,vywVas?^N>͇}̄qcڥ W^壏y,y.ѵsg1ˉ|SNB܊:pي_cxtL͚58::;4j$7IfǎfY ųy}ڔ1y/1}V#l޺ >^^75XNiѭv1:|V0}4Zӥ?5ząJVvki0eڭmg⠓`gϝNjc0x@<=$`hCڶ cFt"^W~G:SxPQʣ0hW']******s>Z:yʃE:NZެhWrZ׾(}R8qù&REEEEGs~uȏjHQHJ:[EEEEd2o01Zt**u:[EEEEEEEEEtJPHTgoW+粲컥 lޛa'B|(WԾ7P pwi#ymt8vU&d^NIhSWQډ4L'$JgFYrڭ -pQ!t2g3ɽ-{VuXK!R ޞ9 {{{Ml;.T4/{ :.m~7Uj H6L><S$8j$=ֽ6zAz^jv;E*v"ZrG p8K'$@uYTHD tI{3Xob?+7o}GT4*Z_YЂ#?EolCq ¾ˇZCh6@r" o^3*,'ёcF=(*\NLd58wϞ0b85kZ.8)d[|l,O?Cŏ?/7ǩYWN7ئU+^~q |}׳nFV"N fϚɨx7h޴|bE&2v 4owwwXMK CJlqj4p|ZOtH ކC :N:қo&'1Ƹѣ9f+yDVe:74n()D\"/%7'OI qrrb]w7g*gQ>#S6YY:ŧ ##CV:%Qrta 6A]#b"e(V;bT ء-]4nԈ[gXa#..~'[m翋*gl cȱ+K䏖Lp4ҩp!ˉh٢YIl|<}|hgnȞЁ3i–c@3MEaq$f\NLd 2wvnFr2GNkǪe?)K >d>a|xb¸u{Uٳfe6!/TD77zRnϏioL&55JNۭvk-H(]p$J5*ЀePQ:VO՛Hl,MK:J–m8F^;\ŹFz,_|G|fee#˂=ׯ_ L2 hl"O$$TrDYoow&ko`xzxХS'tβ[m.D-C[#G6DP$h;OqT)m;EFF&7 ßg`|o9_䛊-T "4)}7,338{Uy;-yCfϝG>UPa @Tt4 \Cg`,%33#Gغ=Ak,Ҿ r!6}d@x8*UbİEvۂwa_ŋf[Cع;|}q4nL@N$@n4.+Fzx7٫';0 XSDT rv͕'t\zmWi6 SNElcЀ~ RJ˂Cza6L&W]3?3##|]6_dbK:s6[;orQC?|==ӱFB28"!R"qD|JiMtμ0y6mBhޘ4:uHNX"D|A3=$\|2<ߙutQȇ a'Uao~pF鞡G1|Pˉ)Dپh4scR7o~)FWnqttd!t z8ۣjK(q-NWΕIi-Jp#2Cҗp,9YrWlwvkZ 9 IRZB^{cET6 ||,ߒo$>ᢸwJFil,|%*C))T#*xg[l ATtsEm@ EVQ)C8r˰W)Bven230Ldggs֕{b[ R`$ie6Hk/Dp DZ-zjF/i,Z-n߲) Zǎa744 ժV%U/_7~OtL L7\<#Mc\ר0 RWuqT/<4PAҺ|". e}тV9QNElCJ."B (͗Qsr,;_@Zexր. JK0Ya=E*N׎nRWԴIIȱt*bF\=WJFil,(!sffb +$]!!&'OQxLR2[q؆Hڊ# Kj{uZ6ʫ*ٶ"1dC>i)⸓!>f^:p!|b899ҵs'rΝuV۵i͹0:XnْŽ^(p%?o }7stRŠhprr&VHNdUK9p>XI{k9\NUjǽT+”mL&kk3uBmdff`Jsa2cF_NjUqww/m DP]42,)2EDҢ=wRTnuxrV˂mFr,|Lox.bd)?@oޜ>HΝ\=VYozt 䋯{zx䄝Vq=<*SF "k%lUC??5c9 fٓ7͋E1L=~!<" Lz;*[z3B&[.cVKyك#m4k̅'߆S|@n(flن"nßL<а0bbq89Ynf&b\ۈOH k^'5-M8_<,,R"ۇ&Ms(0it F=iSݛ>z1TD((Y-CvU$mH9eXrtZgږPnAz56To {q[o|nJb>=:O+؎ȭ6\b[IeZb-~fϝNjc0x@<=qvTAzɰ-vkvZ<}t2>NZHkkNe6ض Dlckp05k`PEZv^"vX4>!.:ބrlagrB\];wf9븜G >ikc߹;/VWod] ZG$mH9eXrtZVgږPS-a̸ǖC_q;MvYCR:6Wy>1ixծ͛oas>Z:V)sR/[:mTDT{VQQQQ8|X|M䣌FqjۑUTrT{VQQQQ)곳 D!**ڳJyP*********u:[EEEEEEEETNl?O𖿨Qr'jժyF/©Ѫe"9yK}Y%aE)U̜1*\}+]񫇋e-%dVBoeʜ^EI_4F[[M$}6FES9Hr"X[n1jpQ#Fok/©AѪEj\TNEԻR2WļD|BZ=sG%<"B*W$}#D6'<'1{0}xX^cժѷw/~Yoo9UTTTTT%//q/"Ms/t?/9ݽ!πӹ,;EF IDAT}ɴ7&1 Q_$٬{éYׯ_g՚,Nys$4jSf..:f?[3ATrb"k~4ehެ)$%%fEʳ+$ɉ;w%,'_dȑԩ]LNӅ_aӦU+^~q |}׳nFVMXNy2'/Ky/< S8"2B$-Y35~o͛6#33^̞{UzYڳKayUG$b[|l,O?Cŏ?/HG/=ޞmaLMa +~6`|s~>k #<`Z Z޼L֮ŋKN蜝͕?2YWz1aXRRSٺ=$>z,_|YNjl,֪Ug<#GX] y@\t*GDf]đkgdmo7xթC^IYջRT {cJ*e?"q͛q91Q'ڵkl޺MVl}zbXrbb06cwhA~#9bcղןzVyqV^3Qo+WXb4zEg 8}ݏcs&*?V*9"P.2y!t_⋯k~VΝشy9n4@wtgK%khS`Ľi6 &2$<;Bhܒrd*2Σ-~L&+V3Wj6|5jȱc-=F .=jق; on4wttd #YRl\OM<٥3U<=-~s%*디Txz(.s|Bvvv3-Zl}{{7#$tEٞ8y uqppP\+WfNJJ2]IyDt!S[qDe 8rm^>4Oy6ˠ(l=A_=fN\|(U˫*ɞ}yf4nԈ=ް0YݶЦǂƎc2-qL  k0VZGY'"Û))Xsuu^oqvrBZ4eݝԴT+G02=ͷCnn.ƍmqg^뤘Ĝ܏0t@~ ƼJJvr/UO&t_z>ŒokN)R6%n8oʝq4Ÿhr3d9r)B~k%_4=ܹZne֮MkĘ,5 Փ'prrkN,4 NNΤߺexy3An2cF_NjU58E}.^dW,t:EcFޝ6F3z1Trʰ$#O5ݳ'7n$?K|B._fĉ\vs[ܷ/%; a+d[3yjb`wo5pIX *Mp y"=mZ,u{py7s M>[nՊ7'M䫯zÂSvmԨ^>z58ت^NLkz)=ʕYÏesF2Q#3?ƒ=;v%ް0\u.݋gMZ D8I֭+݋Gt;PjU슃,yDe`OLoyn*ҥ*0k׮sivat~#:g;v6K/ߟaAsa+%.NE, 8"6P^=vaي_,gɡ$l ${y<ضUG$ROp8+/iƬ]5N.ɗ.k/Cxdfe# :㷢M=g6i6f/WX1ViDn8X6iLǀGDp6:Z8?׮\l\EEEEEe[]7ߞEh4~]5k`?89PZU^H9TTTTTTTTT*:OmKppHEEEEEEEEŋ:R mG/oka "(Sp;w.gϾo \4"\1>N1q+U>Wîy2\!iV+ }{`)urZ)LY9<^=5PQyJ/Lv틽yŋD8::2|ݷ;wd7Yӧ2s;zVZ=n޼ɿ9s.t҅5kbggǥKظq111e75]K%LqX[6nȖ-aO?4aI s]6 0.]F\\ |"[Qyu(ix7'..+WaE$&&rMsg<^"RtGWЉzWY|VYdj9rfrIi&g򔅇M”qu41[*H[Waǎ9r53`@/HKK-Wy[ܴpZ[4r9r$QQgJ!Cxyyqb`0ұcGN7@ll,W\Gl޼*NqPVȑ/(^zy>\xrr}}[ۿ?&/y)IJ;wj˾rFFBCwyU(}u"k֬/\zۀ;ILLd_EOל>}hFImO'(1^\QYyi*3]{e|ޝ5u{l0@Y(P,TE^Su:Fyiߊ<jsh_T9/ċ79ìKߊ?~2~#}f O%wi^0ⅩutO3%mVNx|tÈukJ W,g/^OǬ 4B"r7٧DM1 hW˸I:1&`#ϫ`b/ŘP=ZX9Ǵvv>|?}IRRRSAT~cǎYPZZ$,Ç#'gS狎Bm0F+W.>@ 9 ;K-o0.z,?.\\xw]*>5f!88Zķz GyaI߷m{8Ch%K&͛xwzuaMLL -Zj0$r{p1k,(Vٳ!ݙS=9f2_0U٥ Ą)'7"؛ŵ1<$&,NjGKnB2ж 4TZ+ O%FoN<Jo \>ʬp2 4Ђyc4.)jD6Md°V2֪#r36O%mñ(oaP,@ O%ϥn+"O%c]~&b[p {Ύև碯>|8/DaV\t f ..S.<</"}w;Lq㒝:gtt jjj|3 xq(%%---8w\pss7fLBֽͨ@ttW{ًX,L&Db㑑xhhoo1vXuw`Uس' ,bUXzn}s{k܉ݑ駗77o=Da,466Duu5fl={u fW9V~釯*ţ1wͪ ]t|b=OG\yn؄&4d nqLjFV\[E9GV#8EyU\α䴗vP_4zϥ6z ٥"9ܜ~>XT!rC~=A luS\'waɕzW%X~[ M:#+&OEEbΝc`L|?Ξ2ؽ{7t:.]'N`8~Xw/ݭڟ~RP;Om걜B'N𪋽kLBZ t]#p;Ǹ׻ICp!jk >|xb JJJp8Ȳ>vn  t&^`Yo06Fs+#^5XF/2ۀo91]` nm0<@}{X` $pJz1{0׀;BZ-N*sO 3'[{yZzBV38TFh3!\:s{712 e8zl/|%3vsWȑ#c N}s :]gJU &aK(/UBBpu^?0.h4X~r986l؀U٦&K_f/Ǐɓ'-sRR&0o"""͹^-Djj*D"+1Wq'.EbEzz:}px뭭HOOO< .vY{ܵkW7$&&bŒ.e?Vsfߠoh 8ǻҵ;z:E{2)8s:%hu=CérLJ5]m~z~Gb>| \xZ6ۺF#B[{W2^[(g@Jd3ǫ1?^FǓj uUsWCriZH$0N]ݬ! m(Xl7 =>?~wT*1q$lذnn/ `0X˲/Xj5fb&sbsg_e/(,b}ȐdJ*B `̙1cy @P\?Ɲﻇ {s`.z(.6ef>ZM6!-mRSYM^t. ~:555M-s̹jߨ nv$6`\3[a{]uتltt4{lvݯ-++EJJ :d]C" \Wfb` zo;Ou.ƨiFC} r\`޻faE 0^rCB+&cBd3gN`WG"Hι!zbQ!>d j^6䩤eNM0%;UJs;x3{3G4r7BlNΪ+zEbϓe()FeWo#1u]QSOcZ²,.]\w:uׯcĈuJ1bmbJ_$%%ENZ3vsPVVP1 {ku.ažXY_ $$o&ի0n\2ƍ 466r:]\q]riiiHJJ… 0qDxyyټګN\wOѣG; DGGcΝ8|ع P`5k^H$C^_\ӭ nm( hoY8B,4!.D!z:>_\ep;Ve e Lކw+EǶ"g44 !pRX/m}'ǯC.ï)WO_J`opv S^x㠷Uٕ_R|CBG9S>5رDEEcٲe6lv20wedffbʔ)>GC" "Hȑ mnӕRizչiԩS#<40[lSʆA&!?FbaOaa!xǒ%K8clntO8|_3\ߘ:u*qW\c7n_9Xe",k>@;q='āt]O?=go߾;Ȁ?]˖=#72(Xo.WwΝAq\5WĢn5 95T*XB>$&&:&0 H|~̚T*ر{ǝ֝HB !++ >>>N6LxgoCͬ`n7!6U;q-&B!yyPRB!wH99}ȑw>B!.B k!BoDB!($B!QI!Bx$B!FI$!BፒHB!%B!7J" !BoDB!($B!QI!Bx$B!FI$!BፒHB!%B!7J" !Bo ӝ!B?%wr$\IENDB`helpful-0.21/screenshots/helpful_docs.png000066400000000000000000000337771446151713400206370ustar00rootroot00000000000000PNG  IHDRo[2sBIT|dtEXtSoftwaregnome-screenshot> IDATxyXTU0l&an ffoYjofjYek*.# Ȧ." # ܡ b<̽{;wQxx{kAALw  =A   AAd!  B *AAT  1AAbP! ,ĠBAYA   AAd!Pgڟw(  D%C.aĔipJذi39sGA*"T(JVBY|MU\JeY瓒©g8\AI:Sԙj̜6g''&S*kxg; AA0Q4,^U˗ѸaCVBѝAHbb"wb]zy8880n;Һܺum~~8t?39'Okgf0а0&MIݙ1e27n>:}Пsrss۫'=et ݻv%;+ Nӳ'k*M NHݼ5kdgeKK 8GFxd.mpBˆMkѬ[$;+ˡ(Pө#'O2*R%  T5_bڟiLLL$tAѪ7Y:KKAB(֩-ُ^{c }kFAnn^gd+V*9 cG13.z$$$PΨXG LLOcgkTׯ(Ri\mAT`kc:=[;[RRu?C͟v޽(ML3SSZz0HRY}~V Habb_ӳfn¤eAXT4nSEccR2*ajjF{{{j5'@՟w^ޓ`Ysc rWԯWW7ϒr,0;w^:-JgAAxjgk;O <"KZ9=w[]"#3kׯpe . ޵'w'TsѨQCW*:YЫGwJ5|6e*ͳKra=zDƍG , σQg*/JǏYh4~Y+'g 5Rwu&ŃЯwoj|5߮ ==BЫW`n魻iVF)=r\V={/=={8VΪ˸s|D_NZ-Lʵ2h|h!Z w e{%ճ  <F;Ȅ zU7o]o% |j5KSf~#GIKK̜17[~Fйs(Jlml۶R/n`ɲ\5VV܈e|Tr,^kׯccm\ϴO?#"27:wn7iu =pws# AyPxx{kw   B *AAT  1AAbP! ,$ * ƿ7ofz2 ߏcS;*9꿏a֛>wT~c5ȫo^7AWvO˥̾{i@JEQ*}vʳ̘:E si}pg,ղR⑫u{߽S~mgeiݺb/"}~$G ~z&c/nн2~Qe>z⯗Mhr>+J:wȖm۩($ *nr#6n@jzF?ߢiᡷxyxNO'2*Z7;R%\]\8w-[j*$'7̊$~~YY٘>ǑPq;v\*j).硢WgމOLP'O淍ҳ[71 <"4nP.3irr8~DiTH1uDp0)2MRZ !h7ӣ)!j>e( ~ZhA;p@51ˋR^=jv-[V*[7}iZQOH~z> &#!!-۶&Ҩ^xV*f2IJT*w1淓oW'iݟx:k$33Ы,^tIdL<]}|6d05j8-[K]Gy{`,,,8|(߬bANy9l(ÿS)1KCӫ'f*)S,GyiRIsKG 8hBԵk;@AϿcohz;ˈc)aI}BBFlmISQT4pqazZ;DFEq#6V-fPW^3gYa')){V֘K*+++#F?8}6m;;L'sfo'+Vޞ^}AxڴnY3ٳo֜V5}tO4֭Kݸ8&LK|9\! Qe\$%'3x(L&l\QyWWLL,^:NNEbR;v\Һw҅QÇnF \p,QvXnegVwҕ+c}]ymw)ٷHannw_oV( h4dee>gdpt *jjRSSA{` MsT*9KP(nќr.>obbbb7D)zL ~%Z3AA0d@݁5)Uݧȱ }H ء샊>{WVnݏ4*^ ۇH,[٠?pW}kX,z͚:4e\r޽zVaffrII:}ڵjѿOoݠBr%{>WӦܽw]qBE ۇƍv5\XLMqwkwP999?yqIBhؠիUĩq3x:vhO135ETor%uc011ᣙ38p0ᑑz#i95hʞB.0x@O)Tv7}"G1:&>>Ĥ$狗.ӵ<~,ŭBi)<_rg9A1? s?- #).59)8Gt;oOddfj8M6[Iƪp ߘBV-jDDER&O +k+J%#P(P(TRwOzL ھJhXھ[|DFGߑW!GYR=u>_d,YM_emmMZJVciaR$77Wr^o2kk`\߮FS NϯJvv$%Kҽu`[& E,WyQvjU21̛;߱$JcKGm!EB]AV j4qQp>tʩB*.лgVBlӛߪ7 -_1ʻ232u9ygSty@r)Nڳq0-,,)ڳ_ 4K qe͘u?рهJh׸ddd`gk7֖lwz9:}63_d(VTh4X[[Y=cmL>3))+a$ܻսvJEG.5[ʂCz%bbw(sPrh 4RIŋyV*n8|X7ݧcGztiiiđN]gg6nRl'N2z:g''NnB’qT*5uFVSɹr>|H;Q(ԭS?ϊ[VFF&*/.LZeMf^zӼ7#*:e"Wh%*<ѰA3gnE4F͚5^'wn޺ejܸqt,--q]X)v(W>r3#"8oIWcgkKͧ<FߥDͺuо];m-7CTWBtDysڶyUZUr,[ ogNmWh|G gqmL{j_ gnT@ʕ LLWBqkҘn]Фq#[B.]±zu`oϨflYqqo^8ծSRֽWn|8s:^o} xIrKgN7liSxUKMQߢ*ϲp#D G"o+>:9Ǵ} CMWcb.Ύy-7֖ vhjrm7WK8rmw)Tվ];L>0/׬Y<9w$ԑo}~rm4ip-ISCF&%%V-[p4 @7}ά |"4Źt |Q#7w.M17box'>d0[0d fN†"++Pө]A#hܰ!Ǎ{XJ®˯xw0n,{ T.4jYgXTzKY7nЮm[MR;wrbB$կ1 bK>d0ouLbb_/[^&WggYR|4skOй^5<^Z꘢EsB0?Veسob.CԨȼ9s077X``7s#Oqٞ3 t Q ПGIØ8rmw)T0}{7h|cL^9w ƂRjMC.R*0 nF?@OyL  g*AAT  1AA*GN./S*}v;_-\Y3KNhI'࠿^4}{"࠿ &n{vm>c{)eaP~;J[[[s7}(3~yީhӺsG궐CykX?\iSbaiaߖMxx͹h֤1mJɥ"SʚO׻GɋʊV%6.J|_/՛4]0s4˯R ھ[F\/X9*Ʌ M=5&&&n ?x?w ֆݺL#m[x>=SSSOTr~}zs71K\g܈c@۰TyzL 򖗗q;r #_]Ȁ~} +^7onQVIbnĒ^:=ȨܵT W]@-Z  N:ogn;X;JYY٘V׺W4 >z(|>;z4nڬ*zL#]}|6d05j8-[uUT6#랺Yԓ|:uѾXYYqQY^Chw|_ij5;vb㖭zytЁysg3|fL[&ddd|*&Ox  gn;ڗQёEh:#5 j8,%[,3SSvzmVzu4]hZ.\īaO\{{-P(/lтV-}ZGܼ}ۨGLe< %%é&Ҩ+<}C뼏`eb'Y~!aCЯ/m 91j0ܚ4Ύl^y3/UʉBmm>߉S5}-ǹF/?ktjZL &Gî_M6zL)U_ʚAE֭=k&{ԚҪOCNdee1x(gmG #33S/^zb՚p_Cp+ݝSgβn&jb(Alf]^~<-4tu-r]\]]ߴ9x(L&l\s靝dldŪU8ګu,ݺtaa۰h=. <.y[E0Rѐ ݸq"4*!.ur%Ņ:;[[jT* \\^V-}QQ܈U˒;\r4OiHiP~*k;,B*WWLL,^:NNEbR;vۗ8;kxJODJCN=kt-OÇDtnޤWnM}G^=>/ݻ^lS)ǔ:~$ *Cxd$Klԫˀ}fmm/!Ou4oLY>KZ-gaȠEf߁SoNNyYZXС?qGՅgt޽we1]激e?Ӌt"fW-agk;M;w>oY\?cVt~vs5\=r% OkΩghR|p.BEsݷ !&&&&zMMMQTXT s sJ'g+k+J%#&R(P(TRwrWAQQ(LLW^˃Ĥ$̈>v.b`,Yor8|(K@є*X[[cjjjPϩi,wdg?@Ғ.ѣ|(WYR#fO._qު7ZHT*Qi&j.ݡG^Uʸw0o|ǎǺ4}d ,_ZCISG~!u#|+8,2|DFGߑW*+yRxmǘKQgffT*%55Mm E^tM%+;[ 3#\~ZE uѣGܼy.4d՚2qL>WBw@b.JFF9 V*+VVc[VVVG2qBBp{j*4l6??ZzP(ز~tSII\ {½{^^ݻo.ALy \kRIi緼' Tѿ%K/-t]-Zʲ/tОթEgPX[?s~yy)G!-͸Jv>ّZ?{BǯAETT4͚yMnތhJp:;qRՈ<=rh(fhܨ> d] ߏUVƍ(KKKj&&6V7͘mQj8:\?Jqv­I®^-)K@eA(J/>=MoRָ1ρÇu}:vGnّVi۽GwLII!%%CQY*)Çz u!횦VS`B5kRZ5O ܼuKw@}YP`aasJ[])fg}'5}Fyy)GF nݹmrOzYm4$6&yyyeM?,ov/>c):}WZ͍y_|i66.^LPVbY,79N> \];o|#6y㐋գ;+<}=(/-}ɁWn4jԐ?Νaj*jc_|\1ع)&xxw/-JbjfVN9CNSmۼBV-WIJNfї_p-AL<գ;nTyy1u\U(##(zt{kcHM}h RC^Cn\g6˿7ڿǞK6JUs*sfѣF0o\4bnIJ߸FDT*uOp;L޽/p=G3gJV'?}_]~ڶeI,Uܾs/-&4,LF G͙G-JbR'w_8H34/\р6lL],--I{5?OwKe|p0M72x_KoojFߝMJJ Zxf{""4Q-Ҟî˯xw0n,{ TE\zX6d{˿] H/͹ `&ZM[cϾrK`)l':ڵj*N3u<ݟy?7nx#"2ˡ̝lܼERcߝſ1VongINNά&?g'Rߢ>ktjլf?xK  `ƍt~ŦtVVե36V~ӉA  "2?H՟w(Jjq/VK! ,ę AAd!  B *AAT  1AAbP! ,ĠBAYA   AAd!  B *AAT  1AAbP! ,ĠBAYA   AAd!  B *AAT  I8/i}{"?JBm)|%*JӾ];jfϚY.e+gՏ V~Cc sdt/p2D&ϟ3JΝ:Rh޽x1Z-qqdddʒ#+Ja}dv;{aZĠB;1N|<]||زm{YSa;v߮,)*Zzbfjʮ!gY# >*۽Oy#57ڿΈCyfM233%KIOO\\?P|#~z> &#!!-۶`nn#ء=}[~ێCF)i:v>z 3Nƭq222Xrn۽HИ~CJ'M<@CS؂EX~jMH|\6c,Yc،T*Ƀ N>C>E*ڴnY3ٳo֜V5}tO$++#F?8}6m;;L]>|:su6Tc|G %lѼS'N`UTbbdqrb(رsI 1  |֭Kݸ8&LK|9\! 03֥ c݆DEG3AwXӎE|:vQ#ٰy aWy{@pIFJc<=|;j2yyZ\ڡ~)kH fyvdyGu뿣KZ-gaȠFuR1$&%qjתE>u\޻WZ-+V&++Ĥ$B^yf_^< }pi~)p5<7[5iН9v<貤OImL9c!i԰!; *y:F;17bOY|eJցZs-MFb~w@IzB~< ꧟f-&WC֭ 5hJHEiCh*93SSݚxBw|%ڵ133Kk7:woncVCO||?xBe{ uxn~,]%U~=.?LMFll*Il\&&&|4s<=4:#W?ƶ☛3rP}8w֖ ;1'N_ԗkTcy߻蘘5a"M$Do5ij4ZJR|+k+J%#P(P(TRw DBPΎddgg}aԠ~RRKreX[cVDYS.׮E ߗ%Mn^eh4IyHmo,wHdt4999}%{];;;R ` +"C9'N+>}Emf֠P}ytB(SU$=^r~"1jP?^ɵM%+;[Inn.?["isƦg[Z-SK%##+tk+g,Qze-ll({yG8];0q\ c߁Z?uО譧Z ♃)i]KhO`3R9n7,P*h5қݼQydddR h4Sٙn[ىouSfMW[)uiZnܸKzi8ծ-[ˊS7Ύ:βUXFf&w"11UH^Njƞ6,  KRtT*Mݚ襻x2k+HI#ǺYS?O(`xQg聦"; y%y9&]&ϾʴX?ۘ"'Y} R=a^YSo64nؐӦJL< w774X>6.^ڵqzkm mۼ,|A>ͼ5}ͼ I8a1;SGW#5*gى͛agk˄baaF믵wp79mTRսΜNcűzu/^zVER?ƴƞE|1ݺQU*Wf301ߥ.]Bйs̞o@KӛO6*\"'7c&TmJKy㱳m>!z, rjwB؂\:h-+`Gc¸03} }CV$&&E^yv|Xm: ? +J~K0knj5yss#esyh]f3jl{h ^)paɓtwUr9Q#܌c\?UGK݋cn&JӍUMJW'2:,Ekf%S!Ͼ:L[P(lZ-۶;ePJuB^Q.>orYYtꈭ G{ޡ /Z;|8A-w`41h4xyx0oLJ]c nCqN! ,Ju  BabP! ,ĠBAYA   AAd!  B *AAT  1AAbP! ,"[OI^IENDB`helpful-0.21/screenshots/helpful_docstring.png000066400000000000000000000552541446151713400216750ustar00rootroot00000000000000PNG  IHDRm UsBIT|dtEXtSoftwaregnome-screenshot> IDATxwXTҖ& "v1j,5%hbAɵxc,)E5Zb1 b,]"d8.,g8syg杙LU4kJ@ \cw @ (a @ T&@PF@ A@m@ a @ T&@PF@ A@m/"hWw"`)SZ-))\ c݆M܈z•T*o hp@ 0MTRJ:|7x7i*WT!XZY=p9vϞ-@ <_icҴt:M;| lX"9b@ (GdDŢ,_ 6j*$>x@7z2[T^ pqqQ#iۦ5ܺu;v/~\I7g.?Nn]4._fiz'S'N` ۇcOp'>#Y_Vo7yOj֨NJj*AG_` iӂ7Ӣ9^qR~AZZ^ӊekwݔw~DzFM '/yWҹx @ xj!B4cyxпoN[Z<Φ+ǎalmmY+wsJh5{,CʢkyM3ؾn ;odqԨQ؛>|YFa ПlZ-/zAC._&<"Bp._j5ۋ>>iY\ C]:3exINN!ebbct.)^@ *&Pո%Æ Wm.zS&g;پkُ5j3fsJo2]mn.S'N`$=HV͚\w7i "#qT5mYQ1/ۻƍŻ4W/OF.l߸gÇxsWך_+_npBBC Kr|8M%Q* Bo:uprt$;={yHWWiѬ:_V+NzF*;;ԯJEFg,v%<x*99$˕t~oԁimm#)&ǯ0s\e@ C6 P@jZ~477pt w+E{,-yՋF~T*[G99:蒃_,O޷*j'y%^@ 27jhLL,U*WS%,--h4XXX Z 5U+2<C6Q(PKQ[OwW=ϺzE`, nwӤx@ ?Lzqrt7ӧL U$%CZ5owΎL߸id ay=65jhr"#wTɉCq/\=\={),NǾx!M7699Mg@ &-YGh4^ƎaҸ>Lo~~rހ}ջ75YRqpp e Z4oƈaCiR+<8S| 63bPNaCjTZw^zz:^˗~C\\ ]97nVȇ'q hr8_/ [}UZo?Jg@ &iZ$%qH0c'M!m;w~Gܝ;rN<._[~2>d y[@ H6n޺E_ǀc+V_֮`Kjh=E&-@<"ksݠjTRQwk٢GSӓ4;w~fIѷכF_ʘQ:׮d=jq4jPCA=6668xoW|OnnlyPӓCޥi899ϦSt}C-WW233J]; }n뎝|{ug??fϚ:iM7!##%˖sQ})fbgbdzn֌6~Tvqv\l# 0$yCN8NNNl۸pwgO?0y8"ӧN͍QQt{  _V@a|a٣O ,,,ͥ7_}c'Nfk18Ikشe+t 1 '&2hp,_k[.ucc;q2^1l.\DPdɑGu<ܹȲ+IIKS&'99'Oݝg`뎝,]gg:k֖t4t8\!l.;9{ VnѼY3zxкu+K%Oa '--'CڥjNKHKz7Mǐdq=ٵw@ 6j5ّ(܈yt 44T{QZT*@cdeep>aWGoɑGqp4jؐ~&m {$OVKzzzS}عcdqr6̞;7 =OSrȼOfS IIt#!}zh4~%9C@Go+@ (ozˏfTXYZݴ AG ؋p]++2i̝ fښaêWwb1XXXiSi[[[/ǎ͛( V[VJEHijuIFd_ DDFRB@ ϊR9:8jR1lh'BA*U{6\Riht~""2FùIF^2__}9\<Ģ%Kh4&GzɉdDpaڙ-۷jN\ƭ۷ e<14RIڂY@ ϊRm%&&dfdjeZ*~bb4sϗ7w>Mzq(({;;wʸ1ReMࢋhZP( lml j5..(rc >+)der%JNNNV(Wz(9)hmZЯhN]n޺e+mORi1< n*5mR&223ٶs TZ~F&*᧤ * ֩cXӱ};UD^ATt ӧLёCAAF~jR>͛?L:|;u<7@ 6{ku{">>-ryj-Mǎ {1k8;cccR'+Qzu&›CNxGBBCQ`1XXnكFr*4)mݚժq#1vQ$~׹ xo?ݻv%$4o 7~.\DHh(3H͚5ͼ3Y1%}y4'BBHMM3 ѣ#mڄg:teOL7%˖S m@ (6ںvL'WHX~I.^b̏1|(gB!*:=giSiۦzʄuYRMȺ_!++=PөfR87ؾ='Gev\~AF~W0~JeҾhuhv'CP=ay6~}zLܝx6nbvyL Ƽ? jD˜5CZ-7n23\.AyC_ތ[o";SʤM @Q9qYw < 6@ MP.xo?:p9~Z@ !G@ *>A @F@ A@m@ a @ T&@PF@ A@m@ a @ T&@PF@ A@m@ a @ T&@PF@ A@m@ a @ T&@PF@ A@m@ a @ TJ4f4EIDZb7^T* oǎe^W-`i~ؼȯIYo^h*/qQY?4q(`/܃ޭo^@T?{n] /+Bۓ=;$sA91SڦNE %yزmG+G`o눊 A/tƒaҸaC q-V^< w>1 KKKL_&?RI.ٴxKhR+V:u9;vtr\Ю*[[#==~Ao??Iٰy3DXIJZ* 2a<)?yRΧDfX:bbA֭ 8x5y5s-2o~=/iW/؉YnNRr2%]M[JG_u֥nl,c'Nƫ^=ϙmr1WS95^W#hX~aׯExDlY{Iqb,r!OuTeff5 +--'CڥjNKHKz7Mǐdq=HƂKIݜ:Vr%G7䒞_OZEm_j8"]#:&/m'O2uhѢ9_,ZlBF!++KrJnUvPR]'])EMϲ~hXyĬF=s- 9%#GҦK V{&A}/V#s  .<;~ߍJe˻f޽_`¸ժEb ӨaC:37/iBO}ع?}op2^u߷IF[}z1~]kl^(܈yt  aZMZMjj*tܽw]r VE]br1WSٔ1^ک66‘C٢+ZBi(]l*Wv!)).~~<_IFFg?mrtLrʻYuKn¾@fNΊ~4P(hbK>N3ZW0j5ƾ?kFa99:n0}G䔫cPrd#:2M)mzsE#f5ܹCrJ:>>.&6}$X222tdeeU0ȠKjհDTLjlѼُ8zAZw>/ g1hQ(0M|؛k+KK6U 2xVVViN]F6կWggΜ;H a}hܨ! &Ff&}2M]qgJjΛ3M)mz\=Kj=MƤ0"`g Я/mn.bђh4tKy 5y4]:?INN\)0;99lő7I퍾6j566("'ð4 '5-UJÇ1b|O  U%;۰rTژ$]N>x]v u=iѼa/Ӣ ceeիG9n4, FÁCjglΫ:q5n6DݒRl:/?ctYa ͛|L7Xn:dTrgy >+YO%JNNNV0)殮1V+K7L%` ?X(/BPia/qV/ꇼH_Pͳysu)Mϲ~~gE>!#3m;w@ժUBƖԴ4J_M;"66tly^OpZ$>>- ZZd0t:xԻ^:Grx8u=v7!Wfp {yѠfϑ.9%9#橹&Oǟpyzѓe+0]%廩qŷcNZ&9%婤aQ*Ⱦ@Ƽ?!!Qs6m³N:xR}s|Ix)ݻ׈KRRQ)ΏxɩJC@&~J]wɡ=iղI[ZYz'1Rʎ\L{ڔgU?CŮ hﴩ-0uʄIǛ܈cL0qq_a/]E,e䉬:nnfϝaק7.݉g-&DH ^Đx[77KLZ9 Hՙ=s&VÂs%Ç2{,4c`4rKr1WSs '^W^ETrܠgҷw/egCNUk?/L᳹bRod4,]y?˜GxPa\bXVeMͥPry7%}qÆ`4}7K[&ۏxэpa~>VR3RV\yz"INNK/4GNӏO{X^rt8Uv`J]Wܻզ<"cEVͰD>?Dv#{ӧNՕINA\ ֬{m F@ F@ xo?:p9~Z@ T@ &@P0iNR{d=d⵮2[ԪUtu:ïfn<ޝo^L77Ozzzth׎FS˵&w뚵Cng4ξE>{ >y#f5whi6EtLYx=%!"E^ۍQY7cbu߷zrS6nؐ!xD\ÇF~ ̃B u_ߋl!k kd#ԁ'Oy'3t}{<5[9-oF&mi@,>~ПwYŗt:8AF 4`.~*7lҸ8s\qDÿp SB{,tL`PS TPU$C?V+q^vC?yq*2 eը^k†&G9accÁvE^ժUa$+;[v-[4`:Sгk&\] J8_,Z\߅J];C?T6uz+KKvallт#IZXiMu~iãԱi=OOyMD||9;vtr\Ю*[rieee1hp'Cغ]333M k1|ȻYk ~{ yq88iW/؉YA?lL~!u6mي+yo?CG3 0ɑ\yZÝ,[Txy1exS8~eH[p0Ym WvH_ŕjC8 dJ CSZ P@[&!w+.m'WɀJ*lSe}JjȊj0\} H)]?`ĝ6H Z o!`mo蔫͏\>#7kƚC]Zn7^nӆӧ{^e2tJÒt4t w+ǭV-rsM\y䦏d. sWCpi JeV|V+h7"ÙPd=0쁪H]㥏-HRٽ}mu_Dgi ʆ,Mr=Jeu:.[AVV v -}|L2ږ,[gs?e IS eǮ߹~coo`4gN֠+~Qo@X^tzcUՒnyO/:Ưk׭5387d|t:'BBppp`z#)M&M&55 Wsy̕qp4jؐ~&7zƼIBb4U2bJ<^*^PѦPBi`?& | 81hAk7ή*By2_fO?PX@{N&$^åQ!v 4T=Csۛ۴ayu S t>GD0!K} $+M9xϽ5W#;} U5/9, x !;HFYFV#}\dJC7AÞ=rQrxt#@)lڊTeǬ'"܉KVVٝLegˆ~!#FҮm[ڴ~vmϏ PP0(Q{1baaMe_#" P(a0̐17Vx7mϫV^Ɛ2M3\y̕ П~TV +KKJedOt䦴 +`_,,BYPJ1P[j.p p~=ý+mN_H5مO:s^ڮ^0qU# >%):}=R(Z4oFv#;n/kР>p9 `VR]4'69qɣ,2\ezS>!W`V> A*Q~(%EAפG;g!;] Uc 2 y,cV-;۰Rb4m# `lmm7bl-oJ2pϻNϐ?Lys?[Ȁ~}Yhss9p,Ey~ciiIڰTCT2r0F I@PPJ5\\y̕cMN~|ODDF(a8W2CP )][KՐtC2Z6NaWBZ_VvRAw;y(Tr }''iH\S~Ð% )N> ƭSRsF3\is} ɉb\ZR7rJFAyqɣ,2\eqW1ce/[y.V eD)ݷv>r, F\8<2((;Ѭ,1qX,,,d-jPNOF5WÇF8ġ ޭ+KaٻoIT$222hS_gfdjeZ*~6y̑]|Yq366eDEQZ7K髼bh;AN԰\PxŜY n6ܷI]uz,CwW@}Wl6ij\]P(E/89:9::mr덢>Vk4j{9qc"`2. WH-'C",T =!e(VK=^ε4I2((;D' -$%T;V/D2S(n".\(ll۹Vbt;=_NRÜddd6EW=O-ks4.SÃnuOdi))OBP`ccKjZJ_M%./ uZBƅ{.}_,Urs5׵sm OVfa19)yru:Y'wmT/le;~mݴ)7I^St܅ 7Ÿ]J7Ņuˑ\G޵+'HŜe0T^%UZpk?,iUC/4zxC.<Ԑy0q ?JKE(#hﴩmZ=ex~ݺUu7ңk|(lmm{?_~^?4rBv2Qءweg pI܈cL0qq_aM\m&vlpU^Jcjo)|6ST*i  F̞9kk+T^x3?b̞5 &OtLi'Pm4%}pio8l*/mx9Ԁu|YXN5t:1sD)(Yt'osmu4ik:gZPw bDCջ C|8cL8d2y!hm׫f YƵ@Fԗ=&-Ԩ:LIbR -ޕޗ+=L2MF5rss-3G:_PmKgU'5cC~'ho )0䟪ɳ,h&QڂFoHt5-bt)4 7qyTMHF[͂6&\VmY[[3bP:RŅqqlڲ@]AԨQo̞?w>u nn\۫]hzOٸ6sa-Z0z?<=ISپs7m6Ϗٳf0d(N@M`ɲf!ҴIcgmqK1h[*IQPu5}wuJBqZKɂjڎZҗT퓎K29TnB^obeiɮ?#WK*%J]}?ɝIѨA}~p!]%\:wޡ+] EIOO/WG%!NS#G7䤡?% #OaIyѰAV~-۷پsvvt֕&tU0 6v+ 9'O`ѼY3zxкu+}r6̘>{rlIڶn)Qqݴ 2b^̝?ݻǞ}OLdX(-XBeiW/؉YnNRr2Ϟ5nժEnyw9DXIJZ* 2a<)?yҤeH_.>W؃rS~Ɲ6H5PSV:&55^ztKaGGIQTnx5y5s-2o~=/'#GrQndee1hp'CGeffcΗ\n]2vdc\t`Qrpw3غc'K/ٙڡ5/r%?rtCN#ϣrj>RN:ˡA}/V#-#&SR%7/ ' ?rtCN#ϣrj<JCݨT;mvK&$&>0[b -fJg9+ >yiqX={hm'6YF[~c ܵ%, g]Ϟcn &uGDF덞( }-+KK6U _Ɛ"'''67 П~TV +KKJe'19/}je|r]$PUCOI+uNR{r2!U6Ԇs4TRXy$EA@0k(,OFpNޔ/c sFit,ȕN]?œ HG+cbcӦ/:IC9S!' 15 {͕P|}WXt:P٪yݢ[j a8i\u:HsbBQ('#hsrr"9%Xޞ4a.jlmlP*W<&IDATJ&aLJ%NN(JÇ1b  U3ycMN~|ODDF(a8Wr.BB[mץ7zǸ:%[!'W+uc4o-Пd_+`%gSt<t,--t55-US.̡ WlF6WR͖Αo0 חE_|67hR4KQ@Nџ 9i(ǏiXTk<N#q:祷.7RL->dSq`2t5OFѦVqqv)7,##'GG7GGG r%J~E7!3#V/kW!_q N~teMݗ?gcc[0CŐc(JC-l hP>mǏQl /St<Z;={;;#%!\C7Je2gdd-q((H?A|ܘs)첁E@r(s ' OI~L 9iXSӰW~gUY"dA_RߓCE(]ٿ`PM >4 2OD8"66tl༊k"iapUKEFr 9Ӽ917eV9 é[~~zhiҡ@P`ccKjZ~OJ_MVARnI+2twR zTx~I{8\JY&q vUkxC-y B7t:x?gkk{ڦ KTDRA͙d]$$$Pjij5NNN<)BrPNZԇ%aq~LICȑKuR8T3~Rmɟ)m0<T ԻJ'wwzޝ: !¥K2cԬYhjۛ9Ғ}ʴ9vm۴iSfϛo֣G9G۴ :u%K]B)ٓΕQ:~RϫVhhr8v\\ {1kFtڕ}8{oAHh( L7 uXJLvqXZ6GCVi[XCMXQ|a<:@rwc >v7̈́aIT݈ŷcNZ&9%Ev(/,'Ыg5jȩӤҶukW^Fk܌%))Y_r((>/JBnʩWrPSҰ8LӲ`N}6WR*B[#HVjB&P5j%@Ai_n I] pӢƍeɲe_X^9lٙ;lܲEDH ^Đx[77KV]bXVeM7;m*m۴_Où_\x3?b̞5 &/@'K% o2mD YYY ؇ZNm7RGt0/(6ӍWχχs9sOݸϝZHY!~*3 ޚ/,uX;ԳB~ݱ*v>0ЅԳB~UۣB!׿=*Bd&B\.F BܚSj5'*_>DF)&<ÜO>DU(w*b}{/32FׯKtt9|1o.~-3Q|w|]Qb0:lW9D^?umU2rP# RPWWG}l^YT;]qv;iX,K/Myư3)IcͻSPYjMa8}GMp¢",Z̳O?Ţ%8]AA<Ӌ/Bf1Oa(p"Edwʗ`uֽQĂcOr2@(F{kmӳ'# n:;w˾rE&|=Ǚ |/TDZ7 ivL"߮YCIϓ'*ޜmڰE$$?8sfϙKvnaLzvYYl~4iBtO|fx{4'~k)!L}e WO?%F :w숏c wf,ZBp<>v X>Ee :j la'˿.Nmի#ӹm֔QÇ9]x@=rF%f\Z^šۜhؠ6]/WT<գԪcyp]zՏJ@xV:}'iSޚ:|vqzݫcF`MIaؐ4oWO/\`1x=Xō??䪯ݺؘ,Z?b5e6m.=1Tyq >rNmѝ:rqdd8ڹʗ•Mt%w %b78mmW'> #Pxa^&`U{|*Ë0_}k;3-$;#̀_(Q?\wx {3gpimp~:rY]?ڴ hk3 b 5/5oۻet9v;̞Caa!Ο?so۶/š0yeOluj1OMocٱs'ժUcA' @vii)ۿ3fKB9]x@-*9UiJ.ThuqXmمROiռ5Og-ءj^V6z (#obO]GXǢMy\.{aD`!GբNr63鸖E_Ųmv/[H|+.xc.cZzţbDWN56*R>wPZ%>?{+tuKaSe{Sj4ǿx-8Y1]} Cqnٿlm,\ooo,ZHaaٳgӶ]ּ>^#2 EE$$nw^ZZ'G ysYz%q;Q301g2RXX}R5ksZz: ~DxVoMp: Q#f19sl파 G%*9uF5fWPm<YEUռJ=U?9/N>#))4 `0hWEϽ!!b.^$-Ǡ-FG%*9uFKBJ_/ģ1WĝY1ԳSQENfS*bji q뺘U#9S7^WqmDi8zns B5ul&Q#}h+55ȈǶTjrn^>V1TyW cys'- ۨ_]&$8mUnCKdbBLacnuYۇ\!

?;о=[3mێ6{ybXbzdgR}}@5|?;2%իG4jؐ6L{&v{hLJ€<ǭjʸ<ܛ-[c.srEڵٽg6ſ;XKHܾ桡5i5ǕNعKs<+zģ W9UmJ.TڨԼ (UlJ ƥZ%L5-[Fƍyk>zU0qx&&r'ƍ7`֨Ʋ5SNM_jYj5L~Fr 4"M񂶹ow^ΥKYÊj^Kl%O}+fKJk/S)im._ģ8Csmd$=3gzS4֩_j:kӻW/]7!N>LzAGF|6nr9Çҿodb͚OHLǗ1x׫ڸ8O y~m3~1ص{gT(jr.Dc65ţ2.Dxc/Ucܹjefct̀X:o6lǎ&|/\M48/%}{Ӈ\]zX20K$-R0cQbݖV(j5<̘pjSZ3xønقQؙ#Gsҏ !_Dyɷ;[`0d,[T (0v"*ۣB!;W.z`Or2E jظI;@`0pOxk.Gs]WnEBjV6m/cǘ4~vngn\$iQ!B7pkN)B!t%6!B7 6!B7 6!B7 6!B7 6!B7 6!B7 6!B7 6!B7 6!B7 6!B7 6!B7 6!B7 6!B7 6!B7pUOmۛUIENDB`helpful-0.21/screenshots/helpful_props.png000066400000000000000000000261211446151713400210330ustar00rootroot00000000000000PNG  IHDRprJsBIT|dtEXtSoftwaregnome-screenshot> IDATxwxTEۇR) Hw*݂ +/*'| 4Mz"%$$` )f7lXkaϞ99ysI-UH$0 H$I\"H%H\"H%H\"H%H\"H%H\"H%H\"H%H\"H%ҺUKۦH$-̕233cxsMgp6l$"2Ҡm^'''ΜťаJѭK͝.(([~R <%&r:$~YIrJJ|\a䰡f-yyED9 c0~h\^=gpnS 3+;3SS\Я }oR5Ǎ`݆e:$8ɽGiD"E|sxg;pkkkz쁙C5=wSӓ_ u~<|_ƄUW1~=wo!HQ07+׮buM{ܺ}q/K曥c洩ZCsIbe䰡93}2>pf3bh ...űqV6m=R*Op5WǭƁ[+..|b:u@rrrjɷu"G_~/OHД2pS )):rlM쉊ɏ˾i۟[7s_Oyk>m*Ą cлgjTNZz:Ytys"]}zcmeSdegS׭f_oo 8pl>}ʢgn 0t:o3~:uN{} |}e;m;aC3sT!';:L2G |O聩Ia2{2Wh˵Qoߘ9dddҢY3fxSoLujr!-T°!yݹZ=qUM/ƕkϒTTTtt}ĞC8n,=K\\-7Źz%I(Bf2.=sNzVaMddfr ztJNc9w$M]V&Ӧzz%ΜPvm_)[s&5.͚6vm?vƮ[2hs̘cG?Ϧ[1XiceeI.]V 7K!33 ssj:\/; ?ë'?/V-ٙƎ~Jgp-<G~GUϓQs7f̢Z5'6][Kffw u|mRdOgeٽo nڿ$(rN3ŗ5|{Ύ)B*UX7WO+Vҩpҩ+-=>-tlT)mNrrr8rfʢ^=OlVΎ xZ GhG}V;407/z{eEݻ֮M]77\bcCn^^)ojbO ^=G=wcN}}26ӄ9KzHE .>K/+:5ͨXT*Μ=G½{uó-GvvGSTlν{EIIԨ^kkHq$''SJ6I/^b"!!ZrII%>洴t 5t ssZ|qllc(ۚxץu˖ y1 p"KP*{ԬQV>KxׯcO}ɓNfff[:uhڷĨTavm8Pi9mQ:*[oT,_<\>TR GDh_MLLYi[}lؿo'''Mï&|1eWNN:H{{{صg5>D")BݝGAA9(?wވ"z>IMKJVӓ7jr,qq\SLfSxixm$nݾM]77/ѠR1ml *m)n]7vnބ8=!g4m_n=/iSya8Q:[oˮ4bpqvf⯸sq Q k++5jTgwةȞ޽zҳ[W"n 99 *$ x~~> XD=ښ7AmXs#G%˾#p |Jo㖭狘jggbbq6>̘67n FvCť0wk6.]2TO#HG ?Z\pqvzDtD1733igPT\~|jj%DRBH$ɣC :H$"D")xk wp&f0v-﹯ž 41݆`Ƞ4k`fffڻ;طڻC{w댃P\:2o\*.D[Qr`ksћH&OWTDEG^b[s2j+P@y}JCCq Q( `e#uqPPPIO-Bc`8P^6z9gC}_pnT`eCSBn0`1èE/+; }fc•YƙY@Q ." Cq.T}?\6P\!?rm}HP5EDZuZnG>'oƏͰ!ڽ?JޞkWc_5=Y9?\Q8nFcs-,YV͚1ʼn~j*lezkii ѭKg99qQgOϞy~j ֳ4eieӖTR>zk^Y 3gHzN^n[w畿jSŅVjc=w ?0v ~aooݻwYajvĸѣJFF:_E_WYMEKIV.EaEǪY_͛6 /SP؁WS8B!=A8T::zD‡ & Gk:1tw*+"̍i68? 9BENKh7™`p95']?"™b]͐Q8jvn(n['OqCP  CCf|K]YYYÊWؾؾ#"4ې؁Z/ #nаpl b2 SP{tH|L ֦b${s;-4rn,সv9;59A;gm_ׁ_̭H=|/$Cεȁ_pA"}ЩC|L\|^}E긺bii @PpSSޥ+5յ6{ +:rT'sK׭IQ.kkkyYfM't>זfܹ:CΞG糌 2 mgRFS;: ]Wp7h24C%njfٵu3vnSTʭ)oϚIMuSFI?+9_JP.Cq1+L|59z\܀9_#ҷ!C1ry>w!:nQEPsjLLi7V.bCME@g&SxUMx̭tG*?b_VQ%K;Ⱦ_ O(!5-ضZxB٣lD]rnV|*U`ffƋq:FPzuzpۥl޺MmooORrRUV״+5k++Ju}2'5##333HI1I5dL~eݻvjx84q<2#2|} ^QRR~Wt]"&no;Bؾ7o"*Zsm)*]yPLK8vbVr3Tr/!b}&s0L'.Q *=vc'"N}G v lPT:"{cؐxzxеs'~]x5咑A~~>?cFb ՜8ԙ4;SSSqrt*W])==buvvvdeg=sʜ5dtҙkױkgVV%GC'ƥ0UTFI?+9_JP.Cqqzcv֚z|= Oւ*Z*.!xC,L, 9LQ<*n|he%CWfn]`f kEmj7D]EZrK8q}N O(EmMs-|X(fx;;2e3)yRs|zܼuK[8))Ka׽D_% h] yf:[hε;nutyӦDE߬T6I_4eڔ-[ӈp(-з]e]p1\kצKxsp;_#\n^]%Eg'oB6b$FY k|nUCz'-N[qcsGe쀮sEDbѳfB='6hP85cZm,_̍,w72ܜ\[;}zo(g>&2AAeNDEGӹcNDjj*Ib5+Yrr9~Bx{y{%3-j׮Mus?| O>YoN Ϗy-Яssk|b~'GG05'сZ..%.^ >ݛ$&&iT*gϟc@~c ^i1ѰaNB֭qqv zQJWE}]jʻhnݾʹ)S'6kׯP׳!P05pt9c0|_M_u`7ptD/6L>b$^<Q%EC~,,EԆg{r oխ~7]RU;p!Yp`T1N-Sm"M@AV0-PwZ(,magixyz|,Rؾs?ݺnnG>>\j}\\4l؀*66%7m Ǘ\"oDsnW,yqxv錓#wb?tӫ'cE-Y~=;v Łwvl&q-mXg_~ű'=sn\ %p@ٶs?X韅}H֥٥WmSSSxUz莝-m%jTά7ѬIع{usc[k܈c㍍ au:QJTJϗ>T.5]_G~&./9R5&.RܪL+v%<P8#O"OEj;j\!},zO2Z&O<P8#O"Rt 쓓!Xho?j;NwBVV%)QHw;p"ڑ f知>Ӧu+GFbii)YYYܽ˫/DZBĽݻvaİѾm[<56wؑ!_b[*fffΝ3bP'{>V-bؿ> 8Vy*uqGWcؕ"89iENP{N(pnAb/ؗ/$ۊ阺h5($PްQW#Jέ*QX pޮwwߣhqVmcoV-ƃ`6.NPmo&UA>CK/߸1le%O%p@~Yѥw="&>C177tHq\ s 6< GQJkQsSqU1Vf3 .9ǫׇ,=DD  :,iOA8MѶ>T׵uqk6(yܲ9}^9c0m@=(jr!..ֳݚ)( kWi>zTחKǦMۗM[jlڳo.ST@.]7wN {vϋk>`HEryٜ~O31AK˶)'] DR^7CFzmZJUk@E9W*ӇgH~ɽTzOۀ̞]8mZf7IMKѢ3)eee)9>!Q&`jfiSZZ:nc̨޻eX~= s*(/Ml;׬'}K*(=YHEyTlVM*xO1*n, es\I=[cLߢoFkӡ 2W :W= SRTlX*Bؼm;66֌5vqU>:uHHLn LMMykt._OF*(=YHE y Inp /K8p1u쫢Q)6/Ʀùsu> 9{_BQ{f83PTdeeac]Je3z,SM:^!y*P1$*?E*Pї ?2>_cSZ*S䐀T033<: %ڹ *nj*R#<"O<#U 1ͣ-2SQ+cogkY (=>ޜn;O*(=yHE y* `zюQche%ϵk4oL糖-s؅Rl7xdfewߕ'<;\n^]%Eg' oB6b$FY k|Bռ'vet+sR(_D݌&11Ic xٓ` x'''>?6lSRhӺ5.Μ=A/{D;H9?6Cx)wNXK +F1ձaCqpBIJNy SJ7'Ceh?Mm*] <۾!մ~omVRc]DLhn>TJpϜe[PTYwY܉SAL~e*;hŪO߇PF'K;0a%//QܽJdP XGѷW/⫯u"P\P~|V]+gͤM@kMSмҫb.n,vhԠ_-m%eV;2S^]rT6Z 7nб}{ޜ66ܾs ?%4,Lo4"S(y\ GT"zr%7re E)ufC8h \"Q1ػxVAI cY"(GqG{xfx%7C8Dej2MFo_03ũ^)C)TTOIb[%elf!2zvSwEȃf%c;Ԉ4EE2yt,HJ epTUb~S`_G<Sy4J*sʠisH"5"C]IF"yQ}zA u_DNU}W&sezngƈhc(5GJXqCYh;UM;Wy Sy(iK\l<53ૡolXxȡitr#R bDYq9Y<8y!z FJϟ~aigCN/5- R zM&\'33wdfWď?#Tuj?҃t爋g[g*W NPMf_P9ޱ;op9ـ=>i5"Ttx2Na:AT%ع3'}RTP;"12H:P|E+)_F̈́uE@UjW%/2:Ueδ3]jؘ}ZqcDVڶV-򘎀1j(Y.zZ4I&:_pSFNtx2q5 jm[ĭQjn7mZ=ɶi П}߶jvmZKj"IctIo!@Np9ABZh*6#$ff4oRzG S4ݾ쓆C]Kr)/FP*3&[pL#KqKmD;liUZsN}X'uO?͆ea{*Z'ЄMk+eϐa3"[gH˅4Dty+(=w%>7oݖiC_x|ɈX}iو0܃q4FV$ۙ5ۿ: ?\ gi9"ce+FDQqGvលs? Q!=z~ԫ'V=rՓQ*NعeCGT&.6[5ҧWOT*CXTz4e.2q<.q̞zVzcNK@eksgjrŒPiI]ӛR4TTtA6UFT*>fuUͶg5֯:I.!u޻Hndф:-Fwܫ#F.X&~wPR{~Å X_Ó ~ @]tH6ǖSP(RA@*oI_#Ӈ"瑱qQ &ތƴH]%Z j֨Kx[1c.૑iP+a+f9),&\ Ħҥ=6-z8˜z`i y>6r)%G-"Bq}W(U6,H8n{7NCD\>pqڼ0'݃\9#>ČFMo|3w"OKв|m8n뒃NcCnͼ#-Ah!+šzWyCH`޻8~ [6??_+//Orr4 ۃ2jMz9q+geF6Ʊ>6I9L4YsW]e|J:m'7Fp @{? eG> kvC4y6J#8V|?y7 /%xۧyW">=nXV-ȭ1N$ N2, %n͛EҶU)˜8`LLݾƻc/%L6Jlɘd+8PSf1OWKqeHzjגWmt][J"fx M=>7M:óCGf#F}rPIҶSg ?h~sj͆ҕzw %R*\f6i@'e))ǑruKKT:{$ H͌l3NеU{j4$S{|scO. 1q89ƌo0SX9(}Y;߰޸qWdfM9˺Kʈ-_̳`Wa>zSuNJڶ|K 9 ,CDx-ײpڢy=O^RfT|w-WQ-i~RnrېqtF#l]ye]Ǖll#vT4?o;7obFwf6S'F6AѨaC@毿+0q8Z>̓e/~lVq$ Ofd1|WP2@  f&$ލzi6t}ikH5asB:o+!0 ᷦ9[Ux; FzD(j rBAzP/7Z'r[^nV1惷L5Kv`l@ԫ[+quu%99{oxiZw.#A@L;T'Ke:5OG,b?SZCsDW8-ud`.ھ}bA6-:Dvwe3#ʸ8LQU ̖^cD}y5hlwl۾=_#YkܘR-mҁV-iXՀP*ܻr Q~%/nge:qއLWg}?Gmn:D'?.zuJc߷o~w^ *Դ$; 3dSRSrӖgSMIH'99'OT*&-=ݩ1}4f|q4'#XB5H*W=9! "ML^њSzWSUo0c ^11%6\HL 15kYʗ12c|f)f), "1USB@% ^^v:z0Ĕـhߥh?fMM 4n7!"{Nm]E0<H_|&R7˖=?xD61\źM[b,m)*cU*WtjL\d]X>  Ƥӵt1tJ8Tlx`w KlgHHtCɞOwfӖ6I)eӰ~])1R,=J c+ S%11r(1e|o,٬(aDb)cT*;oBYEbRڃ^{% Be6c ی)Wm%Dd0^n{B>rgfWX5! մ֜igqWLsCņ1ev,'v[}gʆ66Lnm2詛T(FUml:H偏%fxOP@0DbJ@$Kj/+։)SSʪ^}3s ,)'NL}?Ybj3W̏K |iRY*mDb%Db*ߤ1ȋ1U)0V`"1 C!cAgdL-$'H6:]Ur mǎ zH #ZXVg߄؋)#%oXUNJp9 caʤI@HL TQT|'N'厱t]!.6 Y%Q"?4q|?~UDbʙSvV`놋}? O%_oZ ĔQ?̦͑ȋUUԫDb!SR:2C^/#}'IL:`+ظC*Y0t޴6\̰76&lgvFy,)Ĕ=)KZeM*l2zW=Z/O-%]FF:$Sv()Ҙ0vk{6pj$F*^*N);SƤώJBYCRiΪ2&\\R~OђVetLSVa T)bLe 1֌ü$3& "1OK􌉩܏Mr=cWx:LyY4ciLLƛ &>|o}&SΖ222Rb44.ӋӯW`R\%J{ @\wGyrĔ#ErK$DbJ0A @(JRC@@ph|x/MvZǕ*Vdh5'zS@48gH]n2 Ʈm[p5CkЮsWƎIr}_q[jZZY#6@ú r2]Uh׹+C #''#G;kKMtwq%?ҸQ8ii+ %լɮ?`M6Fg/ݻL;/.jp`Fxq<ݥ -7WQ@ 6!s[֯ӓw|;uh_:+ox3<|shnۤ!5]ldn&4(%0H| ,G2#ȵc { aAdۓ^rf ͦz"]۶o}ՕЬiST*"KT-[6Ǐuzcٹy︭v#O7ݿܾ:,Fؘ캃s/9"h*=.n$ ۏ ieD$=Wl sV]gazJ[ա<޴W~[;(]H#IC"BsOǪҖCFлZCI}$mһw5k+' @vh "Π|y %'WM1uJ~صm 9 />Ox둓c6.G4?}{`Ҵ7:5l{.ޯ[G/9 9T 2+/Kxʶ9Rlcf]g u>xx 9m64UZ⻾ AVW$څr [nzi/d5op>5<٪LBB]':J1j[2x:2|ΗQ]. 4Ɍ΍,-K[Ŀj4M'sW(ԩ(ԩ&P@a6~k{]gZ[LH 9!ⷦ~vF"lԵz[h"em1qعOIu^>5V^36Yz\Sk!76rdWc2-!' ?s*єxӫV,HEG.I֭V[wZVW JdV_yNt莖{Cpڈll&0h^Ĭ. Ƀ8nT3:zdWB{iYoOTiTEҍAe޵<\5k>%ܿvUcH4.6t:}&4!.6< I(b7ERW#m3 /һ (tiՎ\ӾDK%fx󧟦aXQ S}ύZy(= IDATd J:'h Q̚r)^O]ItR'fԵ:]olDo{dus9r?\Imo{5Bk~ Cz L;TMvzNZ-YFƐ-(PHuV8~NQORz{\%#z@㺩0sWTC/\&Zj%Fi!J19fxvLmªePaA˪t'Rޣ0٫fm-8捒ygAضomA,s_%]CI8:2I)ZYAZ[NrR \M00|֯˪5qJ#NmqNWƙgB{X4oc_yEtpT*% KCwD*X(ЫGǪӦCܿpAڧ?s\d1CfKM]۶^5X~YަB(z\BHA9 ߼Y$ׯ߰k&v6=ObԬQ?QzRL7n09&Q/rtWVQ{YeC !Wox8܆ @)b|iEty)_`h 7O\l<;(б}q@Z _*st(U Dt$G^#AY.]N>>Qk9[Y K*,vȤ3wS"A*™R,KHPK@ȍ:F H0R͚&R%,!RQG)@F&J,pas˖R;Q9GZ TےH^Ēt zƒt$gcxfN>gmv1l@Jx@lʅ4\߾t^ng tP'?^%~::bCrhKґͥOJ=}R_*,<Yy_N]TΖM.Ȕ(N+Kɯ[DJh"NG\ʉeasJ*jQaP34 /%P:"p.)"Y39*f R%ł(f=df)9sٛiKrL=J(U #9EcmZ/oo(RO'mc{BÓKeՒ:Ӈ_(rRiƖq$%pڵȱ\O{rD)K{ڱEq/K(DYRYhU% bTEx<=tm( ׼ص >^Z:CBC=8ҟ㥡[ۻ9z@Jp&bSJg[s>ɾcR0siuvǗaӢC31FR!GܗNo WT 3fϡ$ճ(UrB *=7vn2xrG>E)*('Pl@FP*L( Ap:LDx\]DKnҵ]7捒Yȏ(Bdby٫e W J $(lB{dq#QJ"eʨ$ ;+t FrT< {eδ3_*BDfs_ yБQ*S?Czs]ߵm :w>IR[oӦ8A9_/Q&vl{5kT=h԰)))'(LYRR+IMwi ^]M1Stm:Sҭ"$O{'>t0ӏ5-V}zUX,k.RA:B\=+,"f|t 9K\"(4Y(jZ)m'Oюλ"IL.^'yvP(e5rcCSyFICN$+S5}9t@Ș *Ou nfl[@7ap.^͛lop~p=3nٷ?+Kcf޽L?T*Tg>lFn&f-i徬H՝xY&1niExB|7?ljXrFu-E#@S1I&^ ?Bɴ4Ylf7eval[^{>u(Io@6?wgJe52^ןoKl%MUTWvR& t9]h %tRDZ] rSix{PMh WP̦0i;yMj' ۡw5uZߺVKZ] NBuu';mc4 O*9BQ6_Mk,)={Uh߮-ݞ]r5APo*/MMLKA^_[ce~qd7x_Ye\EY6nuȮ3AW !)Wdχh*EIPzu<9UZ) ^5'$gy4A6]OfЖk2Zc^Yq}eIzx]LNH4/- nMNv# ޣ 9!zGskxBz٤ixj=נJCe;u]'Nf+d7xmǛIVx=N,5Ɔ4=f]YG !zgxUvO|(N{|z7?r<ۥK<>6^X{qDap8  *2l_i W ) 1wbv5OfcNsw{wwܞ14]:w;uW`v]K^)]v8”w̟g;͹Ι;7j5}UV5~e]t\Vf#{F٪-hq%S. F+_r! 8a.-aǂ^NCݜc_ϕ5/v+J>Qk9?Gxw"es3נfH2ŋ f uQuT$};6k¬=w"ZSˡS~|smx1i&̨+{󧟦aX$fO=KxTrr-/WFo(1pS$'GǩZ1Kr8Cݴ:xYx F4uYa&NdܹPGZB"CUh~y=nnnxzxN#3+ƞv"BD\\\z:m[ےSgQ!ujq}x }Vj}v.+N!;=Mv_H}+rh\;XJˈ$7J&5{ԟE+~Sb1ã$G#wԨ"$ڴqH;_ZB"J#Nr=&JZMvv6dfex!Uȩ38uڵ揿r9n*@a"www'6' 56|Vz|*D2:1d,ڥOI%ᖇl; J\ uAf.2_cDF^J%df+VYs(j}KI"|~gR77^0}L >w.OqQj7,*w/ڼ87O_v/}laL\l _֭iP(XaCWݝ*\MCBQzRLN*2NZ.sןԫ7Zl;{6'O=T1e$f͙#:B 1qdꄅ*HAe( J%I9z,)RATcXfcٷ JlQΥÄ6끛?IeԡR'pu~im]LgW@Ev,)^e8n u&nMaj%n:D$.6t:}&4!.6L=3N*xi.f%.6|̈Prؾ.zbG˃o]2\~1:IIIe T) A:jE-rf];2X^ٱ`Սl!49Z u&k% K*gVULs\}&4!Zj%FBZi6;nŒܸNT}iDS%x22wqykEf. iz(U"GJڏY߾p, N`N^rQ<  =*^fc(+VYWut(vOq>&F())Q%<|YNV:jbҶ;t&U*ڽ4W7Oo^~}bGIxo؋ xXQO+^ذ^ZNaϡx2j5}(C ?}˃/l]"5=od^=%j?r6۷lȔ(e}@pZH0:l (U;}R~>D4n(@^DՐJ\lCQ 3=s[4oc_aGADGαc]_ۘ, P34T8GAT~#{U,|q JRlD<Mg$AA*(R3m=JINyRj ٨5OBf3e%oܾpm_?r'dRIR03Oqԙ,m,aJPNMVg9vf6mlְ0|Rl1j D)6O>kשhVCjp™RٯΔ (u%(UD23X3G{K6+DbK*Ju@(U3+J:Sa!J B@@@fi%AA*ٰct޽@RWH+0 D)bA8Q*.6OKމy%KI'V=DR IQ KY<5oLj F:P6mhyHE4pF%T2d] DbA8Q KY<=K2jL;oۂxTE J=l A %^a!HP"7(RgGy”+,=DR oGy”+,=RA8Q̙vF"J͞zm_`ӢC`ɔMη߃IlfL<ΰ$\ v_`U+|;sY={ RA8QjֲRa:x}V4)^l , W)(z@(V-#.H:ٱ`D@0㖰cHodIT C!nٮ'[MGsƓVτ&<ç}J(fM9KTR\ڹx͓Ъ6\3Ef SQ@\ЋTRQE(ռQ2.tK?F4ËM=0дA*q42,*, eof(%RNAFTflKڵ z뵕y2"U[O!GD(-Q"H:zƗe̙vFjg/=eXS0Uxص}^BQCB Q@A(J-za!|"T)UH(J 89YoxXd{'Q:AEQճW Ɯs^(UJ*7kYb997aYn@[_p/;E)ݯ_,6.65vawVE}G4, 5 !}\vuSqu՛vwr]u6 Ն(WJ"JYE k+sAǘO,բJ#:&%Ji5fD)7O_v/}l) J2<=th$)/^&48@2{rF_?}WV)?>XJmwiz`FWMfVO1p~706tμZy0@vm]g#um( nЃȔ(u5zTʔ ؉SEfD)Ns$7ZDR)&1kAJ.D'.(R:N,J2 JRLD)Ow-ϗPTT(URUy{4i`(e5M^sFɑ>u>%.6ST(z}5 RgbItSkՀxG"=X( e*(UJ#gQ~˝skHZԨ :ˉs޴hbh0D).'׎TX J #ӅREs2 JY쁜Z/]R*,Qʲ\;(JpU.XalT(U"J9ta66Q {BRxF2jbG(5~5yI*_;xcCxyj9qNJ&ẉ$E%gӱ}{>1HJ.Q*uG L% J,2  B@@@8Go%zB@@@@@@@0D! @*cƶQLT**WO7O>[Oze=w#5-+Wo+" PRS;뉉4_}JA޽T1dv_-[NVAvև3h!C HOO7O~7U`yi݊F V-ήv|MhҘZ5krqǤH*y\TUa}_@DE-_ܗ,LK,߷z+me)[jڢ`.( 00 Ae>pl{{)wkpww,VY]g&MaCDIi)Xλdeg( 4%TP(0[,}!& w/ݝӦ+>\cp9~$douR>ATp>˜LƎŌds0}c4f4|R_={ˡ:}z^[͛ BDAqS>@~aw놧jKv⯉O/L)79C^Ts _>|ɧ>z¢"mP_]O)yⱙ̙m}@F< ( 4`݆TTT0Y|&5)3pW*2fH6_Puǂ^?MBx&O9}{b.Ud$/Hnx핗x|#L{TM$ 4`ʹiݚ n⽷qC3|hFQ#y)7={1\•N5_}Lg <ڝlJJr,s"[ۛB|%>=QAhPZA  @ ף 5Ƶ_ݮ-<2m|4:\pYQp<'Ex;?WW>z=s9siUۊP݇kYP5P2p1vzG#>/Q5MNkk{]x>U壽~QybA)ʺ=\c;Sc e]Smi*:P2SL):ȁWbkc+f6e=;,~ ZzJŧڑ_bp5_VۍXNӨC9g_شe+ᄉ{ﺋyԖ&K{2m۴X%o|嗶ϘcG}9a9;0)̜0;QZZʢÎ>1l۾Zfm{f[:J]w  +;_~qmNWʁCyuBJJJ^^^ۮƢO J~oj}1b,B}d%md+Pc1慱퍨ckh"$EI6C:uպ*TuJyQhO>i|y``i (R\ZU> 17 U:Uw!a*j_)̾Q(g`l3WឲkgSnoYn c) 7OT'6Z φ;|Vk(K]VMiP'p ]_ϷWg]=v"BV[f`e]󗹶)lO}>zNJ0V+t[(s*SX:&\$eK:r2OBTၧ#oA0f̚m_*SXɓX}&s5cc!̚1!7V=\ʵ=39uΧuV|~mUⅯXh1;ubyP(5d@31]-M]8KQ(lݰjkݸb/?%/pO]Fǯʇ,IʺD} Ǯ{x}vvz;PkU2}_ԬЮ }wzr5vgj[5vEP|wNw:Aqx({_"Yg=m,{EE`{k#>AwxU\-ƢcϫP۩-Gi( `l5 Ssh:M{*XDz5oCגǾ< qwPp-WL! z@yTǿԑ.o!+Zn]Ѓ,;@ӮmB~Pz/ 1LfXwܔJ7kƙO%5k|N EщK-?-*ѯ3l]UW7ޕ\%i-밨|lӃmPQ..C/:kəjW\X*RvTvKe0۷,**bP\Q@Io}hO1PpY/e> '`Qb(km,{(.eiOcZgc1z>i5Gj3aKQ/]03.&=7/Nk`.dp|*):CKsYVZWY'W~Ƿ z]gt߅|PgٽXL?:}U1ի=f:rLXZ|wضc^|O>\mҫ%>?cp]UBAEECem۾m۷>rnV|ݧW؈ƪ2XkYe(~I@R%p1ǵ,j?{K0ǞWQ{_ mboLa)>Aq`.Gq?Aw|=}PS[0DV_G}fu+leB;cL {N:K͠Pvj sCGsA:E( NfU|2 ݝbm GfpdCu,|l |$~DnzJRuw/@fϗ/Qw  IDATE"Qo߬)Yu byHn%ٷf_֩#c\Q*-Q$-NC]Ǒ~+`\F~jWez2`rzظ+&Wge\1m`#2v3yq1fKH/0oYkkשkxxb&zÑ^Xhvwb!]#,H"Z61宻@7c]վCĴ>';֘m'GɅ>xW3k1T4蔔꭛۳Z|Lh T$m8|-fk-Hz'2yLw3yU(rkJmwupv>r3_N^/H9j_zmJ8a;Y/]Oq뺟n59nUjP?U)'su&_cā$Wk.NScǫ}V l6sͣo/OJJu]|>EEŘLN}OeJKuΪ*8CQ_m }OgbOH$ڼL;:Uy0&# ʬ7-p&Wak<|'迴>gwښ,ɇ|0]Q̼'0v#u;tr/m{Y̵gt֕)~_"7c nϤJ9́…Qua<;˃Fθe#9 .Z FW|٢3^[Oj|:`%Miq KqWW뇇c5ZԚGAM?O&;I[Be=LYUfs|}C[sci۳nZe9=BJ9>kwgȑu S&W__{c̖vl?@CGwҹSu錻mI)򩯲6vc\}a#GR,&i> AA.AApI )3Xҽ]V 4DR-:#DN[Gd 4G_GT>Da1oގVݮ'$ 7IߵHJK#8*dl5}OJXDã19s^X=ҢS?"HYISr:u?a*33'_h T9ON.vomE=+I>z "<m޶O G1"敥Zy@#o?u([ښB7{ǧ$;wdK󇈎3qRD$%4™DRF]?"D2~Zi=4EΡ]*HqB/֮?,be灴{+&oP{!k^ѡ7}r %j\JN~7w/SҢSbNkǰYMFUY(/ \a]:ǮWJVi#J1%lmZ`AV.H:w,5PWcP`՛)6; ?ӭc1z0e\6桹DGwשpN)SCDDRVD$%8UL")Ql : Wk]n0'm7YKq ݨג})?„(ps:dNd!xсsRn=2+k[G|lz|ق>5Q[bU~Ut<˃SDG鈏.ak~mxMrWv ׶3yj^~5]bLyߏ୧1]y bP:!"")+"*pqq?xe1}&P-)32vzfM>Or߄,V}X,Pn_i3oɩǹli!9(m#u$e޲Vuzbݎ G|xh.D_lh-ן!Ns6$)SCDDR"EDR5<|{&EnX,.XkQR^kWɠy #2=D$B AAhڈH&>g xT=s؍9@rV/J`Ib\1wA2lvadDԑ(/D$%M$pn}pR6C"n]9ƾW' "dp`*"<"_}) O'1O6'1NKHG %JEX¡*Y^-KZ9fqC44 1PU}O KWET2&J>Kz>{7VV|L p(~>&Jt -).Qҭc1N'@ńm~6?PsFkrF&@8H^}ػz-[IA_qW0wsM|ʸbfF͍e` 'fcx9c 3j)x+8Ozcbż˔:r<ˣJy5I޲<\kjiqv巃Nwb!#; Ox=i~Wv9"r.D$eEDRS$k}Hq8{GrZnT{Ns{mmM*d;wΡkS*73AXi$ <&;<ƺV}'un!(ADRVD$%8U,")^x_Q ꃋ)G_VyO>ZeVM55l9Z~WZF_tAjMK=O{8TVBvb{QQBNżXiѼ_o-#ƜX)ԥ]!*g D$%8M," iȽ9f1גy KqWmC uߺfU *)5?nb+pSZl@ /1vigRnr%_S~Vȍ)OwdSa*kOfWY0X4OnXֲGX-+Kn ")I t"")A*D%  v ")AAFDR )SBDRDI]rMτB$YgݹcN'oQ*oR>ɇ|M嬈i!")I M$T4X3)+xDB6L Yq|:c_[P[> R&#")+"*?~ׄs丗\u;mi^~5]bLyߏ୧1]yS"e:""")Ȉ$j4& oDni=.~=֠ڠ+S8\QD$4$D$U9!")iIMal Y̟Ze&;WW߇UIw"˃/64ݎv')@9H IHJhcI-xy  2r,ӓ{"eAh  MI 0"rrڷ.gSWp]"ꂈ")py#*Bρ o^Yښ1%vt()؛۟ĺ)y➓trel錈J "DI]1%lmZ`AV.H:Wpd+?F~jWra*f/X"IYTY$uYd$:JG|t ?9tP} Tf&ȿmJ8"x# f Hʊ D$uefzu-$:Ji$/k- .tT\e@7 D뉎ґqʓ}"""4[CxsQFH>˼e+PW1X4On En4{=}$m ~ugGDR"EDRŇ(KZwkA$AA""AaD$Ո +gFܟ"@HfD-x<ĸb>Yۜ8-"uf65 sid 7_E0.RUѨxlo>ci7DCEZ%tUDw"IHJh"j6I\)tildX0}b&/N?ƌcw4JE)xxH62z`y)㲹iF2=0{3ٞ|*3ISPWpqĊy74)Oux_5ND$eEDRS"rHR$ 娵'_S].MB{->^&4*6[gag3е_Rjmu=:^a?ӹ֩UcDDRVD$%8U!"#/R-Uhd` k2r@atzWGʙre:J$`tE2(b (}LaAQW""I N@H:*/L.z 1~Y4|vƥ̚d)-gѕ)~_"7c nϤJ9_5-D$%")]D$%4QD~%7  4mD$% ÈHRmH+殱9Uft. [)EԴ 4BDRթ/qӥy#DGHMf|ȗCnùW~pTNBC@DR"H!"ԗRԨfCτ"LSZ; MIYT\*GMX}d$:JG\R; MIYTPz71*zbg"J[`Ҧ+&"w:"4.O}a@~ćWo"7<ݑNӇQ MNBBDR"EDR!r'AF@ д  #"z1tQЖ*8のL >ч"q#")AhӘERg#9 :EP~Omm_IHJh"ӘER/`$WY>tVD~\Hʊ D$e]E*"rD$eEDRS"BHʞvÕHۯԇ"򫦋*g D$%8M!"ӘERq8+a ")I t"")A*D~%  v ")AAFDRKyÔ@W?"ɧ<+iΓSO0D/dd(~1%]xsE[ K6WK=.@b}>z "<Os{ Rh7TDDRVD$%8U!")+ ږQ&_7۬΅%_Obepedr!GKԡM)r<~tUo?uI ,*>N=DI N@H:GNxa0ddP#\1#\:Qztddx*ԧH YBq[?Se3k~ (@ W*g D$%8M!"\; &d'oBch|,(7́=¥"7?=>LYwrc4)zv<98ܜ͠7i›s6RG!_-kUg Dմ:vT0voϤ Ņk0bms>JhmΊABAAApI5NHjFTuGDRDILcI[֨yK *6%m;t~{ #7<ЕW'k]ԣ]vgETuGDR"H!"4VTHֲJ IR2g$me<Sesӌv `Ԁfwd+J{|L>;fV?Fx9 5վ!+74cf!,+IHJh"DSI#SPf />|މ[U 2Ra^n"f͏a39:/)de tV`Q<9z~gp:ɀki'fRWk1952Qx<?ow? q9 fH|C I;xh4fp"")&Gd*@[_ r9X5ך|_-?a6pׅ2+ ISWPQa=Ȕ蔔ꭻg뺟59nmSVY㭊h;AMֶSWߢO#X191ֵ>SY *I N@45=2%{Pxֶif}_r J(G\l)G_Vc? Z&!-Û'2ѷ[!w{|:+RIDAT6?ٷ۲szW͸eYޘsP7Q;TRaՂRa\@W*g D$%8MDRȔ@lZG|kNs.셂Ycx,r737}O S'd1Vj\5_}`t߳:Ky~|X:`tvx쮓 SP~cWC5ED$%4LD$%M_   4mD$% ÈHP/gY$QvI B H6)} # Gq]t)X›+K `Y"{Y*[4Is%K$8r.bdL9j>"Z|Lh T$m8|-fkp%WNv#/gJADRVD$%8UDRG^TL]! FW*3o,b!聚 FjMH/"))㲙5?^] a@Zw R%FְfK(#f@W^g_$DDR3"&hj")GMT2Kya화\=T? FWM./I|57mC[ʡg jT¨0z`㇝[OWng5MeO?C$ʾ`UDR"""QN9toD   M    @    T;)IENDB`helpful-0.21/screenshots/helpful_source.png000066400000000000000000000404561446151713400211770ustar00rootroot00000000000000PNG  IHDRuQƕsBIT|dtEXtSoftwaregnome-screenshot> IDATxw|u.Y%YErƅf! !'$4H G@Z  7ٲ,[Vt:]!{ڞte~xờhgwvW?s!B!';!B!!!B!BB!B B!  !B!2@B!B(d B!PA!BB!B!!B!BB!B^M"N+W삥$'ݻy78XVvcAgg'V\hB!:UWnZ~!>!=E : ?'|;7` .6^|B!"T]AXe<Y'st'4`ZA,^oKTTW]~ L!B`A'*2}K.2t%cƌ7~7~GY3O/-_l㾟={+^yyƌIϏ??y2<7>S0QUUoŚu+X~+/ch [ux!*Sk(ڳׯu˖.!dpqㇷrC?[ONv6v@PPO<'Q].&derх(+8*BBBl%CVf&_~^Wu2{LF#23{B!*U Uػ7n,s7"W\RYnu*i\w՘L!w+˯_ KLgg'~'V\%_Gkd2q5Wzѝwsno߈bag1>]u \ͺ1stc\p WȘB!*U |y wXryD[OHHϾ"Ӊ;{|o!!defgotuu=W2jkGkQ\3>Z @ tM7*i5Y(;T*su bz{OQ dB!B 465Skp24 t;p\x<Rpmm}~~}CC#/7̚9C/,@=88Hu \ͺzd*SOXB!B + @WWz.\ "#-DFDq\hZgsp7 %&$bV^fu`y99̛;g_xQ^zU^SaB9V cB!bǪ`< ;{w7cX0\BuRBCB8p f9C z],=7\w-7}zqpWaV]I,^;ˮP?ΡLY!&a4}ޤȘB! ݘq(륾0C[ovPvRSg_xWxSɫ233vXl]x=Sćk*._P}c9].>b42vMKK+cCQ]SؼuGu%m+N' Ƨ}ΦÃ5kn232HNNp 9gt:yWB!C1!B!O1B!Bd B!PA!BB!B!!B!B՛G5Wz޹`uQ][Gǐ,2̌tL&ZZXgF/ውN~YL|y߿=Y1ѹ/qٽ';Qzh-l|4CC˷^3B 2X&f5>]~+67 +gٗ]>?eO:טtͼwD:K%n}͸D߸u~&>G5ї%VVbV`} ~+ȗqٔϞx39nnOym| Zse1mU'!N]nP^QEUM-dOdl|).bcÖm;hiktÌn#j4HǙt&ako p_ĵAxF]-oO]WA)a;1K[Ud BSۨ -k*jXr([w^ax<J751w]u';SPPYNh-gCgtx $9WAD㲣o؊yt^I7=gљze;*oP 5wj Tj:Յ~Α})ޠ߽h/{N6F҆brX,VHIN IgIDEEp8))=}3|>2u)F!\l:3MEU5y9tWVm.ޣ,)Vp2@h^S$ΌeА~JEFL{1#L%5tO)xL5ぷ}c߂3|Σ}D?9NYyϼ]ED:<;AѰ-?2*Hskɂyshlj\M<Ә2)hmmgbn6ёTTw@b>'\یb߿u%Lz_1T}N}h-=h8,J^>F'x9ϣ5@kkuv1f C%ۂGp% h?ж3LeO1-oP4ΔEx~ ˱DRLpC+wVΦsh;1x𺫫bVjR-7HvDFs\Ye+,Ƭ'8;`c[hm8Sc,_w-p%cD ۱O tF;SiquՇ=S'~gyxBjգo8(טh* }8AQ-9X=h\]~wdmWSO}O/Tß[ξ`УY庫)KY[gHk?h\v}3yٗO8̽k\]h6p9=thm v\mYyxNoW{;: Uqu5L+z#~ xh\Cاzib<.>mϲs)uk.yL>vͻ}[L=4:AV>A!wJ "0+ˮbkzuy8xx<m0J C'B`<xpt>]cf"=J窀Oc*\kkmT#|=|psʉop 7X5n;|a9;mkv:TS?>|S#{8AcVj8P9ak˕P s_zGZK5d߄T3j縹xM}>-HmYyx:au=抢ZC+!}L<@'!zrLL4sf %yܰ#<<1 TV x<45b_ Fm<f:"G4Vh̟W+'a^ g~c5NL^Fـ'$AzE~wt1Hǀugh=D |v%Yպ/K b0{+ڮfߨ !t.~~~ ęT%Έs~? К+ѵ~@`I8SӞO>s>A!ȁhcS &=-Ԕ$l]]WVgo .3"M|FLcYx<;>71gtƍMT>Ϛ1UKVMLER<))BК+ Wv}F_':~kpMkƝxJ^#x=v`; #u&f5뮦@m 51yxA{}7yZCh=t͸γAw- Q;Sՙ035e,fx p#(.t; ϴ0]! kt.xOӵOBigT=9q9a1^OtOxr6ũBڪB _K/Y-۩ꐚ4bk.7BQ/:!!F)BS[ !&!B!¯ǜ !B!No2@B!B( L(ȟ2`t3,C ,8"+[6|۩^߽Fze ׮f x >4kWSv5?t: ׮fYg /gjA~'M_%$8xX!BnzK.vxiӖ56 g KS%nwl{pW'JRO^w6hZKyE*sYi!;vsಕ+x/ ,!BӉ_F%/Y;HuUbl^/ }gVailjh^OS'dy}ǧ}~LGvcZ_"77g !B6 DP[W$6KRSBxB:ChB IDAT9+54-5CZ27`;IYVBg]GQo>]:utvTmHP 9\7Ϗ2 6197~?}=^z݉V)=p.t=p8G*Bqk`X _)Dp̿\V:m|;X>==hdt)?3-1h4^>X8EwX~"m;O А?.Çk )0nB!:ꬮ%1qL;zBOTI|44J.Sɓ*H]qu/GB@tVo<0qX fڍfN6~&>͙ډpBe}NBzjZ@xwҕ^i#XK&&RUSB!N~ 3oF(gw?RMq7v*YM.1g8CĦM&zɴ"KP?!\}au=u(u]IJKp++hmmSn"d2=|sFTT$cz7G,_v!9l޲̞͘ٱWY,_mDkkӜ4 &d /B!8hIŗ 2ȟ<ײ}HkU1:CpJ6~9Wٖ(.|kK5zc0zc*YcL+VJM/)V_>žC}F+ȿo'^L@ENoF5ma{zxp)~{xG}ms>F8xqumUUs;Zv DY#)6&Iy9lٶv\Nא9p/djC5F~}g6GshJqI66hYО+hmm'""ٽ fEI9>3QRz "%y1woj`$ 뤬Zr`v*W kj䢥de?vhLz`4s*n0<PNK#TѶ74r 7m=PFzwi0 @cSscМ,+fnzfB#bcB}CH°±.f$3u$"p8dzXyvʤ\LrI˂ S+廴d͙{c̩t:.*k|*˙Nmwu4:*sd4b줸3 T+.=%?P+t Lw>+p%./$1yb.a8.ZZشNNcʤ-X/PߞGJhHUDBye5u==qd-!>Σ}D?9NYy%.w>X9SS[Ͼp󼹳#<<̧AFFEi`B"Y0oM}6DDcvtŬv,갹>󎉎կxcukh4,>DD3oάÝ\&qzNgIHh>;DEF0ve4.9gpu$3p8Ul[ ^#]?jR^i)OcO~[ZcF 5RR(2>ZQ]?',,t("**sڿGMYjX rX,+}H: :;ma6f8N1)/C|3R[WW@AVF:M-465APc\zqlظIcRW_΄LZZؼu;uDEE3! Bzd)KzLȤ gԳpR~ _l܌ƍ5?jR6%PHnw}aێ8N)+$gB49T^Y ((i)ɔWT)iF~zĹ|F̲JΞs?Դ1sʂlw51@zIy(+ %Py$mݾp΢8j@=Bs )FYfg=>|rcV%>.Iy9hZ<UUb9Kb +NG^R Aբht@Ö6 LDӑǜ3v8o$**~\2FsVu xnw?$Ǟ!@&r'|7XzdɡctwwXGډA)ԅ?Petj^Ŋ󘳢mLFQ5iS[߀ =5}CXXHGlںzL~^ix=$Mr9~d C&#.GZ+.qImm:%2nl"IIKMf㦭>'NUrP^QEC3`q z4 ȟ!AXp/Gn.‹ֿЦL&=5;hmk?% XGMXAB|X}?e 6/f$ھxLF#^o4uxdjRdd):_e\pb;L h7NTp"1qbr9\.7̜^pN^ҨrKKudhx<45b#Vөxxv{w#/9:ϙ^juN{3fNh4PQubHj?WGRS@m,T_'*@:UtMȈpCDF>" +gܤUYŮpnã(b u-&'Q[WOWW7C'DdR[ L>rXH 0cz {ָ:liie֌Y&&dg1GcS3FBݮ<d25w$MVxbcoqc WYmw50g BCi0FB\,_|yds=s5=OɈUnRKzd)K21)rijn%/g!<-PRSO8о|pp$ھv3lOTd)Ilٶ4W2`25ˌt(+9`ȿ>>Ṕǟ;Z@IJkRsK+."#'"" 8LFfEGgYgb6[rKVx )1 ̜^ova  465FnNU4652)ysgxh0|{ 9b}J}L+̴S8p~f?mgi,<\.7SpaKkF#Z_HvVƐqZQYM\L4 `4`;憵Y3eWzt$ez=/ޢLG;@] 7?9 X;)>5|y&搑zBQS** !{B&Yh|nT#PRSO8о|pp$ھΞe8nI갦i}^thOS돁Xhx g˯PqS&ܫQ\R<}kOq Ә?^Ǝ]{ʞ43gړ- 11k:a|aG΄L'Ow?  80>=&\n7R0 OwD %""Iy9<O}x$ǑF_ B7(ȟHjr Uh !"SB!QS!B!ȑB!BqA&o 򧜬Ft#o2kWꉿdgOڛo xw aᚵUdfdpu2iboGnR9PV瞃{ާLM߾-$&:^y V[,-u7=wΤtvvߞ ^sm?bυk{(޷[KI=O ?/d2O몿xTV1s>e\5$Ib}XV蝣oAxyr}F.fΘ-[KM!B14'e0cTjjkikos[˵W]XpϝwW_T۪i7wݷF[[;ZgTpM7oK`%̓<)+/g|ZgTj:LJ䪿ǣ)zy^yu.8t#t3@Wޏ?~ݻ)<0o<NK4i[oSO3$8(Պno|_o7{ 6W~VJfL4B!bhN!;{w#88k>}%Dss ?-dJo 7'-9hw\ełb^NVailjh^O>=! U ^o`>?&ԣvZa34B!'eCm]}m6>z;j:Nںzv򹵵^n_+*jf6f39N#B!wRF״cy^<GJ{=L,^t:o{y͆N#""3)eEmYG4 ^{zL'ܾ8o_nc?R04B!'e`X Y^~d .2"sDDŪ\QNYGFO=O t]ǧlv!BΐZ )Y.88)&J4x+*;wa29kޙ>;rŸNUfiH\\l6Ullb"U5N#B!wR -gIDAT2@B!B(d B!PA!BB!B!!B!BB!B B!  !B!2@B!B(d B!PA!BB!B!!B!BB!B B!  !B!`$W-IENDB`helpful-0.21/screenshots/helpful_tools.png000066400000000000000000000153461446151713400210370ustar00rootroot00000000000000PNG  IHDR=N%]IDATxgxTրIH! 5J5"(Eʽ~z(׊AQ,Tc ҉!d2>ߏ9df>q:u{Vqq*J߀,%#= ={ҹ3J #=^ISm+|AIn]g K"qy}{UFLmGJi@)ޤ bi여?(*[O&'e3ƄЧf{Xwe\Xib>uw`h: !6o/ ̞俳 ;;[U, 3ΊAo/ݦ5ߊo0&5)#?'/i!9o~Uiz׿r*m/߅#Tbe)7[lҫPD?bg|d c+ILUP|_\i$rhMukX =Z&#=V;å.*S(+ۯt =={|lNm4a]X ! DZl ٕf.Z;}gY#v3|n0sd:NyS& 6Bڠ;9÷k^%4Gh4[`/p_O@oD-@w˛1Ŏ!۫Rϣz;L~,-P  UZVƄSwcꌵA5[OڰXZ ^ZQpI_-4HN~@1 1>U'O z4^Fz͟v1][B}dXڷkCg lv僚|j3d9u Zڳ;g7GSa/T6#=Ҽy ;vǑwW9u +.`1,*yſ0sǎUWba}T+1era0 1G$ʝw{ݻ,Sl/lQ]㺽_%\EЏ!喲 07C-SKZH~W4M.%Rd3?U|imKn kޒ2ovr5># Up-2zDN>-/HO'iTpf_)n?v )Cd6\Iw1eڳ4kD=d]|0c{'M3^:\ŐH>Rzprrr˴p[:|Avq*1WKq{^.n%@{~ac>[TlQ]Wo@^x2ީҒm}HkA,\s[)}~J2Xq}KZXXf)P+*q%LI,/3Ŏ(|)z (0_KKHHa6z̤ h t%ֆ=1&LGa9%fNa8 z` e"l]):է Hn#]) |Η1ﻥ6JսKRFتPإr: lĵ.SWN 'c#]o!uK8Uk+u#5Z P2SRxN3-qjðt]Ot ~{VXn7rm#Jo{DS @СOno+М(|͹-rg^N(,y$crZ( *S+|ݮX"VO O ''Y2Xl 1͚$``ģ]u:aպN,ZX>enn@9F}+)9D~ﱴ\TpMv4g6V.C/< ~t6QX~+<Ėğ5d3:haOy=Ez-[fhPqFԆK&ݵ{ds {ai2k<$g{66an*[;GX73l (},M0uR {[$£A\gT| (9| Euǖ{v?$rɭ2iOCf-o9t敏t,4g7%xY3huW΃^Zd q\=ꋿ9[ݮT/7k4^eϡ4^I3&3tmXe[a~(l5r#~#OxMSIA'qjñh3;:a9Yr}A+.8 G5QmRnTGh4>ww-N)G}]MJm:p gO$wз򔾢%y˯jaGXr-cߪ+%ghZ@:$֤nr\3BiV[ʋCƋ%eok &[%SljZIWkV"_w[wt}k>(h-7ǯ& s.NM( KCRr*<%ePiqj#PM,Q!xhBIËqڰU 簢vqD|)L9öQhE;lnwwz˗Wp%g|DBC&e˻Q]>FȎ`i>KB{xi_w:L))nɼMǷ(^y ʨxƘbGGoIctɡTcUSq7$\_(J>anvߟtJmՆl#+9~rMU!*93TɅ@ O O ''=E#Do+ 0z@ @ ' ]HOc Ec71~vla3S9x~|իW$%dggm.],B FGǎSi݊;f}!]7 }N?maftaܜѴhÁ,9O?KƍynƋlݾL}zLG;x">_ʳS]'?#JşznO1tԑKѩcyi]@P%;;vmӱn<1q/6~w%3z%mJ5mvyj3ůeKWZoXFb|8c5=( @Zzz%&1箘oݨ(俘;˔@ FOT&Bg0ץ !( .i|r e:ƺݿX,~ڲ?@֭ʻMVTV YY\Y?p?N^y 1zaao׎s?BVsQ})wޥWbt,\?nrKQ$tF18NǧH#Qmvca2S~}z:|xeʋ@ Fo3ys)F'XP@=5%'~j֭9oAPP>ߧάxRRBV"23H>z.]r`2alܔ䧞%9~wN'V/UߑH{9׫MFzi߁%*J pRVM-Yʂ-s## Ae8IXbE +s?kݦሌ!>eATpXt5;(MrDXt{Ғ34Jtեàe¾wg(U.GP.۪]T2IٌS+LQ7 zo\oc_;LoTS7*#~ySxAƟDeqE`=~cߝA_ #2/g@\S ʝ2}PFsf6'yÀO[R< `mS(ɈZQ_<@ȎQXY2k&E,|0cqjaIF{|C;wuKdL2/-h+Bki*K0@pÌ01k/֎qjPX 4m3\㏌A;"413B64g3I TȻ{kmnS[U?`>fw>{?W 795aണ}ഓ{ണ=FȮWpO0?\IƟ@V'A6(щjĩ +2Xvϑn4-nOo n0oVd@PnFssJwg^seY>~Ҹ/Ë}b IDATxw\,AMs9Qi\9˕҆[3̑~J+Gӽ49s8˸?W>.`~>>~s>~Uo؊B!(ԅ!BQ$ B!($ B!($ B!($ B!($ B!($ B!($ B!($ B!($ B!($ B!($ | 5mҘm[XΊB!ml6P._Ʌ5s ^O׻t LBMG!™:Ch(Y[dάoWN~뱢~}Ѻ{:?p#Gz[B!J):Ca䘱XVƌ_Ŋ .̯Y_`§ ;B!("  .EF2cW̙ k֤dD߹@ѽK֭[]u7إ˻fMt\r5lݾ?Χgym_iøưcԱe޵ zT=_N_GtTf3];F.](di%h>~W))|5c:O7jei԰ժVԩP&OA||<ժV姹 \EW7{zj&!1#yQ {PNSB!J#`w_E^څG |rPlYF Lot̞m_iObM&Wk9mےb4ty5iyW>={0rP|4;w'7>SZxc f&LhXgؖ ;NhXڦ=Ө͚6!ŘPThrkFP:qy#˭s*B!ă:Cjb0}9sY~_ۧ76l %%m_B$&&9Ob49x2 βUÄq\2DDoXVs)VEs͚Qr%.^wSg;1rM7 =}Yb9|4d?yv:e˖aق_iܳjB&)ʬs*B!ărR\^^Mi3 | wwUJp֯۶Ζݽ˦?8DfK $$&wwFLo[G}/5U O@1oor:<_jEDN?ꊷqw:\([BQ4* Txyz`HH wm- qqqx>?# qf3({qjiL:ϩiiyZljS'ӨA,) y)B!\kDM_522%w1Z-& ZH(RŒsu4R0$oǐkU*ۂ{Vش~ӷeO/WN,BNy{yQ^=Ǝ @xDwbbI+uh@vmpw'1).pI iښ5jU62-u/hUÅ:w<Ɣ<=Աmzb &n*fۧX1yiYVArr2ujv8%ֳB!#:C8kSSS~L&.^!9t]K-=yVuLٲel$4,M[z4 '?5ilI,_xpl2Sd C%$$p-J*Ŝp5nEG9w^^|0j$/\wiluWd z&vbճB!#:Ch6={2r4O]kׯ~=oeնe1E||<..\xxd5hb5*%Kl.\\d$%%Cf.AuU= gBB$"ΜV-ZG~]tթCu?Rn]խ˓KY![ ;B!cB!EB!EB!EB!EB!Enٸ./[\mkN\Z_(h&><=[|m8ȄO'۶4(CxDGRm[yB/[nֲE >|wl}]dZ711]fG_ym~ɒ%XpKWd%obO\#7V}[j׬I޽j9{duj3ñZoMOJNfƗ_-k2Jtҙ}z1ٶ}{&1)Uk:%e9 4ZsJz9&w˅aA*\}]9{3 /Re\b[CW3zkYd)%JE[.^d@[,\"|(]sV/[kˠX,nhLA2/_dVٲe6ivLii%55oVld2a4mhm;vҾ][f~=4ɡ()_Ŋ|8g9ϣ M\ً/X06x 遬s$7hZ+'>CŪNR0ERQry_, J3PL:oǜ?Rjm׿r1 4_EKr12~~4mVh;ԨVÇǁC2x%쬾\3H٫'[mg?,_;ɮ<~wK.ѱ};BNs O )Z-zw:͕%WdV+w7زm;V7o鿉`[`3;X~.F[oz4O7jpS:wB3{Zʼ޵c6q&d[wޅAntw;11}o`'>Do0cd];΋ר^ ?hfcĞcEJ0fOO?2`+?5r+Dn}Ti^qj@tLPܿ-><ظ8GMWNcRf͘d!GAd|qjլIV- s՗ ct7^^=mf#0|ʗ+Gtɦ<~wYagӉ7 !pFNGjvGn!+ѭKg>x"_IE^:P*Vs~Ͼ9z Y!X8ҩQ:ǎ[cԨ^hxzzh3*?FS`P$&%1j'eV+-Gӱh|y2jǼO lO;D킩T\/S{b@ _,q>7}oBsffvaN +c#$%%~/rbbVш^w( 9ùNC4kܘ֭Zv!ܾk%JLoKjj*h4ПRR(YQ7nGm7g%%hbո9 `[`0ssCx%y;=ClM` {b ϖ;8s2fINwbbɗBٴѽ+3g|ba]̜5KVPiHn߹?CTX%Q'\ǢEjUXuʳQV~_Ą9 ce7YfXpsM_뼽wZ18siii OÜd=+-W^:g/%dZXӌ}kXh4姠_Β={ٽg/:/MePvvoGs*4 7;:uhϚ$%&a6ebhx{2ʊ7DyyyaLIq38>_<>?ދo+V+m3r8XC+(]AȦ~Sal޺5UZ"XNJP)qX]ۡ~akejWЅ.G.@J'_y̱`0Jzd}UKXiV}9/aAֳ-WAI:YǩhdWP#AV?q?Q~$<"Cy{74 i&aT+W2{LHbbz>˴.T**WlgѨQCinsN+66SagKh {ܤyԨ^;vvILJb ܺu%jIC{$*h#3Ò޿4q1zj\1gү OUhuR_2j1jeT)X])/#d7&8崭8:eҢv3!TToyn\='ήG)r95:k;dK{N`_E:kK%?V2^z5ZȎ]yBfN4SzjԨ^?[7eZxb0l;qAmӆ:___._;gkI5lfԫ[J~|}|pssCI}|:7<JmiD}Xnvd[v(03{ZZhi:$qvSԪU!G{g6tR=~¡mN#˕=\AֳH~fGH ]\{ƆCHHz%-aE{# e&*[H֙ԪP߽:ٍ _mK Gƺ8uƍ2ep%*/OztTV+G  ACV/8Vrde7:k;+ ymυqjܳ9h*WD X< קw1\b{Pj} DzԱK+G4jؐC09D>ƱF~u@x)"Μٱk7'NbGЏc2qR$d~q%Llz{NpR{gC}6Css3~9/gҷw/ڽ nof)~>>6kj Nkט2}aamK{(;clI-% My\/l-r#3I۟p+AX%sS?۹@?9~:0q;UWoϮ[1K3#zvf59T6~Lpvn[J82dgyw錯׮Gbjf̨,F#l`HBo+'9d_ٍl?\ǯfϦMgWPm^nmuW|떮XAt:n`O?^#rGUq+!D$= ҶS !YP\\ߐ΂|4),!pBB!JH@(BQc]B!CB!B"NB!B"NB!B"NB!B"NB!B"NB!B"NB!B"NB!B"NB!B"NB!B"NB!B"NB!B"NB!B"NB!B"NB!B"NB!B"NB!B"NB!B"NB!B"N'|D-9Lt# V 1ʩO ~O%??~9lSٳo_agG;q)2'N6إŠ:k_mqi6ӵs'FBn2 |>={drZmC(W Ohr.\,^A A۶,^6e|& ~׻vɴnbb"v^=ߏoq-%K|XɂK \6={|V ,HDkƣj=bXagŎj%e ;+u܉d>-;v͚݋f˙HNNδLAǥm< (iy}.Μ=G ?p)2W^-ӡ=,\%JТԭ] /;`PNm&|8UkֲMI+eM4[Ӌ̶ۻ7IIZ6c@AxDujdh(=9+w`4h 97Y,~Q}Zʗ/dokcߕQ6Rd$"#y^]<)}[Wغ`a#:77}Ƅe0m$3履yEƘ¢>=._ɍ7ydI:}?LR>|y{XڼܚAc.;btÆ?UT!`` ,[JqO,ɪ>pF 33 QQTR}ߤnx{{5kʓե>]gV.'KfⅶϏRקʖ%))̘#/dEiп[/W]clپ^m \wsY+'ެ]/m;vڦUŸ~`*:S_@z=wsж{6ֺU+&G߀?r8uk!11Yϱ-];t~r)5j0٬^ڱn5mӍ:@\xO&Ojr08OOOzxáލf}kH^hz8j֤uNU.̍7dszFjG[ٳ%S^=k֌Mf8rĖ?ʍ7myOHH}Ώ8ۙD%&&[b]LE@ݻw=HI{;Xi[s;tfpV(M'po:Փ6o&< Ç|rDGQh4rmBOwg]~CV2wb T49Ll_o:!!nn'KhEdjVƁCesJY J()W.] 1֭XvrSGq{.܉y-o//~_nZȑ;~A3Nڵklݱ#W v>GEEQzu 'ORB\\\ogϟK:uK=x}ۖquu}X|6odxk18\JD^Z1ӨAt:]QR? '%%}حk6TYCo0rEڵ([ [O+~sq$$ILLjb4s=?@LLm;S9vݴ#GQg_o[L:Ѭqc+Zj턇GWnn4_qQhZTlw0~ 9~d|vt\}TωrUR\IMM՗g/9i><gLbR߼~[jxrt:.ϷYq‡/u-\ovpGրw! JE%qCv_ŋh67we1[,عfcrല&6.@dA/H?-cn5׭?/Dę\zۂ!9Y$ V˽6 ơ,V ZU# i&_9CJJ*et:6AmD=JJ*4lPа05ϭ۷qqq!"~@]?eSpfW2>;:>Di|||2]5$$83DZ5ٷ0?[ٳv˘M&E߉ &6ơ<*y$f3,Z̟Y| kW?Hϳ) /jɲ+ټumۣGw( *ݴ'RbWPl7Bʚ%c0ER)(̩>%y~۷z.T㥖/Gh?x9z+I;1% f4ĈCPٶ;G$''Sz55h?0b`"#/sڵL^vsv™Gظ}ώrݍ:>YXՀ69w(3|yU`Tf2Ne??r%?G=l2z~+U*nn:ۦz['˴ G NCT**WorDbRko֭[,Y"QZ?O͕͟gI,S:s)] \ѣYʞl?ΒS;,y~wV=K={FMkt#Ξ;4ۓKgǝNGDаA<==8V3,--oΣFt)䤒_E:kK%NK`!3N)PZ5jT:V\zCpMΞ?ogZ9zoOpH*T::P:mKW_%n>viۦ !!t~#\~ホjMz>}|ΌtОZjr8qwlӦ.UO(ގ9q!!eʔKT(_锩vF^L/p08X ~Йˑenw`p0wƓ[ٕ] g(icJ8 ~ΪggﯼϚ|>ycF`<۬e)E=h܎rJTPа~}y+OWp(0IIԯWN;$ԇauǎS|?ִ ggGռRR70cdGShaHA/q<ʡ >@ٻv XB-2-A$n԰!#asnˋ O<؏&0ǏdJH6mݻ{jLf~31F_0lڲ! gZv j׬wӓ1;%KyA ym~Sg1lϣzWp%Z4oΨ\v)gж&Vt_]bՙ[x }b4>^o.5%u23پcCuHٕpvȎ6# 'zvk f3ۻ^y[n7lO懏~igӦ+(6/麌+>uKW}Wyw@t:Q7n0mqh4:R#Gҵs'#;C ggGռRR;^=ޠSl۱}Pxq2yp4+e?0l{L: wIOȴl7z餄7nϹ-uҙЭg/RRxgB|5nJ|bjQx ePbŶ;$BQI@(Ao1~Z0B9d,BQkgB!IB!B"NB!B"NB!B"NB!B"NB!B"NB!B"NB!B"NB!B"NB!B"NB!B"NB!B"NB!B"NB!B"NB!B"NB!B"NB!B"NB!B"NB!B"NB!B".ǀ0=nœ71_A!oi_vf>sqcq%q}CBb/ ;;B!(D9ҍ즛J5aEwUZ[~;὾.vVBQȴ9-=Ӹ6TiThb"WZ3k[~nfBQr ˍØJ?c7Ti7ų ]6{;3fz `*.n+q [@|͸.̪LVJr![FQ'1U-̴+'1[[2RuJ.VifzOaq+&nz~CXkKm@chB!Ƞ maR+Uj+fIC{$*h#3b@s'Sgzu .i?g :T)q)V{d+a٧jJZ|!Cq@JK@{'j]Ĝ 8X<`,Ek@wb~mHzcʿHZ1V_ٖFV%n!Ul&:ԡ)Oq]K`ї$r7`x*cC:-cOjNJ>UH#Biڨ`L%%r/>'7G#|4I_Qy.B!9T7ȄB! )|LaEB!D3B!EO5!B B!8 B!8 B!8 B!8 B!8 B!8 B!8 B!8 B!8 B!kbuIENDB`helpful-0.21/test/000077500000000000000000000000001446151713400140605ustar00rootroot00000000000000helpful-0.21/test/helpful-unit-test.el000066400000000000000000001132131446151713400177740ustar00rootroot00000000000000;;; helpful-unit-test.el -*- lexical-binding: t; -*- (require 'ert) (require 'edebug) (require 'helpful) (require 'python) (when noninteractive (unless find-function-C-source-directory (let* ((emacs-src-path (f-join default-directory "emacs-25.3" "src"))) (if (f-exists-p emacs-src-path) (setq find-function-C-source-directory emacs-src-path) (message "No Emacs source code found at %S, some tests will be skipped. Run ./download_emacs_src.sh" emacs-src-path)))) (setq jka-compr-verbose nil)) (defvar helpful-test-var nil) (define-obsolete-variable-alias 'helpful-test-var-obsolete 'helpful-test-var "23.1") (defun helpful-test-fun-obsolete () (declare (obsolete helpful "1.2.3")) nil) (defun test-foo () "Docstring here." nil) (defun test-foo-advised () "Docstring here too." nil) (autoload 'some-unused-function "somelib.el") (defadvice test-foo-advised (before test-advice1 activate) "Placeholder advice 1." nil) (defadvice test-foo-advised (after test-advice2 activate) "Placeholder advice 2." nil) (ert-deftest helpful--docstring () "Basic docstring fetching." (should (equal (helpful--docstring #'test-foo t) "Docstring here."))) (ert-deftest helpful--docstring-symbol () "Correctly handle quotes around symbols." ;; We should replace quoted symbols with links, so the punctuation ;; should not be in the output. (let* ((formatted-docstring (helpful--format-docstring "`message'"))) (should (equal formatted-docstring "message"))) ;; We should handle stray backquotes. (let* ((formatted-docstring (helpful--format-docstring "`foo `message'"))) (should (equal formatted-docstring "`foo message"))) ;; Handle a missing closing '. (let* ((formatted-docstring (helpful--format-docstring "`foo"))) (should (equal formatted-docstring "`foo")))) (ert-deftest helpful--docstring-unescape () "Discard \\=\\= in docstrings." (let* ((docstring (helpful--docstring #'apply t)) (formatted-docstring (helpful--format-docstring docstring))) (should (not (s-contains-p "\\=" formatted-docstring))))) (ert-deftest helpful--docstring-strings () "Double-quoted strings should be treated literally." ;; Ensure backslashes are shown escaped, so the output is a valid string literal. (let* ((formatted-docstring (helpful--format-docstring "hello \"x\\y\" world"))) ;; This test will fail in a local Emacs instance that has modified ;; minibuffer keybindings. (should (string-equal formatted-docstring "hello \"x\\\\y\" world"))) ;; Don't crash on unbalanced doublequotes. (helpful--format-docstring "hello \" world") ;; Command sequences \\[foo] should be ignored inside doublequotes. (let* ((formatted-docstring (helpful--format-docstring "hello \"\\[foo]\" world"))) ;; This test will fail in a local Emacs instance that has modified ;; minibuffer keybindings. (should (string-equal formatted-docstring "hello \"\\\\[foo]\" world")))) (ert-deftest helpful--docstring-keymap () "Handle keymap references in docstrings." (let* ((formatted-docstring (helpful--format-docstring "\\\\[next-history-element]"))) ;; This test will fail in a local Emacs instance that has modified ;; minibuffer keybindings. (should (string-equal formatted-docstring "M-n")))) (ert-deftest helpful--docstring-keymap-newline () "If a keymap reference is on its own line, remove the entire line." (should (string-equal (helpful--format-docstring "Foo. \\ bar") "Foo. bar"))) (ert-deftest helpful--docstring-advice () "Get the docstring on advised functions." (should (equal (helpful--docstring #'test-foo-advised t) (if (version< emacs-version "28") "Docstring here too." "Docstring here too.\n\nThis function has :around advice: `ad-Advice-test-foo-advised'.")))) (defun test-foo-no-docstring () nil) (ert-deftest helpful--no-docstring () "We should not crash on a function without a docstring." (should (null (helpful--docstring #'test-foo-no-docstring t)))) (ert-deftest helpful--interactively-defined-fn () "We should not crash on a function without source code." (eval '(defun test-foo-defined-interactively () 42)) (with-temp-buffer (helpful-function #'test-foo-defined-interactively) (should (equal (buffer-name) "*helpful function: test-foo-defined-interactively*")))) (ert-deftest helpful--variable-defined-at-point () ;; The happy case with valid code. (with-temp-buffer (insert "(defvar foo nil)") (goto-char (1+ (point-min))) (should (eq (helpful--variable-defined-at-point) 'foo))) ;; Ensure we don't crash if the source code isn't valid. (with-temp-buffer (insert "(defvar foo nil") (goto-char (1+ (point-min))) (helpful--variable-defined-at-point))) (ert-deftest helpful--edebug-fn () "We should not crash on a function with edebug enabled." (let ((edebug-all-forms t) (edebug-all-defs t)) (with-temp-buffer (insert "(defun test-foo-edebug () 44)") (goto-char (point-min)) (cl-letf (((symbol-function #'message) (lambda (_format-string &rest _args)))) (eval (eval-sexp-add-defvars (edebug-read-top-level-form)) t)))) (helpful-function #'test-foo-edebug)) (defun test-foo-return-arg (s) "blah blah." s) (ert-deftest helpful--edebug-p () "Ensure that we don't crash on a function whose body ends with symbol (not a form)." (should (not (helpful--edebug-p #'test-foo-return-arg)))) (defun test-foo-usage-docstring () "\n\n(fn &rest ARGS)" nil) (ert-deftest helpful--usage-docstring () "If a function docstring only has usage, do not return it." (should (null (helpful--docstring #'test-foo-usage-docstring t)))) (defun test-foo-no-properties () nil) (ert-deftest helpful--primitive-p () ;; Defined in C. (should (helpful--primitive-p 'message t)) ;; Defined in C, but an alias. (should (helpful--primitive-p 'not t)) ;; Defined in elisp. (should (not (helpful--primitive-p 'when t)))) (ert-deftest helpful--primitive-p--advised () "Ensure we handly advised primitive functions correctly." ;; `rename-buffer' is primitive, but it's advised by uniquify. (should (helpful--primitive-p 'rename-buffer t))) (ert-deftest helpful--without-advice () "Ensure we remove advice to get the underlying function." ;; Removing the advice on an unadvised function should give us the ;; same function. (should (eq (helpful--without-advice #'test-foo) (indirect-function #'test-foo))) ;; Removing the advice should give us an unadvised function. (should (not (helpful--advised-p (helpful--without-advice #'test-foo-advised))))) (ert-deftest helpful-callable () ;; Functions. Also a regression test for #170. (helpful-callable 'face-attribute) ;; We should not crash when looking at macros. (helpful-callable 'when) ;; Special forms should work too. (helpful-callable 'if) ;; Named keyboard macros (strings and vectors). (fset 'aaa "aaa") (helpful-callable 'aaa) (fset 'backspace-return [backspace return]) (helpful-callable 'backspace-return)) (ert-deftest helpful-callable--with-C-source () "Smoke test for special forms when we have the Emacs C source loaded." (skip-unless find-function-C-source-directory) (helpful-callable 'if)) (ert-deftest helpful--no-symbol-properties () "Helpful should handle functions without any symbol properties." ;; Interactively evaluating this file will set edebug properties on ;; test-foo-no-properties, so remove all properties. (setplist #'test-foo-no-properties nil) ;; This shouldn't throw any errors. (helpful-function #'test-foo-no-properties)) (ert-deftest helpful--split-first-line () ;; Don't modify a single line string. (should (equal (helpful--split-first-line "foo") "foo")) ;; Don't modify a two-line string if we don't end with . (should (equal (helpful--split-first-line "foo\nbar") "foo\nbar")) ;; If the second line is already empty, do nothing. (should (equal (helpful--split-first-line "foo.\n\nbar") "foo.\n\nbar")) ;; But if we have a single sentence and no empty line, insert one. (should (equal (helpful--split-first-line "foo.\nbar") "foo.\n\nbar"))) (ert-deftest helpful--format-reference () (should (equal (helpful--format-reference '(def foo) 10 1 123 "/foo/bar.el") "(def foo ...) 1 reference")) (should (equal (helpful--format-reference '(advice-add 'bar) 10 1 123 "/foo/bar.el") "(advice-add 'bar ...) 1 reference"))) (ert-deftest helpful--format-docstring () "Ensure we handle `foo' formatting correctly." ;; If it's bound, we should link it. (let* ((formatted (helpful--format-docstring "foo `message'.")) (m-position (s-index-of "m" formatted))) (should (get-text-property m-position 'button formatted))) ;; If it's not bound, we should not. (let* ((formatted (helpful--format-docstring "foo `messagexxx'.")) (m-position (s-index-of "m" formatted))) (should (not (get-text-property m-position 'button formatted))) ;; But we should always remove the backticks. (should (equal formatted "foo messagexxx."))) ;; Don't require the text between the quotes to be a valid symbol, e.g. ;; support `C-M-\' (found in `vhdl-mode'). (let* ((formatted (helpful--format-docstring "foo `C-M-\\'"))) (should (equal formatted "foo C-M-\\")))) (ert-deftest helpful--format-docstring-escapes () "Ensure we handle escaped quotes correctly." (let* ((formatted (helpful--format-docstring "foo \\=`message\\='.")) (m-position (s-index-of "m" formatted))) (should (equal formatted "foo `message'.")) (should (not (get-text-property m-position 'button formatted))))) (ert-deftest helpful--format-docstring-command-keys () "Ensure we propertize references to command key sequences." ;; This test will fail in your current Emacs instance if you've ;; overridden the `set-mark-command' keybinding. (-let [formatted (helpful--format-docstring "\\[set-mark-command]")] (should (string-equal formatted "C-SPC")) (should (get-text-property 0 'button formatted))) ;; If we have quotes around a key sequence, we should not propertize ;; it as the button styling will no longer be visible. (-let* ((emacs-major-version 28) (formatted (helpful--format-docstring "`\\[set-mark-command]'"))) (should (string-equal formatted "C-SPC")) (should (eq (get-text-property 0 'face formatted) 'help-key-binding))) (-let* ((emacs-major-version 27) (formatted (helpful--format-docstring "`\\[set-mark-command]'"))) (should (string-equal formatted "C-SPC")) (should (eq (get-text-property 0 'face formatted) 'button)))) (ert-deftest helpful--format-docstring-mode-maps () "Ensure we propertize references to keymaps." (-let [formatted (helpful--format-docstring "\\{python-mode-map}")] (should (s-contains-p "run-python" formatted))) ;; Handle non-existent mode maps gracefully. (-let [formatted (helpful--format-docstring "\\{no-such-mode-map}")] (should (s-contains-p "not currently defined" formatted)))) (ert-deftest helpful--format-docstring--info () "Ensure we propertize references to the info manual." ;; This is the typical format. (let* ((formatted (helpful--format-docstring "Info node `(elisp)foo'")) (paren-position (s-index-of "(" formatted))) (should (string-equal formatted "Info node (elisp)foo")) (should (get-text-property paren-position 'button formatted))) ;; Some functions, such as `signal', use 'anchor'. (let* ((formatted (helpful--format-docstring "Info anchor `(elisp)foo'")) (paren-position (s-index-of "(" formatted))) (should (string-equal formatted "Info anchor (elisp)foo")) (should (get-text-property paren-position 'button formatted))) ;; Ensure we handle wrapped lines too, e.g. in `org-odt-pixels-per-inch'. (let* ((formatted (helpful--format-docstring "Info node `(elisp)foo \nbar'")) (paren-position (s-index-of "(" formatted))) (should (string-equal formatted "Info node (elisp)foo \nbar")) (should (get-text-property paren-position 'button formatted))) ;; Some docstrings use "info" (lowercase). (let* ((formatted (helpful--format-docstring "info node `(elisp)foo'")) (paren-position (s-index-of "(" formatted))) (should (string-equal formatted "info node (elisp)foo")) (should (get-text-property paren-position 'button formatted))) ;; Some docstrings use angular quotation marks. (let* ((formatted (helpful--format-docstring "Info node ‘(elisp)foo’")) (paren-position (s-index-of "(" formatted))) (should (string-equal formatted "Info node (elisp)foo")) (should (get-text-property paren-position 'button formatted))) ;; If there's no manual information, assume it is part of the Emacs manual. (let* ((formatted (helpful--format-docstring "Info node ‘foo’")) (paren-position (s-index-of "(" formatted))) (should (string-equal formatted "Info node (emacs)foo")) (should (get-text-property paren-position 'button formatted)))) (ert-deftest helpful--format-docstring--url () "Ensure we propertize URLs with backticks." (let* ((formatted (helpful--format-docstring "URL `http://example.com'")) (url-position (s-index-of "h" formatted))) (should (string-equal formatted "URL http://example.com")) (should (get-text-property url-position 'button formatted)))) (ert-deftest helpful--format-docstring--bare-url () "Ensure we propertize URLs without backticks." (let* ((formatted (helpful--format-docstring "http://example.com\nbar")) (url-position (s-index-of "h" formatted))) (should (string-equal formatted "http://example.com\nbar")) (should (get-text-property url-position 'button formatted)) (should (equal (get-text-property url-position 'url formatted) "http://example.com"))) ;; Don't consider trailing punctuation to be part of the URL. (let* ((formatted (helpful--format-docstring "See http://example.com.")) (url-position (s-index-of "h" formatted))) (should (string-equal formatted "See http://example.com.")) (should (equal (get-text-property url-position 'url formatted) "http://example.com"))) ;; Format markdown-style links. (let* ((formatted (helpful--format-docstring "See .")) (url-position (s-index-of "h" formatted))) (should (equal (get-text-property url-position 'url formatted) "http://example.com")))) (ert-deftest helpful--definition-c-vars () "Handle definitions of variables in C source code." (skip-unless find-function-C-source-directory) (helpful--definition 'default-directory nil)) (ert-deftest helpful--definition-special-form () "Ensure we find the position of special forms." (skip-unless find-function-C-source-directory) (-let [(buf pos _) (helpful--definition 'if t)] (should buf) (should pos))) (setq helpful-var-without-defvar 'foo) (ert-deftest helpful--definition-no-defvar () "Ensure we don't crash on calling `helpful--definition' on variables defined without `defvar'." (helpful--definition 'helpful-var-without-defvar nil)) (ert-deftest helpful--definition-buffer-opened () "Ensure we mark buffers as opened for variables." (require 'python) ;; This test will fail if you already have python.el.gz open in your ;; Emacs instance. (skip-unless (null (get-buffer "python.el.gz"))) (-let [(buf _pos opened) (helpful--definition 'python-indent-offset nil)] (should (bufferp buf)) (should opened))) (ert-deftest helpful--definition-edebug-fn () "Ensure we use the position information set by edebug, if present." ;; Test with both edebug enabled and disabled. The edebug property ;; on the symbol varies based on this. (dolist (edebug-on (list nil t)) (let ((edebug-all-forms edebug-on) (edebug-all-defs edebug-on)) (with-temp-buffer (insert "(defun test-foo-edebug-defn () 44)") (goto-char (point-min)) (cl-letf (((symbol-function #'message) (lambda (_format-string &rest _args)))) (eval (eval-sexp-add-defvars (edebug-read-top-level-form)) t)) (-let [(buf _pos _opened) (helpful--definition 'test-foo-edebug-defn t)] (should buf)))))) (ert-deftest helpful--definition-defstruct () "Ensure we find the position of struct functions." (-let [(buf pos _) (helpful--definition #'make-ert-test t)] (should buf) (should pos))) (ert-deftest helpful-variable () "Smoke test for `helpful-variable'." (helpful-variable 'tab-width)) (ert-deftest helpful-visit-reference () "Smoke test for `helpful-visit-reference'." (helpful-function 'replace-regexp-in-string) (goto-char (point-min)) ;; Move forward to the first reference. (while (not (get-text-property (point) 'helpful-pos)) (forward-char 1)) (helpful-visit-reference)) (ert-deftest helpful--signature () "Ensure that autoloaded functions are handled gracefully" (should (equal (helpful--signature 'some-unused-function) "(some-unused-function [Arg list not available until function definition is loaded.])"))) (ert-deftest helpful--signature-space () "Ensure that symbols with spaces are handled correctly." (should (equal (helpful--signature 'helpful-test-fn-with\ space) "(helpful-test-fn-with\\ space)"))) (ert-deftest helpful--signature--advertised () "Ensure that we respect functions that declare `advertised-calling-convention'." (should (equal (helpful--signature 'start-process-shell-command) "(start-process-shell-command NAME BUFFER COMMAND)"))) (ert-deftest helpful-function--single-buffer () "Ensure that calling `helpful-buffer' does not leave any extra buffers lying around." (let ((initial-buffers (buffer-list)) expected-buffers results-buffer) (helpful-function #'enable-theme) (setq results-buffer (get-buffer "*helpful command: enable-theme*")) (setq expected-buffers (cons results-buffer initial-buffers)) (should (null (-difference (buffer-list) expected-buffers))))) (ert-deftest helpful--kind-name () (should (equal (helpful--kind-name 'message nil) "variable")) (should (equal (helpful--kind-name 'message t) "function")) (should (equal (helpful--kind-name 'save-excursion t) "special form"))) (ert-deftest helpful--pretty-print () ;; Strings should be formatted with double-quotes. (should (equal "\"foo\"" (helpful--pretty-print "foo"))) ;; Don't crash on large plists using keywords. (helpful--pretty-print '(:foo foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo :bar bar))) (ert-deftest helpful-update-after-killing-buf () "If we originally looked at a variable in a specific buffer, and that buffer has been killed, handle it gracefully." ;; Don't crash if the underlying buffer has been killed. (let (helpful-buf) (with-temp-buffer (helpful-variable 'tab-width) (setq helpful-buf (current-buffer))) (with-current-buffer helpful-buf (helpful-update)))) (ert-deftest helpful--canonical-symbol () (should (eq (helpful--canonical-symbol 'not t) 'null)) (should (eq (helpful--canonical-symbol 'search-forward-regexp t) 're-search-forward)) (should (eq (helpful--canonical-symbol 'emacs-bzr-version nil) 'emacs-repository-version))) (ert-deftest helpful--aliases () (should (equal (helpful--aliases 'null t) (list 'not))) (should (equal (helpful--aliases 'emacs-repository-version nil) (list 'emacs-bzr-version)))) (defun helpful-fn-in-elc ()) (ert-deftest helpful--elc-only () "Ensure we handle functions where we have the .elc but no .el file." ;; Pretend that we've loaded `helpful-fn-in-elc' from /tmp/foo.elc. (let ((load-history (cons '("/tmp/foo.elc" (defun . helpful-fn-in-elc)) load-history))) ;; This should not error. (helpful-function 'helpful-fn-in-elc))) (ert-deftest helpful--unnamed-func () "Ensure we handle unnamed functions too. This is important for `helpful-key', where a user may have associated a lambda with a keybinding." (let* ((fun (lambda (x) x)) (buf (helpful--buffer fun t))) ;; There's no name, so just show lambda in the buffer name. (should (equal (buffer-name buf) "*helpful lambda*")) ;; Don't crash when we show the buffer. (with-current-buffer buf (helpful-update)))) (ert-deftest helpful--unnamed-func-with-docstring () (let* ((fun (lambda (x) "Foo" x)) (buf (helpful--buffer fun t))) ;; Don't crash when we show the buffer. (with-current-buffer buf (helpful-update)))) (ert-deftest helpful--unnamed-compiled-func () "Ensure we handle unnamed byte-compiled functions. This is important for `helpful-key', where a user may have associated a lambda with a keybinding." (let* ((fun (byte-compile (lambda (x) x))) (buf (helpful--buffer fun t))) ;; There's no name, so just show lambda in the buffer name. (should (equal (buffer-name buf) "*helpful lambda*")) ;; Don't crash when we show the buffer. (with-current-buffer buf (helpful-update)))) (ert-deftest helpful--obsolete-variable () "Test display of obsolete variable." (let* ((var 'helpful-test-var-obsolete) (info (helpful--format-obsolete-info var nil))) (should (equal info "This variable is obsolete since 23.1; use helpful-test-var instead.")))) (ert-deftest helpful--obsolete-function () "Test display of obsolete function." (let* ((fun 'helpful-test-fun-obsolete) (info (helpful--format-obsolete-info fun t))) (should (equal info "This function is obsolete since 1.2.3; use helpful instead.")))) (ert-deftest helpful--keymap-keys--sparse () (let* ((parent-keymap (make-sparse-keymap)) (keymap (make-sparse-keymap))) (set-keymap-parent keymap parent-keymap) (define-key parent-keymap (kbd "a") #'forward-char) (define-key keymap (kbd "C-c C-M-a") #'backward-char) (define-key keymap [remap quoted-insert] #'forward-line) (should (equal (helpful--keymap-keys keymap) '(([17] forward-line) ([3 27 1] backward-char) ([97] forward-char)))))) (defvar helpful--dummy-keymap (let ((keymap (make-sparse-keymap))) (define-key keymap (kbd "a") #'forward-char) keymap)) ;; `fset' is necessary for keymaps as prefixes. This is a quirky Emacs ;; API: https://emacs.stackexchange.com/q/28576/304 (fset 'helpful--dummy-keymap helpful--dummy-keymap) (ert-deftest helpful--keymap-keys--prefix () "Test we flatten keymaps with prefix keys." (let* ((keymap (make-sparse-keymap))) (define-key keymap (kbd "C-c") 'helpful--dummy-keymap) (should (equal (helpful--keymap-keys keymap) '(([3 97] forward-char)))))) (ert-deftest helpful--keymap-keys () (let* ((parent-keymap (make-keymap)) (keymap (make-keymap))) (set-keymap-parent keymap parent-keymap) (define-key parent-keymap (kbd "a") #'forward-char) (define-key keymap (kbd "C-c C-M-a") #'backward-char) (define-key keymap [remap quoted-insert] #'forward-line) (should (equal ;; This order differs from a sparse keymap. We should fix that ;; if it makes any difference. (helpful--keymap-keys keymap) '(([3 27 1] backward-char) ([17] forward-line) ([97] forward-char)))))) (ert-deftest helpful--keymap-keys--strings () "Test that we handle maps with format (TYPE ITEM-NAME . BINDING)." ;; This is an actual piece of smerge-mode-map. (let ((keymap '(keymap (3 keymap (94 keymap (61 keymap (61 "upper-lower" . smerge-diff-upper-lower) (62 "base-lower" . smerge-diff-base-lower) (60 "base-upper" . smerge-diff-base-upper) "Diff")))))) (should (equal (helpful--keymap-keys keymap) '(([3 94 61 61] smerge-diff-upper-lower) ([3 94 61 62] smerge-diff-base-lower) ([3 94 61 60] smerge-diff-base-upper)))))) (ert-deftest helpful--keymap-keys--anonymous-fns () (let* ((keymap (make-keymap))) (define-key keymap (kbd "a") (lambda () (interactive) (message ""))) (define-key keymap (kbd "a") (byte-compile (lambda () (interactive) (message "")))) ;; Don't crash on anonymous functions in a keymap. (helpful--keymap-keys keymap))) (ert-deftest helpful--format-keymap--keyboard-macros () (let* ((keymap (make-keymap))) ;; A keyboard macro can be a string or a vector. (define-key keymap "a" "ABC") (define-key keymap "b" [TAB]) (should (equal (helpful--format-keymap keymap) "a Keyboard Macro\nb Keyboard Macro")))) (defun helpful--dummy-command () (interactive)) (ert-deftest helpful--keymaps-containing () "Ensure that we find keymaps for variables with bindings." ;; This is defined in the global map. (should (helpful--keymaps-containing #'where-is)) ;; Only defined in `minor-mode-map-alist'. (let ((keymap (make-sparse-keymap))) (define-key keymap (kbd "a") #'helpful--dummy-command) (let ((minor-mode-map-alist (cons (cons 'foo-mode keymap) minor-mode-map-alist))) (should (helpful--keymaps-containing #'helpful--dummy-command)))) ;; Don't crash if there are dodgy values in `minor-mode-map-alist'. (let ((minor-mode-map-alist ;; I'm not convinced this is legal, but ;; pdf-cache-prefetch-minor-mode in pdf-tools has t as a ;; keymap. (cons (cons 'foo-mode t) minor-mode-map-alist))) (helpful--keymaps-containing #'helpful--dummy-command)) ;; Create a keybinding that is very unlikely to clobber actually ;; defined keybindings in the current emacs instance. (global-set-key (kbd "C-c M-S-c") #'helpful--dummy-command) ;; This command should only be in `global-map' and ;; `mode-specific-map'. (should (equal (length (helpful--keymaps-containing #'helpful--dummy-command)) 2)) ;; Undo keybinding. (global-set-key (kbd "C-c M-S-c") nil) ;; Check for ido command remapping. (ido-mode 1) (should (equal (helpful--keymaps-containing 'ido-find-file) '(("minor-mode-map-alist (ido-mode)" "" "C-x C-f")))) (ido-mode 0)) (defalias 'helpful--dummy-command-alias #'helpful--dummy-command) (ert-deftest helpful--keymaps-containing-aliases () "Ensure that we find keymaps that we've bound command aliases in." ;; Create keybindings that are very unlikely to clobber actually ;; defined keybindings in the current emacs instance. (global-set-key (kbd "C-c M-S-c") #'helpful--dummy-command) (global-set-key (kbd "C-c M-S-d") #'helpful--dummy-command-alias) (unwind-protect (let* ((keymaps (helpful--keymaps-containing-aliases #'helpful--dummy-command (helpful--aliases 'helpful--dummy-command t))) (global-keybindings (cdr (assoc "global-map" keymaps)))) (should (equal global-keybindings (list "C-c M-S-c" "C-c M-S-d")))) ;; Undo keybindings. (global-set-key (kbd "C-c M-S-c") nil) (global-set-key (kbd "C-c M-S-d") nil))) (ert-deftest helpful--merge-alists () (should (equal (helpful--merge-alists '((a . (1 2 3)) (b . (4))) '((a . (10)) (c . (11)))) '((a . (1 2 3 10)) (b . (4)) (c . (11)))))) (ert-deftest helpful--source () (-let* (((buf pos _opened) (helpful--definition #'helpful--source t)) (source (helpful--source #'helpful--source t buf pos))) (should (s-starts-with-p "(defun " source)))) (ert-deftest helpful--source-autoloaded () "We should include the autoload cookie." (-let* (((buf pos _opened) (helpful--definition #'helpful-at-point t)) (source (helpful--source #'helpful-at-point t buf pos))) (should (s-starts-with-p ";;;###autoload" source)))) (ert-deftest helpful--source--interactively-defined-fn () "We should return the raw sexp for functions where we can't find the source code." (eval '(defun test-foo-defined-interactively () 42)) (-let* (((buf pos _opened) (helpful--definition #'test-foo-defined-interactively t))) (should (not (null (helpful--source #'test-foo-defined-interactively t buf pos)))))) (ert-deftest helpful--outer-sexp () ;; If point is in the middle of a form, we should return its position. (with-temp-buffer (insert "(foo bar baz)") (goto-char (point-min)) (search-forward "b") (-let [(pos subforms) (helpful--outer-sexp (current-buffer) (point))] (should (equal pos (point-min))) (should (equal subforms '(foo bar))))) ;; If point is at the beginning of a form, we should still return its position. (with-temp-buffer (insert "(foo) (bar)") (goto-char (point-min)) (search-forward "b") (backward-char 2) (-let [(pos subforms) (save-excursion (helpful--outer-sexp (current-buffer) (point)))] (should (equal pos (point))) (should (equal subforms '(bar)))))) (ert-deftest helpful--summary--aliases () ;; exclude the sym itself "Ensure we mention that a symbol is an alias." (-let* (((buf pos opened) (helpful--definition '-select t)) (summary (helpful--summary '-select t buf pos))) (when opened (kill-buffer buf)) ;; Strip properties to make assertion messages more readable. (set-text-properties 0 (1- (length summary)) nil summary) (should (equal summary "-select is a function alias for -filter, defined in dash.el.")))) (ert-deftest helpful--summary--special-form () "Ensure we describe special forms correctly" (-let* ((summary (helpful--summary 'if t nil nil))) ;; Strip properties to make assertion messages more readable. (set-text-properties 0 (1- (length summary)) nil summary) (should (s-starts-with-p "if is a special form defined in" summary)))) (defun helpful-test-fn-interactive () (interactive)) (ert-deftest helpful--summary--interactive-fn () "Ensure we use \"an\" for interactive functions." (let* ((summary (helpful--summary 'helpful-test-fn-interactive t nil nil))) ;; Strip properties to make assertion messages more readable. (set-text-properties 0 (1- (length summary)) nil summary) (should (s-starts-with-p "helpful-test-fn-interactive is an interactive function" summary)))) (defun helpful-test-fn? () (interactive)) (ert-deftest helpful--summary--fn-with-? () "Ensure we use don't needlessly escape ? in function names." (let* ((summary (helpful--summary 'helpful-test-fn? t nil nil))) ;; Strip properties to make assertion messages more readable. (set-text-properties 0 (1- (length summary)) nil summary) (should (s-starts-with-p "helpful-test-fn? is" summary)))) (ert-deftest helpful--signature-fn-with? () "Ensure that symbols with question marks are handled correctly." (should (equal (helpful--signature 'helpful-test-fn?) "(helpful-test-fn?)"))) (defun helpful-test-fn-with\ space () 42) (ert-deftest helpful--summary--symbol-with-space () "Ensure we correctly format symbols containing spaces." (let* ((summary (helpful--summary 'helpful-test-fn-with\ space t nil nil))) ;; Strip properties to make assertion messages more readable. (set-text-properties 0 (1- (length summary)) nil summary) (should (s-starts-with-p "helpful-test-fn-with\\ space is a function" summary)))) (ert-deftest helpful--bound-p () ;; Functions. (should (helpful--bound-p 'message)) ;; Variables (should (helpful--bound-p 'tab-width)) ;; Unbound. (should (not (helpful--bound-p 'this-variable-does-not-exist))) ;; For our purposes, we don't consider nil or t to be bound. (should (not (helpful--bound-p 'nil))) (should (not (helpful--bound-p 't)))) (ert-deftest helpful--callees () (should (equal (helpful--callees '(quote (foo))) nil)) ;; Simple function calls. (should (equal (helpful--callees '(foo (bar 1) 2)) '(foo bar)))) (ert-deftest helpful--callees-let () (should (equal (helpful--callees '(progn (let ((x (foo)) (y t)) (bar x y)) (let (y) (baz)) (let* ((z (quux)))))) '(foo bar baz quux)))) (ert-deftest helpful--callees--lambda () (should (equal (helpful--callees '(lambda (x) (foo x))) '(foo)))) (ert-deftest helpful--callees--closure () (should (equal (helpful--callees '(closure (t) (x) (foo x))) '(foo)))) (ert-deftest helpful--callees--function () (should (equal (helpful--callees '(function (lambda (x) (foo x)))) '(foo))) (should (equal (helpful--callees '(function foo)) '(foo)))) (ert-deftest helpful--callees--cond () (should (equal (helpful--callees '(cond (x) ((foo)) ((bar) (baz)) (t (quux)) )) '(foo bar baz quux)))) (ert-deftest helpful--callees--condition-case () (should (equal (helpful--callees '(condition-case e (foo) (error (bar)) ((arith-error file-error) (baz)))) '(foo bar baz)))) (ert-deftest helpful--callees--funcall () (let ((result (helpful--callees '(progn (funcall 'foo 1) (apply 'bar 2) (apply (baz) 3) (apply unknown-var 3))))) (should (memq 'foo result)) (should (memq 'bar result)) (should (memq 'baz result)) (should (not (memq 'unknown-var result)))) (let ((result (helpful--callees '(progn (funcall #'foo 1) (apply #'bar 2))))) (should (memq 'foo result)) (should (memq 'bar result)))) (ert-deftest helpful--callees-button--smoke () (with-temp-buffer (let ((button (helpful--make-callees-button 'whatever '(defun whatever () (something) (test 5))))) (insert button) (goto-char (point-min)) (push-button))) (with-temp-buffer (let ((button (helpful--make-callees-button '(lambda () (interactive) (other-window -1)) '(lambda () (interactive) (other-window -1))))) (insert button) (goto-char (point-min)) (push-button)))) ;; TODO: broken when byte-compiling helpful.el. ;; (ert-deftest helpful--autoloaded-p () ;; (-let [(buf pos opened) (helpful--definition 'rx-to-string t)] ;; (should (helpful--autoloaded-p 'rx-to-string buf)) ;; (when opened ;; (kill-buffer buf)))) (ert-deftest helpful--inhibit-read-only () (helpful-variable 'inhibit-read-only) (should (s-contains-p "Value\nnil" (buffer-string)))) (ert-deftest helpful--convert-c-name () (should (equal 'make-string (helpful--convert-c-name 'Fmake_string nil))) (should (equal 'gc-cons-percentage (helpful--convert-c-name 'Vgc_cons_percentage t))) (should-not (helpful--convert-c-name 'Fmake_string t)) (should-not (helpful--convert-c-name 'Vgc_cons_percentage nil))) (ert-deftest helpful-symbol-c-style () (helpful-symbol 'Fget_char_property) (helpful-symbol 'Vinhibit_field_text_motion)) (ert-deftest helpful-symbol-unbound () "Ensure we inform the user if we're given an unbound symbol." (should (condition-case _ (helpful-symbol 'notboundtoanything) ('user-error t)))) (ert-deftest helpful--loads-autoload-symbol () "When asked to describe an autoloaded symbol, just load it." ;; This test assumes that you haven't loaded tetris.el.gz in your ;; current instance. (skip-unless (autoloadp (symbol-function 'tetris))) ;; This is a regression test: `tetris' has `tetris-mode-map' in its ;; docstring, so we can't display the mode map unless tetris.el.gz is ;; loaded. ;; (helpful-function #'tetris)) (defcustom helpful-test-custom-var 123 "I am an example custom variable." :type 'number :group 'helpful :package-version '(helpful . "1.2.3")) ;; Ensure the current value differs from the original value. (setq helpful-test-custom-var 456) (ert-deftest helpful--original-value () "Show the original value for defcustom variables." (helpful-variable 'helpful-test-custom-var) (should (s-contains-p "Original Value\n123" (buffer-string)))) (ert-deftest helpful--preserve-position () "Show the original value for defcustom variables." (-let [(buf _pos _opened) (helpful--definition 'helpful-test-custom-var nil)] (with-current-buffer buf (goto-char (point-min)) (save-current-buffer (helpful-variable 'helpful-test-custom-var)) (should (eq (point) (point-min)))))) (ert-deftest helpful--package-version () "Report when a variable was added" (helpful-variable 'helpful-test-custom-var) (should (s-contains-p (s-word-wrap 70 "This variable was added, or its default value changed, in helpful version 1.2.3.") (buffer-string)))) (ert-deftest helpful--display-implementations () (require 'xref) (helpful-function 'xref-location-marker) (should (s-contains-p "Implementations" (buffer-string))) (should (s-contains-p "((l xref-file-location))" (buffer-string))) (should (s-contains-p "((l xref-buffer-location))" (buffer-string)))) (defun helpful--boring-advice (orig-fn &rest args) (apply orig-fn args)) (advice-add 'ruby-mode :around #'helpful--boring-advice) (ert-deftest helpful--autoload-functions-with-advice () "Ensure that we can describe an autoloaded function that has advice attached before it is loadedl." (helpful-function 'ruby-mode)) (ert-deftest helpful--tree-any-p () (should (helpful--tree-any-p (lambda (x) (eq x 1)) '((((1)))))) (should (helpful--tree-any-p (lambda (x) (eq x 1)) (cons 2 1)))) helpful-0.21/test/test-helper.el000066400000000000000000000006771446151713400166500ustar00rootroot00000000000000;;; test-helper.el --- Helper for tests -*- lexical-binding: t; -*- ;; Copyright (C) 2017 Wilfred Hughes ;; Author: ;;; Code: (require 'ert) (require 'f) (let ((helpful-dir (f-parent (f-dirname (f-this-file))))) (add-to-list 'load-path helpful-dir)) (require 'undercover) (undercover "helpful.el" (:exclude "*-test.el") (:report-file "/tmp/undercover-report.json")) ;;; test-helper.el ends here