From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: Barry OReilly Newsgroups: gmane.emacs.devel Subject: Re: [RFC] Editing Lisp through changing indentation Date: Wed, 28 Aug 2013 17:19:07 -0400 Message-ID: NNTP-Posting-Host: plane.gmane.org Mime-Version: 1.0 Content-Type: multipart/alternative; boundary=089e013c6ab42cb74804e5088a51 X-Trace: ger.gmane.org 1377724755 9031 80.91.229.3 (28 Aug 2013 21:19:15 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Wed, 28 Aug 2013 21:19:15 +0000 (UTC) To: emacs-devel@gnu.org Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Wed Aug 28 23:19:19 2013 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by plane.gmane.org with esmtp (Exim 4.69) (envelope-from ) id 1VEn94-0003gG-NP for ged-emacs-devel@m.gmane.org; Wed, 28 Aug 2013 23:19:19 +0200 Original-Received: from localhost ([::1]:38698 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1VEn94-0002mT-2O for ged-emacs-devel@m.gmane.org; Wed, 28 Aug 2013 17:19:18 -0400 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:40652) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1VEn8y-0002mN-4U for emacs-devel@gnu.org; Wed, 28 Aug 2013 17:19:14 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1VEn8w-0003Kz-6c for emacs-devel@gnu.org; Wed, 28 Aug 2013 17:19:12 -0400 Original-Received: from mail-wg0-x22c.google.com ([2a00:1450:400c:c00::22c]:38688) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1VEn8v-0003Jx-Ho for emacs-devel@gnu.org; Wed, 28 Aug 2013 17:19:10 -0400 Original-Received: by mail-wg0-f44.google.com with SMTP id x12so5121273wgg.35 for ; Wed, 28 Aug 2013 14:19:07 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:date:message-id:subject:from:to:content-type; bh=8H9Eu5MjBkaN25+PxicWmfzhGqQnCh7ApiA3HA71jtE=; b=OAtDqj1IpBUH3bapQH0d6tWD36r9gusftUsCj6q4FAuwsHkEdOq9za5g8+IRcapyX3 2JuObWrNr0ndtruUSDwZH/GBYPVLV8FZfHxVlroTgYI9T0Uqs+exlIy7lDpZ6VRt7YTB 2KIEqLxY8Q1LRsVlOC9+lKUG37onBQrT+dTYKjNn8dg5/73VRhzc148Spy2u+PooEWNw mQaU814TGkFRapqxV8eOPbLGchcx4f3UUdfoajUo2hUxd/N3/iBRTJjKLx8fBFF581G3 L5NusLMYD0nc3Ar6aQi0YedLkx84vosB2zwk41PcI9r/lvlog0m8HlosKJ7Xds7CvBYq +Aiw== X-Received: by 10.194.173.66 with SMTP id bi2mr6175649wjc.43.1377724747525; Wed, 28 Aug 2013 14:19:07 -0700 (PDT) Original-Received: by 10.194.234.234 with HTTP; Wed, 28 Aug 2013 14:19:07 -0700 (PDT) X-detected-operating-system: by eggs.gnu.org: Error: Malformed IPv6 address (bad octet value). X-Received-From: 2a00:1450:400c:c00::22c X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Original-Sender: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Xref: news.gmane.org gmane.emacs.devel:163080 Archived-At: --089e013c6ab42cb74804e5088a51 Content-Type: text/plain; charset=windows-1252 Content-Transfer-Encoding: quoted-printable >From http://lists.gnu.org/archive/html/emacs-devel/2013-07/msg00612.html > The motivating idea is this: When reading Lisp, I find I pay > attention to open parens (because foo is not (foo) is not ((foo))) > and just the close parens whose opener is on the same line. When a > sexp spans more than one line, I deduce the close paren from > indentation. If that's how we read Lisp, then why not edit Lisp that > way: change the indentation and let the close parens adjust > themselves to be consistent. > > This would create an editing experience somewhat like python-mode. > There are differences because lisp-mode knows a bit more due to > existing parens. I've been using these commands and find they are useful in practice. Is the feature suitable for core lisp-mode, or should I create a new ELPA package to provide the commands? I've made some updates to the code I posted before: =95 lisp-indent-adjust-parens potentially calls indent-for-tab-command, so as it can be suitable for binding to TAB =95 Implemented a prefix arg to specify levels of indentation to increase or decrease =95 Fixed a couple of edge case bugs Enable in a lisp-mode-hook like: (local-set-key (kbd "TAB") 'lisp-indent-adjust-parens) (local-set-key (kbd "") 'lisp-dedent-adjust-parens) Code: (require 'cl) (defun last-sexp-with-relative-depth (from-pos to-pos rel-depth) "Parsing sexps from FROM-POS (inclusive) to TO-POS (exclusive), return the position of the last sexp that had depth REL-DEPTH relative to FROM-POS. Returns nil if REL-DEPTH is not reached. Examples: Region: a (b c (d)) e (f g (h i)) j Evaluate: (last-sexp-with-relative-depth pos-a (1+ pos-j) 0) Returns: position of j Evaluate: (last-sexp-with-relative-depth pos-a (1+ pos-j) -1) Returns: position of (h i) This function assumes FROM-POS is not in a string or comment." (save-excursion (goto-char from-pos) (let (the-last-pos (parse-state '(0 nil nil nil nil nil nil nil nil))) (while (< (point) to-pos) (setq parse-state (parse-partial-sexp (point) to-pos nil t ; Stop before sexp parse-state)) (and (not (eq (point) to-pos)) (eq (car parse-state) rel-depth) (setq the-last-pos (point))) ;; The previous parse may not advance. To advance and maintain ;; correctness of depth, we parse over the next char. (setq parse-state (parse-partial-sexp (point) (1+ (point)) nil nil parse-state))) the-last-pos))) (defun adjust-close-paren-for-indent () "Adjust a close parentheses of a sexp so as lisp-indent-adjust-parens can indent that many levels. If a close paren was moved, returns a two element list of positions: where the close paren was moved from and the position following where it moved to. If there's no close parens to move, either return nil or allow scan-error to propogate up." (save-excursion (let ((deleted-paren-pos (save-excursion (beginning-of-line) (backward-sexp) ;; Account for edge case when point has no sexp before it (if (bobp) nil ;; If the sexp at point is a list, ;; delete its closing paren (when (eq (scan-lists (point) 1 0) (scan-sexps (point) 1)) (forward-sexp) (delete-char -1) (point)))))) (when deleted-paren-pos (let ((sexp-to-close (last-sexp-with-relative-depth (point) (progn (end-of-line) (point)) 0))) (when sexp-to-close (goto-char sexp-to-close) (forward-sexp)) ;; Note: when no sexp-to-close found, line is empty. So put ;; close paren after point. (insert ")") (list deleted-paren-pos (point))))))) (defun adjust-close-paren-for-dedent () "Adjust a close parentheses of a sexp so as lisp-dedent-adjust-parens can dedent that many levels. If a close paren was moved, returns a two element list of positions: where the close paren was moved from and the position following where it moved to. If there's no close parens to move, either return nil or allow scan-error to propogate up." (save-excursion (let ((deleted-paren-pos (save-excursion (when (< (point) (progn (up-list) (point))) (delete-char -1) (point))))) (when deleted-paren-pos (let ((sexp-to-close ;; Needs to work when dedenting in an empty list, in ;; which case backward-sexp will signal scan-error and ;; sexp-to-close will be nil. (condition-case nil (progn (backward-sexp) (point)) (scan-error nil)))) ;; Move point to where to insert close paren (if sexp-to-close (forward-sexp) (backward-up-list) (forward-char 1)) (insert ")") ;; The insertion makes deleted-paren-pos off by 1 (list (1+ deleted-paren-pos) (point))))))) (defun adjust-parens-p () "Whether to adjust parens." (save-excursion (let ((orig-pos (point))) (back-to-indentation) (and (not (use-region-p)) (<=3D orig-pos (point)))))) (defun adjust-parens-and-indent (adjust-function prefix-arg) "Adjust close parens and indent the region over which the parens moved." (let ((region-of-change (list (point) (point)))) (cl-loop for i from 1 to (or prefix-arg 1) with finished =3D nil while (not finished) do (condition-case err (let ((close-paren-movement (funcall adjust-function))) (if close-paren-movement (setq region-of-change (list (min (car region-of-change) (car close-paren-movement) (cadr close-paren-movement)) (max (cadr region-of-change) (car close-paren-movement) (cadr close-paren-movement)))) (setq finished t))) (scan-error (setq finished err)))) (apply 'indent-region region-of-change)) (back-to-indentation)) (defun lisp-indent-adjust-parens (&optional prefix-arg) "Indent Lisp code to the next level while adjusting sexp balanced expressions to be consistent. This command can be bound to TAB instead of indent-for-tab-command. It potentially calls the latter." (interactive "P") (if (adjust-parens-p) (adjust-parens-and-indent 'adjust-close-paren-for-indent prefix-arg) (indent-for-tab-command prefix-arg))) (defun lisp-dedent-adjust-parens (&optional prefix-arg) "Dedent Lisp code to the previous level while adjusting sexp balanced expressions to be consistent. Binding to (ie Shift-Tab) is a sensible choice." (interactive "P") (when (adjust-parens-p) (adjust-parens-and-indent 'adjust-close-paren-for-dedent prefix-arg))) --089e013c6ab42cb74804e5088a51 Content-Type: text/html; charset=windows-1252 Content-Transfer-Encoding: base64 PGRpdiBkaXI9Imx0ciI+RnJvbSA8YSBocmVmPSJodHRwOi8vbGlzdHMuZ251Lm9yZy9hcmNoaXZl L2h0bWwvZW1hY3MtZGV2ZWwvMjAxMy0wNy9tc2cwMDYxMi5odG1sIj5odHRwOi8vbGlzdHMuZ251 Lm9yZy9hcmNoaXZlL2h0bWwvZW1hY3MtZGV2ZWwvMjAxMy0wNy9tc2cwMDYxMi5odG1sPC9hPjxi cj48YnI+Jmd0OyBUaGUgbW90aXZhdGluZyBpZGVhIGlzIHRoaXM6IFdoZW4gcmVhZGluZyBMaXNw LCBJIGZpbmQgSSBwYXk8YnI+DQomZ3Q7IGF0dGVudGlvbiB0byBvcGVuIHBhcmVucyAoYmVjYXVz ZSBmb28gaXMgbm90IChmb28pIGlzIG5vdCAoKGZvbykpKTxicj4mZ3Q7IGFuZCBqdXN0IHRoZSBj bG9zZSBwYXJlbnMgd2hvc2Ugb3BlbmVyIGlzIG9uIHRoZSBzYW1lIGxpbmUuIFdoZW4gYTxicj4m Z3Q7IHNleHAgc3BhbnMgbW9yZSB0aGFuIG9uZSBsaW5lLCBJIGRlZHVjZSB0aGUgY2xvc2UgcGFy ZW4gZnJvbTxicj4mZ3Q7IGluZGVudGF0aW9uLiBJZiB0aGF0JiMzOTtzIGhvdyB3ZSByZWFkIExp c3AsIHRoZW4gd2h5IG5vdCBlZGl0IExpc3AgdGhhdDxicj4NCiZndDsgd2F5OiBjaGFuZ2UgdGhl IGluZGVudGF0aW9uIGFuZCBsZXQgdGhlIGNsb3NlIHBhcmVucyBhZGp1c3Q8YnI+Jmd0OyB0aGVt c2VsdmVzIHRvIGJlIGNvbnNpc3RlbnQuPGJyPiZndDsgPGJyPiZndDsgVGhpcyB3b3VsZCBjcmVh dGUgYW4gZWRpdGluZyBleHBlcmllbmNlIHNvbWV3aGF0IGxpa2UgcHl0aG9uLW1vZGUuPGJyPiZn dDsgVGhlcmUgYXJlIGRpZmZlcmVuY2VzIGJlY2F1c2UgbGlzcC1tb2RlIGtub3dzIGEgYml0IG1v cmUgZHVlIHRvPGJyPg0KJmd0OyBleGlzdGluZyBwYXJlbnMuPGJyPjxicj5JJiMzOTt2ZSBiZWVu IHVzaW5nIHRoZXNlIGNvbW1hbmRzIGFuZCBmaW5kIHRoZXkgYXJlIHVzZWZ1bCBpbiBwcmFjdGlj ZS48YnI+SXMgdGhlIGZlYXR1cmUgc3VpdGFibGUgZm9yIGNvcmUgbGlzcC1tb2RlLCBvciBzaG91 bGQgSSBjcmVhdGUgYSBuZXc8YnI+RUxQQSBwYWNrYWdlIHRvIHByb3ZpZGUgdGhlIGNvbW1hbmRz Pzxicj48YnI+DQpJJiMzOTt2ZSBtYWRlIHNvbWUgdXBkYXRlcyB0byB0aGUgY29kZSBJIHBvc3Rl ZCBiZWZvcmU6PGJyPqAglSBsaXNwLWluZGVudC1hZGp1c3QtcGFyZW5zIHBvdGVudGlhbGx5IGNh bGxzPGJyPqCgoCBpbmRlbnQtZm9yLXRhYi1jb21tYW5kLCBzbyBhcyBpdCBjYW4gYmUgc3VpdGFi bGUgZm9yIGJpbmRpbmcgdG88YnI+oKCgIFRBQjxicj6gIJUgSW1wbGVtZW50ZWQgYSBwcmVmaXgg YXJnIHRvIHNwZWNpZnkgbGV2ZWxzIG9mIGluZGVudGF0aW9uIHRvPGJyPg0KoKCgIGluY3JlYXNl IG9yIGRlY3JlYXNlPGJyPqAglSBGaXhlZCBhIGNvdXBsZSBvZiBlZGdlIGNhc2UgYnVnczxicj48 YnI+RW5hYmxlIGluIGEgbGlzcC1tb2RlLWhvb2sgbGlrZTo8YnI+PGJyPqAgKGxvY2FsLXNldC1r ZXkgKGtiZCAmcXVvdDtUQUImcXVvdDspICYjMzk7bGlzcC1pbmRlbnQtYWRqdXN0LXBhcmVucyk8 YnI+oCAobG9jYWwtc2V0LWtleSAoa2JkICZxdW90OyZsdDtiYWNrdGFiJmd0OyZxdW90OykgJiMz OTtsaXNwLWRlZGVudC1hZGp1c3QtcGFyZW5zKTxicj4NCjxicj5Db2RlOjxicj48YnI+KHJlcXVp cmUgJiMzOTtjbCk8YnI+PGJyPihkZWZ1biBsYXN0LXNleHAtd2l0aC1yZWxhdGl2ZS1kZXB0aCAo ZnJvbS1wb3MgdG8tcG9zIHJlbC1kZXB0aCk8YnI+oCAmcXVvdDtQYXJzaW5nIHNleHBzIGZyb20g RlJPTS1QT1MgKGluY2x1c2l2ZSkgdG8gVE8tUE9TIChleGNsdXNpdmUpLDxicj5yZXR1cm4gdGhl IHBvc2l0aW9uIG9mIHRoZSBsYXN0IHNleHAgdGhhdCBoYWQgZGVwdGggUkVMLURFUFRIIHJlbGF0 aXZlPGJyPg0KdG8gRlJPTS1QT1MuIFJldHVybnMgbmlsIGlmIFJFTC1ERVBUSCBpcyBub3QgcmVh Y2hlZC48YnI+PGJyPkV4YW1wbGVzOjxicj6gIFJlZ2lvbjqgoCBhIChiIGMgKGQpKSBlIChmIGcg KGggaSkpIGo8YnI+PGJyPqAgRXZhbHVhdGU6IChsYXN0LXNleHAtd2l0aC1yZWxhdGl2ZS1kZXB0 aCBwb3MtYSAoMSsgcG9zLWopIDApPGJyPqAgUmV0dXJuczqgIHBvc2l0aW9uIG9mIGo8YnI+PGJy PqAgRXZhbHVhdGU6IChsYXN0LXNleHAtd2l0aC1yZWxhdGl2ZS1kZXB0aCBwb3MtYSAoMSsgcG9z LWopIC0xKTxicj4NCqAgUmV0dXJuczqgIHBvc2l0aW9uIG9mIChoIGkpPGJyPjxicj5UaGlzIGZ1 bmN0aW9uIGFzc3VtZXMgRlJPTS1QT1MgaXMgbm90IGluIGEgc3RyaW5nIG9yIGNvbW1lbnQuJnF1 b3Q7PGJyPqAgKHNhdmUtZXhjdXJzaW9uPGJyPqCgoCAoZ290by1jaGFyIGZyb20tcG9zKTxicj6g oKAgKGxldCAodGhlLWxhc3QtcG9zPGJyPqCgoKCgoKCgoCAocGFyc2Utc3RhdGUgJiMzOTsoMCBu aWwgbmlsIG5pbCBuaWwgbmlsIG5pbCBuaWwgbmlsKSkpPGJyPg0KoKCgoKAgKHdoaWxlICgmbHQ7 IChwb2ludCkgdG8tcG9zKTxicj6goKCgoKCgIChzZXRxIHBhcnNlLXN0YXRlPGJyPqCgoKCgoKCg oKCgoKAgKHBhcnNlLXBhcnRpYWwtc2V4cCAocG9pbnQpPGJyPqCgoKCgoKCgoKCgoKCgoKCgoKCg oKCgoKCgoKCgoKCgoCB0by1wb3M8YnI+oKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCg IG5pbDxicj6goKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKAgdCA7IFN0b3AgYmVmb3Jl IHNleHA8YnI+DQqgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKAgcGFyc2Utc3RhdGUp KTxicj6goKCgoKCgIChhbmQgKG5vdCAoZXEgKHBvaW50KSB0by1wb3MpKTxicj6goKCgoKCgoKCg oKAgKGVxIChjYXIgcGFyc2Utc3RhdGUpIHJlbC1kZXB0aCk8YnI+oKCgoKCgoKCgoKCgIChzZXRx IHRoZS1sYXN0LXBvcyAocG9pbnQpKSk8YnI+oKCgoKCgoCA7OyBUaGUgcHJldmlvdXMgcGFyc2Ug bWF5IG5vdCBhZHZhbmNlLiBUbyBhZHZhbmNlIGFuZCBtYWludGFpbjxicj4NCqCgoKCgoKAgOzsg Y29ycmVjdG5lc3Mgb2YgZGVwdGgsIHdlIHBhcnNlIG92ZXIgdGhlIG5leHQgY2hhci48YnI+oKCg oKCgoCAoc2V0cSBwYXJzZS1zdGF0ZTxicj6goKCgoKCgoKCgoKCgIChwYXJzZS1wYXJ0aWFsLXNl eHAgKHBvaW50KTxicj6goKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKAgKDErIChwb2lu dCkpPGJyPqCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoCBuaWw8YnI+DQqgoKCgoKCg oKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKAgbmlsPGJyPqCgoKCgoKCgoKCgoKCgoKCgoKCgoKCg oKCgoKCgoKCgoCBwYXJzZS1zdGF0ZSkpKTxicj6goKCgoCB0aGUtbGFzdC1wb3MpKSk8YnI+PGJy PihkZWZ1biBhZGp1c3QtY2xvc2UtcGFyZW4tZm9yLWluZGVudCAoKTxicj6gICZxdW90O0FkanVz dCBhIGNsb3NlIHBhcmVudGhlc2VzIG9mIGEgc2V4cCBzbyBhczxicj5saXNwLWluZGVudC1hZGp1 c3QtcGFyZW5zIGNhbiBpbmRlbnQgdGhhdCBtYW55IGxldmVscy48YnI+DQo8YnI+SWYgYSBjbG9z ZSBwYXJlbiB3YXMgbW92ZWQsIHJldHVybnMgYSB0d28gZWxlbWVudCBsaXN0IG9mIHBvc2l0aW9u czo8YnI+d2hlcmUgdGhlIGNsb3NlIHBhcmVuIHdhcyBtb3ZlZCBmcm9tIGFuZCB0aGUgcG9zaXRp b24gZm9sbG93aW5nIHdoZXJlPGJyPml0IG1vdmVkIHRvLjxicj48YnI+SWYgdGhlcmUmIzM5O3Mg bm8gY2xvc2UgcGFyZW5zIHRvIG1vdmUsIGVpdGhlciByZXR1cm4gbmlsIG9yIGFsbG93PGJyPg0K c2Nhbi1lcnJvciB0byBwcm9wb2dhdGUgdXAuJnF1b3Q7PGJyPqAgKHNhdmUtZXhjdXJzaW9uPGJy PqCgoCAobGV0ICgoZGVsZXRlZC1wYXJlbi1wb3M8YnI+oKCgoKCgoKCgoCAoc2F2ZS1leGN1cnNp b248YnI+oKCgoKCgoKCgoKCgIChiZWdpbm5pbmctb2YtbGluZSk8YnI+oKCgoKCgoKCgoKCgIChi YWNrd2FyZC1zZXhwKTxicj6goKCgoKCgoKCgoKAgOzsgQWNjb3VudCBmb3IgZWRnZSBjYXNlIHdo ZW4gcG9pbnQgaGFzIG5vIHNleHAgYmVmb3JlIGl0PGJyPg0KoKCgoKCgoKCgoKCgIChpZiAoYm9i cCk8YnI+oKCgoKCgoKCgoKCgoKCgoCBuaWw8YnI+oKCgoKCgoKCgoKCgoKAgOzsgSWYgdGhlIHNl eHAgYXQgcG9pbnQgaXMgYSBsaXN0LDxicj6goKCgoKCgoKCgoKCgoCA7OyBkZWxldGUgaXRzIGNs b3NpbmcgcGFyZW48YnI+oKCgoKCgoKCgoKCgoKAgKHdoZW4gKGVxIChzY2FuLWxpc3RzIChwb2lu dCkgMSAwKTxicj6goKCgoKCgoKCgoKCgoKCgoKCgoKCgoKAgKHNjYW4tc2V4cHMgKHBvaW50KSAx KSk8YnI+DQqgoKCgoKCgoKCgoKCgoKCgIChmb3J3YXJkLXNleHApPGJyPqCgoKCgoKCgoKCgoKCg oKAgKGRlbGV0ZS1jaGFyIC0xKTxicj6goKCgoKCgoKCgoKCgoKCgIChwb2ludCkpKSkpKTxicj6g oKCgoCAod2hlbiBkZWxldGVkLXBhcmVuLXBvczxicj6goKCgoKCgIChsZXQgKChzZXhwLXRvLWNs b3NlPGJyPqCgoKCgoKCgoKCgoKCgIChsYXN0LXNleHAtd2l0aC1yZWxhdGl2ZS1kZXB0aCAocG9p bnQpPGJyPg0KoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgIChw cm9nbiAoZW5kLW9mLWxpbmUpPGJyPqCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCg oKCgoKCgoKCgoKCgoKCgoKAgKHBvaW50KSk8YnI+oKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCg oKCgoKCgoKCgoKCgoKCgoKCgIDApKSk8YnI+oKCgoKCgoKCgICh3aGVuIHNleHAtdG8tY2xvc2U8 YnI+DQqgoKCgoKCgoKCgoCAoZ290by1jaGFyIHNleHAtdG8tY2xvc2UpPGJyPqCgoKCgoKCgoKCg IChmb3J3YXJkLXNleHApKTxicj6goKCgoKCgoKAgOzsgTm90ZTogd2hlbiBubyBzZXhwLXRvLWNs b3NlIGZvdW5kLCBsaW5lIGlzIGVtcHR5LiBTbyBwdXQ8YnI+oKCgoKCgoKCgIDs7IGNsb3NlIHBh cmVuIGFmdGVyIHBvaW50Ljxicj6goKCgoKCgoKAgKGluc2VydCAmcXVvdDspJnF1b3Q7KTxicj4N CqCgoKCgoKCgoCAobGlzdCBkZWxldGVkLXBhcmVuLXBvcyAocG9pbnQpKSkpKSkpPGJyPjxicj4o ZGVmdW4gYWRqdXN0LWNsb3NlLXBhcmVuLWZvci1kZWRlbnQgKCk8YnI+oCAmcXVvdDtBZGp1c3Qg YSBjbG9zZSBwYXJlbnRoZXNlcyBvZiBhIHNleHAgc28gYXM8YnI+bGlzcC1kZWRlbnQtYWRqdXN0 LXBhcmVucyBjYW4gZGVkZW50IHRoYXQgbWFueSBsZXZlbHMuPGJyPjxicj5JZiBhIGNsb3NlIHBh cmVuIHdhcyBtb3ZlZCwgcmV0dXJucyBhIHR3byBlbGVtZW50IGxpc3Qgb2YgcG9zaXRpb25zOjxi cj4NCndoZXJlIHRoZSBjbG9zZSBwYXJlbiB3YXMgbW92ZWQgZnJvbSBhbmQgdGhlIHBvc2l0aW9u IGZvbGxvd2luZyB3aGVyZTxicj5pdCBtb3ZlZCB0by48YnI+PGJyPklmIHRoZXJlJiMzOTtzIG5v IGNsb3NlIHBhcmVucyB0byBtb3ZlLCBlaXRoZXIgcmV0dXJuIG5pbCBvciBhbGxvdzxicj5zY2Fu LWVycm9yIHRvIHByb3BvZ2F0ZSB1cC4mcXVvdDs8YnI+oCAoc2F2ZS1leGN1cnNpb248YnI+DQqg oKAgKGxldCAoKGRlbGV0ZWQtcGFyZW4tcG9zPGJyPqCgoKCgoKCgoKAgKHNhdmUtZXhjdXJzaW9u PGJyPqCgoKCgoKCgoKCgoCAod2hlbiAoJmx0OyAocG9pbnQpPGJyPqCgoKCgoKCgoKCgoKCgoKCg oKCgoCAocHJvZ24gKHVwLWxpc3QpPGJyPqCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKAgKHBv aW50KSkpPGJyPqCgoKCgoKCgoKCgoKCgIChkZWxldGUtY2hhciAtMSk8YnI+oKCgoKCgoKCgoKCg oKAgKHBvaW50KSkpKSk8YnI+DQqgoKCgoCAod2hlbiBkZWxldGVkLXBhcmVuLXBvczxicj6goKCg oKCgIChsZXQgKChzZXhwLXRvLWNsb3NlPGJyPqCgoKCgoKCgoKCgoKCgIDs7IE5lZWRzIHRvIHdv cmsgd2hlbiBkZWRlbnRpbmcgaW4gYW4gZW1wdHkgbGlzdCwgaW48YnI+oKCgoKCgoKCgoKCgoKAg Ozsgd2hpY2ggY2FzZSBiYWNrd2FyZC1zZXhwIHdpbGwgc2lnbmFsIHNjYW4tZXJyb3IgYW5kPGJy PqCgoKCgoKCgoKCgoKCgIDs7IHNleHAtdG8tY2xvc2Ugd2lsbCBiZSBuaWwuPGJyPg0KoKCgoKCg oKCgoKCgoKAgKGNvbmRpdGlvbi1jYXNlIG5pbDxicj6goKCgoKCgoKCgoKCgoKCgoKAgKHByb2du IChiYWNrd2FyZC1zZXhwKTxicj6goKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgIChwb2ludCkpPGJy PqCgoKCgoKCgoKCgoKCgoKAgKHNjYW4tZXJyb3IgbmlsKSkpKTxicj6goKCgoKCgoKAgOzsgTW92 ZSBwb2ludCB0byB3aGVyZSB0byBpbnNlcnQgY2xvc2UgcGFyZW48YnI+oKCgoKCgoKCgIChpZiBz ZXhwLXRvLWNsb3NlPGJyPg0KoKCgoKCgoKCgoKCgoCAoZm9yd2FyZC1zZXhwKTxicj6goKCgoKCg oKCgoCAoYmFja3dhcmQtdXAtbGlzdCk8YnI+oKCgoKCgoKCgoKAgKGZvcndhcmQtY2hhciAxKSk8 YnI+oKCgoKCgoKCgIChpbnNlcnQgJnF1b3Q7KSZxdW90Oyk8YnI+oKCgoKCgoKCgIDs7IFRoZSBp bnNlcnRpb24gbWFrZXMgZGVsZXRlZC1wYXJlbi1wb3Mgb2ZmIGJ5IDE8YnI+oKCgoKCgoKCgIChs aXN0ICgxKyBkZWxldGVkLXBhcmVuLXBvcyk8YnI+DQqgoKCgoKCgoKCgoKCgoKAgKHBvaW50KSkp KSkpKTxicj48YnI+KGRlZnVuIGFkanVzdC1wYXJlbnMtcCAoKTxicj6gICZxdW90O1doZXRoZXIg dG8gYWRqdXN0IHBhcmVucy4mcXVvdDs8YnI+oCAoc2F2ZS1leGN1cnNpb248YnI+oKCgIChsZXQg KChvcmlnLXBvcyAocG9pbnQpKSk8YnI+oKCgoKAgKGJhY2stdG8taW5kZW50YXRpb24pPGJyPqCg oKCgIChhbmQgKG5vdCAodXNlLXJlZ2lvbi1wKSk8YnI+DQqgoKCgoKCgoKCgICgmbHQ7PSBvcmln LXBvcyAocG9pbnQpKSkpKSk8YnI+PGJyPihkZWZ1biBhZGp1c3QtcGFyZW5zLWFuZC1pbmRlbnQg KGFkanVzdC1mdW5jdGlvbiBwcmVmaXgtYXJnKTxicj6gICZxdW90O0FkanVzdCBjbG9zZSBwYXJl bnMgYW5kIGluZGVudCB0aGUgcmVnaW9uIG92ZXIgd2hpY2ggdGhlIHBhcmVuczxicj5tb3ZlZC4m cXVvdDs8YnI+oCAobGV0ICgocmVnaW9uLW9mLWNoYW5nZSAobGlzdCAocG9pbnQpIChwb2ludCkp KSk8YnI+DQqgoKAgKGNsLWxvb3AgZm9yIGkgZnJvbSAxIHRvIChvciBwcmVmaXgtYXJnIDEpPGJy PqCgoKCgoKCgoKCgoCB3aXRoIGZpbmlzaGVkID0gbmlsPGJyPqCgoKCgoKCgoKCgoCB3aGlsZSAo bm90IGZpbmlzaGVkKTxicj6goKCgoKCgoKCgoKAgZG88YnI+oKCgoKCgoKCgoKCgIChjb25kaXRp b24tY2FzZSBlcnI8YnI+oKCgoKCgoKCgoKCgoKCgoCAobGV0ICgoY2xvc2UtcGFyZW4tbW92ZW1l bnQ8YnI+DQqgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoCAoZnVuY2FsbCBhZGp1c3QtZnVuY3Rpb24p KSk8YnI+oKCgoKCgoKCgoKCgoKCgoKCgIChpZiBjbG9zZS1wYXJlbi1tb3ZlbWVudDxicj6goKCg oKCgoKCgoKCgoKCgoKCgoKCgIChzZXRxIHJlZ2lvbi1vZi1jaGFuZ2U8YnI+oKCgoKCgoKCgoKCg oKCgoKCgoKCgoKCgoKCgoCAobGlzdCAobWluIChjYXIgcmVnaW9uLW9mLWNoYW5nZSk8YnI+oKCg oKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgIChjYXIgY2xvc2UtcGFyZW4tbW92 ZW1lbnQpPGJyPg0KoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgIChjYWRy IGNsb3NlLXBhcmVuLW1vdmVtZW50KSk8YnI+oKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCg oKCgoCAobWF4IChjYWRyIHJlZ2lvbi1vZi1jaGFuZ2UpPGJyPqCgoKCgoKCgoKCgoKCgoKCgoKCg oKCgoKCgoKCgoKCgoKCgoKCgoCAoY2FyIGNsb3NlLXBhcmVuLW1vdmVtZW50KTxicj6goKCgoKCg oKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKAgKGNhZHIgY2xvc2UtcGFyZW4tbW92ZW1l bnQpKSkpPGJyPg0KoKCgoKCgoKCgoKCgoKCgoKCgoKAgKHNldHEgZmluaXNoZWQgdCkpKTxicj6g oKCgoKCgoKCgoKCgoCAoc2Nhbi1lcnJvciAoc2V0cSBmaW5pc2hlZCBlcnIpKSkpPGJyPqCgoCAo YXBwbHkgJiMzOTtpbmRlbnQtcmVnaW9uIHJlZ2lvbi1vZi1jaGFuZ2UpKTxicj6gIChiYWNrLXRv LWluZGVudGF0aW9uKSk8YnI+PGJyPihkZWZ1biBsaXNwLWluZGVudC1hZGp1c3QtcGFyZW5zICgm YW1wO29wdGlvbmFsIHByZWZpeC1hcmcpPGJyPg0KoCAmcXVvdDtJbmRlbnQgTGlzcCBjb2RlIHRv IHRoZSBuZXh0IGxldmVsIHdoaWxlIGFkanVzdGluZyBzZXhwIGJhbGFuY2VkPGJyPmV4cHJlc3Np b25zIHRvIGJlIGNvbnNpc3RlbnQuPGJyPjxicj5UaGlzIGNvbW1hbmQgY2FuIGJlIGJvdW5kIHRv IFRBQiBpbnN0ZWFkIG9mIGluZGVudC1mb3ItdGFiLWNvbW1hbmQuIEl0PGJyPnBvdGVudGlhbGx5 IGNhbGxzIHRoZSBsYXR0ZXIuJnF1b3Q7PGJyPg0KoCAoaW50ZXJhY3RpdmUgJnF1b3Q7UCZxdW90 Oyk8YnI+oCAoaWYgKGFkanVzdC1wYXJlbnMtcCk8YnI+oKCgoKAgKGFkanVzdC1wYXJlbnMtYW5k LWluZGVudCAmIzM5O2FkanVzdC1jbG9zZS1wYXJlbi1mb3ItaW5kZW50PGJyPqCgoKCgoKCgoKCg oKCgoKCgoKCgoKCgoKCgoKCgoKAgcHJlZml4LWFyZyk8YnI+oKCgIChpbmRlbnQtZm9yLXRhYi1j b21tYW5kIHByZWZpeC1hcmcpKSk8YnI+DQo8YnI+KGRlZnVuIGxpc3AtZGVkZW50LWFkanVzdC1w YXJlbnMgKCZhbXA7b3B0aW9uYWwgcHJlZml4LWFyZyk8YnI+oCAmcXVvdDtEZWRlbnQgTGlzcCBj b2RlIHRvIHRoZSBwcmV2aW91cyBsZXZlbCB3aGlsZSBhZGp1c3Rpbmcgc2V4cDxicj5iYWxhbmNl ZCBleHByZXNzaW9ucyB0byBiZSBjb25zaXN0ZW50Ljxicj48YnI+QmluZGluZyB0byAmbHQ7YmFj a3RhYiZndDsgKGllIFNoaWZ0LVRhYikgaXMgYSBzZW5zaWJsZSBjaG9pY2UuJnF1b3Q7PGJyPg0K oCAoaW50ZXJhY3RpdmUgJnF1b3Q7UCZxdW90Oyk8YnI+oCAod2hlbiAoYWRqdXN0LXBhcmVucy1w KTxicj6goKAgKGFkanVzdC1wYXJlbnMtYW5kLWluZGVudCAmIzM5O2FkanVzdC1jbG9zZS1wYXJl bi1mb3ItZGVkZW50PGJyPqCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgIHByZWZpeC1hcmcp KSk8YnI+PGJyPjxicj48L2Rpdj4NCg== --089e013c6ab42cb74804e5088a51--