pax_global_header00006660000000000000000000000064131725442670014525gustar00rootroot0000000000000052 comment=334065a91ecea7d94e165934dab121795374d3f1 irony-mode-1.2.0/000077500000000000000000000000001317254426700136075ustar00rootroot00000000000000irony-mode-1.2.0/.dir-locals.el000066400000000000000000000002721317254426700162410ustar00rootroot00000000000000((nil (indent-tabs-mode . nil)) (c++-mode (c-basic-offset . 2) (c-file-style . "gnu") (eval . (c-set-offset 'innamespace 0)))) ;; Local Variables: ;; no-byte-compile: t ;; End irony-mode-1.2.0/.gitignore000066400000000000000000000000331317254426700155730ustar00rootroot00000000000000/local-data/ /build/ /bin/ irony-mode-1.2.0/.travis.yml000066400000000000000000000026301317254426700157210ustar00rootroot00000000000000language: cpp sudo: required dist: trusty addons: apt: sources: - llvm-toolchain-trusty-4.0 - sourceline: 'ppa:ubuntu-elisp/ppa' packages: - libclang-dev - emacs - emacs-snapshot - clang-tidy-4.0 compiler: - gcc - clang env: - EMACS=emacs - EMACS=emacs-snapshot matrix: include: - addons: apt: sources: - llvm-toolchain-trusty-4.0 packages: - emacs - clang-4.0 - libclang-4.0-dev - clang-format-4.0 - clang-tidy-4.0 - realpath script: - mkdir build - cd build - | cmake -DCMAKE_C_COMPILER=clang-4.0 -DCMAKE_CXX_COMPILER=clang++-4.0 \ -DIRONY_WARNINGS_AS_ERRORS=ON \ -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ ../server - make -k - make check - | ../server/build-aux/run-clang-tidy/run-clang-tidy.py \ -clang-tidy-binary clang-tidy-4.0 \ -clang-apply-replacements-binary clang-apply-replacements-4.0 \ -p . \ -header-filter $(realpath ../server) \ -warnings-as-errors='*' script: - mkdir build - cd build - $EMACS --version - | cmake \ -DEMACS_EXECUTABLE=$(which $EMACS) \ -DIRONY_WARNINGS_AS_ERRORS=ON \ -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ ../server - make -k - make check irony-mode-1.2.0/CONTRIBUTING.md000066400000000000000000000046731317254426700160520ustar00rootroot00000000000000# How to contribute ## C++ coding style Please, before writing a new file take a look at how the existing files are written. The license bit, the Doxygen header, the Emacs C++ modeline for headers `-*-C++-*-`, ... For formatting it is highly suggested to use [Clang-Format](http://clang.llvm.org/docs/ClangFormat.html), a configuration file is provided, the hidden file *.clang-format* at the root of the project. Apart from some differences written in the *.clang-format* configuration file and in the example that follows it's safe to follow the [LLVM Coding Standards](http://llvm.org/docs/CodingStandards.html). ```cpp /** * The order of declarations in the body of a class favor the public interface * first, e.g: public member functions first, then protected, then private, ... * * Member variables at the bottom of the class. */ class CamelCaseClassName { // types can be declared on the top of a class no matter of the // visibility if they are required by the rest of the class public: typedef void (*blah)(int); private: enum Name { BOB, MICHELLE }; /** * Declare public stuff first, then protected/private at the end of the file. * * Start with ctor/dtor/operator= then member functions */ public: /** * The couple constructors/init-list looks like this. */ CamelCaseClassName(bool camelCaseExceptFirstLetter) : camelCaseExceptFirstLetter_(camelCaseExceptFirstLetter) , endWithUnderscore_(3.0f) , ref_() , ptr_() { } /** * No one-line body, whether they are short or empty. */ ~CamelCaseClassName() { } /** * Methods use this kind of camelCase. * * For the comments, if possible follow how it's done in `clang-c/Index.h`, * with the exception that `\brief` doesn't need to be everywhere, autobrief * is fine. * * Exhaustive Doxygen document is not mandatory but it looks good, and * hopefully one day irony-mode will be able to show these kind of * documentations while working on the code, it would be nice to have the * sources compliant with this. * * \param bar * pfiou. */ void fooBar(int bar); private: Name barFoo(); private: /** * Member variables at the bottom. */ bool camelCaseExceptFirstLetter_; float endWithUnderscore_; // references have a space before and after '&' std::string &ref_; // strangely pointers do not follow the same rules and require space // only before std::string *ptr_; }; ``` irony-mode-1.2.0/COPYING000066400000000000000000001045131317254426700146460ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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 . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . irony-mode-1.2.0/README.md000066400000000000000000000206401317254426700150700ustar00rootroot00000000000000# Irony-Mode ## A C/C++ minor mode powered by [libclang][libclang-ref] `irony-mode` is an Emacs minor-mode that aims at improving the editing experience for the C, C++ and Objective-C languages. It works by using a combination of an Emacs package and a C++ program (`irony-server`) exposing [libclang][libclang-ref]. **Features:** * Code completion: * With Emacs' built-in `completion-at-point-functions` * With [company-mode][company-ref]'s backend: [company-irony][company-irony-ref] * Syntax checking: * With [flycheck][flycheck-ref]'s checker: [flycheck-irony][flycheck-irony-ref] * `eldoc` integration: [irony-eldoc][irony-eldoc-ref] * [counsel][counsel-ref] integration: https://oremacs.com/2017/03/28/emacs-cpp-ide/ ## Dependencies ### Elisp dependencies These dependencies will be installed automatically when using the [standard installation](#installation) procedure described below. | Package | Comment | | -------------------- | -------------------------------------------------------------------------------- | | [cl-lib][cl-lib-ref] | Built-in since Emacs 24.3 | | [json][json-el-ref] | Built-in since Emacs 23.1 | | [YASnippet][yas-ref] | Optional. May be used to provide post-completion expansion of function arguments | ### Irony-Server prerequisites `irony-server` provides the [libclang][libclang-ref] interface to `irony-mode`. It uses a simple protocol based on S-expression. This server, written in C++ and requires the following packages to be installed on your system: * [CMake][cmake-ref] >= 2.8.3 * [libclang][libclang-ref] ## Installation The recommended way to install `irony-mode` and its dependencies is to use a package manager. * Using [MELPA](http://melpa.milkbox.net/) M-x package-install RET irony RET ## Configuration ~~~el (add-hook 'c++-mode-hook 'irony-mode) (add-hook 'c-mode-hook 'irony-mode) (add-hook 'objc-mode-hook 'irony-mode) (add-hook 'irony-mode-hook 'irony-cdb-autosetup-compile-options) ~~~ ## Windows considerations `irony-mode` should work fine on Windows but there are some things to take care of first. * `libclang.dll` is expected to be available in the `PATH` or in Emacs' `exec-path`. * **Emacs >= 24.4 is required.** A bug in previous versions makes irony unuseable (Emacs bug #18420). * `w32-pipe-read-delay` default value of `50` should be changed. This should not cause any issue on today's version of Windows. The default value of `50` may be lowered in mainline Emacs in future versions, until then, I suggest to set it to `0`. * `w32-pipe-buffer-size`, introduced by Emacs 25, can be set to a larger value than the default to improve `irony-server` communication performances (c.f. https://github.com/Sarcasm/irony-mode/issues/321). The variable to customize is `irony-server-w32-pipe-buffer-size`. Windows configuration tweaks to add to your Emacs configuration: ```el ;; Windows performance tweaks ;; (when (boundp 'w32-pipe-read-delay) (setq w32-pipe-read-delay 0)) ;; Set the buffer size to 64K on Windows (from the original 4K) (when (boundp 'w32-pipe-buffer-size) (setq irony-server-w32-pipe-buffer-size (* 64 1024))) ``` ## Usage On the first run, `irony-mode` will ask you to build and install `irony-server`. To do so, type `M-x irony-install-server RET`. To tune `irony-mode`, use `customize`: M-x customize-group RET irony RET In order to provide context sensitive and accurate information, `irony-mode` needs to know about the compiler flags used to parse the current buffer. The best way to achieve this is to use a [Compilation Database](#compilation-database). ## Compilation Database In order to work correctly, `irony-mode` needs to know the compile flags. `irony-cdb` aims to provide *as automatic as possible* compile flags discovery, with minimal user input. Please refer to `irony-cdb-autosetup-compile-options` and `irony-cdb-compilation-databases`. Right now `irony-cdb` supports the following compilation databases: * [JSON Compilation Database][clang-compile-db-ref] - A JSON formatted file generated by various build tools. The file is named `compile_commands.json`, it lists the compile options associated to each file in the project. * [CMake >= 2.8.5][cmake-ref] will generate a compilation database in the build directory when issuing the following command `cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON <...>`. * [ninja >= 1.2][ninja-ref] will generate a JSON Compilation Database when using the `compdb` tool. * [Bear][bear-ref] generates a JSON Compilation Database file by "monitoring" the build of a project. The typical usage for a `make`-based project will be `bear -- make -B`. * [.clang_complete][clang_complete-doc-ref] - A file at the root of your project containing the compilation flags, one per line. This is compatible with the with plugin [Rip-Rip/clang_complete][clang_complete-vim-ref]. If you want to generate the `.clang_complete` automatically, take a look at the [cc_args.py documentation][cc_args-py-doc-ref]. More detailed information on compilation database is available here: * https://sarcasm.github.io/notes/dev/compilation-database.html ## FAQ __It's slow, why?__ A bug in old version of Clang (at least '3.1-8') caused the completion to fail on the standard library types. To eliminate this bug an optimisation has been disabled in the parsing of a translation unit. This result in a slower parsing. This only affect old versions of Clang (< 3.2), it is suggested to update your libclang installation if you want to take advantage of the optimizations. __libclang.so: cannot open shared object file...__ Compiling `irony-server` succeed but you have the following message when you try to run the `irony-server` executable: 'irony-server: error while loading shared libraries: libclang.so: cannot open shared object file: No such file or directory When `libclang` is installed in a non-standard location (one that is missing from the path list of the dynamic loader, see *ld.so.conf*) you can tell CMake to use the [rpath][rpath-ref] when installing the target `irony-server`. To enable rpath in CMake use the following command: cmake -DCMAKE_INSTALL_RPATH_USE_LINK_PATH=ON If you're running OS X, you can also use `install_name_tool` to explicitly tell `irony-server` where an appropriate version of `libclang.dylib` lives. For example, Homebrew (with `brew install llvm --with-clang`) will install a `libclang.dylib` library at `/usr/local/opt/llvm/lib/libclang.dylib`; you can instruct `irony-server` to use this with something like: install_name_tool -change @rpath/libclang.dylib /usr/local/opt/llvm/lib/libclang.dylib irony-server [ac-irony-ref]: https://github.com/Sarcasm/ac-irony "AC Irony" [ac-ref]: https://github.com/auto-complete/auto-complete "Auto Complete" [bear-ref]: https://github.com/rizsotto/Bear "Bear" [cc_args-py-doc-ref]: https://github.com/Rip-Rip/clang_complete/blob/c8673142759b87316265eb0edd1f620196ec1fba/doc/clang_complete.txt#L270 "cc_args.py documentation" [cl-lib-ref]: http://elpa.gnu.org/packages/cl-lib.html "cl-lib" [clang-compile-db-ref]: http://clang.llvm.org/docs/JSONCompilationDatabase.html "Clang: JSONCompilationDatabase" [clang_complete-doc-ref]: https://github.com/Rip-Rip/clang_complete/blob/c8673142759b87316265eb0edd1f620196ec1fba/doc/clang_complete.txt#L55 ".clang_complete" [clang_complete-vim-ref]: https://github.com/Rip-Rip/clang_complete "clang_complete Vim plugin" [cmake-ref]: http://www.cmake.org "CMake" [counsel-ref]: https://github.com/abo-abo/swiper#counsel "Counsel on Github" [company-irony-ref]: https://github.com/Sarcasm/company-irony "Company Irony" [company-ref]: https://github.com/company-mode/company-mode "Company-Mode" [flycheck-irony-ref]: https://github.com/Sarcasm/flycheck-irony "Flycheck Irony" [flycheck-ref]: http://www.flycheck.org "Flycheck -- Modern Emacs syntax checking" [irony-eldoc-ref]: https://github.com/ikirill/irony-eldoc "irony-eldoc -- irony-mode support for eldoc-mode" [json-el-ref]: http://edward.oconnor.cx/2006/03/json.el "Introducing json.el" [libclang-ref]: http://clang.llvm.org/doxygen/group__CINDEX.html "libclang: C Interface to Clang" [ninja-ref]: https://ninja-build.org "Ninja" [rpath-ref]: http://en.wikipedia.org/wiki/Rpath "rpath Wikipedia article" [yas-ref]: https://github.com/capitaomorte/yasnippet "YASnippet" irony-mode-1.2.0/irony-cdb-clang-complete.el000066400000000000000000000041641317254426700207140ustar00rootroot00000000000000;;; irony-cdb-clang-complete.el --- .clang_complete compilation database ;; Copyright (C) 2014 Guillaume Papin ;; Author: Guillaume Papin ;; Keywords: c, convenience, tools ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Commentary: ;; ;; This file defines a compilation database for .clang_complete file. ;; ;;; Code: (require 'irony-cdb) (require 'cl-lib) ;;;###autoload (defun irony-cdb-clang-complete (command &rest args) (cl-case command (get-compile-options (irony-cdb-clang-complete--get-compile-options)))) (defun irony-cdb-clang-complete--get-compile-options () (irony--awhen (irony-cdb-clang-complete--locate-db) (irony-cdb-clang-complete--load-db it))) (defun irony-cdb-clang-complete--locate-db () (when buffer-file-name (irony--awhen (locate-dominating-file buffer-file-name ".clang_complete") (concat (file-name-as-directory it) ".clang_complete")))) (defun irony-cdb-clang-complete--load-db (cc-file) (with-temp-buffer (insert-file-contents cc-file) (list (cons ;; compile options with trailing whitespaces removed (mapcar #'(lambda (line) (if (string-match "[ \t]+$" line) (replace-match "" t t line) line)) (split-string (buffer-string) "\n" t)) ;; working directory (expand-file-name (file-name-directory cc-file)))))) (provide 'irony-cdb-clang-complete) ;; Local Variables: ;; byte-compile-warnings: (not cl-functions) ;; End: ;;; irony-cdb-clang-complete ends here irony-mode-1.2.0/irony-cdb-json.el000066400000000000000000000300741317254426700167720ustar00rootroot00000000000000;;; irony-cdb-json.el --- JSON Compilation Database support for irony ;; Copyright (C) 2014 Guillaume Papin ;; Author: Guillaume Papin ;; Keywords: c, convenience, tools ;; 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: ;; ;; JSON Compilation Database support for Irony, see ;; http://clang.llvm.org/docs/JSONCompilationDatabase.html. ;; ;;; Code: (require 'irony-cdb) (require 'cl-lib) (require 'json) (require 'pp) (defvar irony-cdb-json--project-alist nil "Alist of source directory and compile_commands.json locations. Note, the compile_commands.json location may be relative to the source directory.") (defconst irony-cdb-json--project-alist-file (concat irony-user-dir "cdb-json-projects")) ;;;###autoload (defun irony-cdb-json (command &rest args) (cl-case command (get-compile-options (irony-cdb-json--get-compile-options)))) ;;;###autoload (defun irony-cdb-json-add-compile-commands-path (project-root compile-commands-path) "Add an out-of-source compilation database. Files below the PROJECT-ROOT directory will use the JSON Compilation Database as specified by COMPILE-COMMANDS-PATH. The JSON Compilation Database are often generated in the build directory. This functions helps mapping out-of-source build directories to project directory." (interactive (progn (let ((proot (read-directory-name "Project root:" nil nil t))) (list proot (read-file-name "Compile commands:" proot nil t "compile_commands.json"))))) (add-to-list 'irony-cdb-json--project-alist (cons (expand-file-name project-root) (expand-file-name compile-commands-path))) (irony-cdb-json--save-project-alist) ; and tell irony to load it now (irony-cdb-autosetup-compile-options)) (defun irony-cdb-json--put-first (pos target-list) (if (>= pos (length target-list)) target-list (let ((elm (nth pos target-list))) (append (list elm) (delete elm target-list))))) (defun irony-cdb-json--choose-cdb () "Prompt to select CDB from current project root." (let* ((proot (irony-cdb-json--find-best-prefix-path (irony-cdb-json--target-path) (mapcar 'car irony-cdb-json--project-alist))) (cdbs (mapcar 'cdr (cl-remove-if-not (lambda (x) (string-equal proot (car x))) irony-cdb-json--project-alist)))) (completing-read "Choose Irony CDB: " cdbs nil 'require-match nil))) ;;;###autoload (defun irony-cdb-json-select () "Select CDB to use with a prompt. It is useful when you have several CDBs with the same project root. The completion function used internally is `completing-read' so it could easily be used with other completion functions by temporarily using a let-bind on `completing-read-function'. Or even helm by enabling `helm-mode' before calling the function." (interactive) (let ((pos (cl-position (irony-cdb-json--choose-cdb) irony-cdb-json--project-alist :test (lambda (x y) (string-equal x (cdr y)))))) (setq irony-cdb-json--project-alist (irony-cdb-json--put-first pos irony-cdb-json--project-alist)) (irony-cdb-json--save-project-alist) (irony-cdb-autosetup-compile-options))) (defun irony-cdb-json--last-mod (file) "File modification time or null time if file doesn't exist." (or (nth 5 (file-attributes file)) '(0 0 0 0))) ;;;###autoload (defun irony-cdb-json-select-most-recent () "Select CDB that is most recently modified." (interactive) (setq irony-cdb-json--project-alist (sort irony-cdb-json--project-alist (lambda (x y) (time-less-p (irony-cdb-json--last-mod (cdr y)) (irony-cdb-json--last-mod (cdr x)))))) (irony-cdb-json--save-project-alist) (irony-cdb-autosetup-compile-options)) (defun irony-cdb-json--get-compile-options () (irony--awhen (irony-cdb-json--locate-db) (let ((db (irony-cdb-json--load-db it))) (irony--aif (irony-cdb-json--exact-flags db) it (let ((dir-cdb (irony-cdb-json--compute-directory-cdb db))) (irony-cdb-json--guess-flags dir-cdb)))))) (defsubst irony-cdb-json--target-path () (or buffer-file-name (expand-file-name default-directory))) (defun irony-cdb-json--ensure-project-alist-loaded () (unless irony-cdb-json--project-alist (irony-cdb-json--load-project-alist))) (defun irony-cdb-json--save-project-alist () (with-temp-file irony-cdb-json--project-alist-file (insert ";; -*- emacs-lisp -*-\n\ ;;\n\ ;; JSON Compilation Database project list.\n\ ;;\n\ ;; File auto-generated by irony-cdb-json.\n\ ;;\n") (pp irony-cdb-json--project-alist (current-buffer)) (insert "\n"))) (defun irony-cdb-json--load-project-alist () (when (file-exists-p irony-cdb-json--project-alist-file) (setq irony-cdb-json--project-alist (with-temp-buffer (insert-file-contents irony-cdb-json--project-alist-file) (read (current-buffer)))))) (defun irony-cdb-json--find-best-prefix-path (file prefixes) (cl-loop for prefix in prefixes with found = nil ;; keep the closest directory if (and (string-prefix-p prefix file) (> (length prefix) (length found))) do (setq found prefix) finally return found)) (defun irony-cdb-json--locate-db () (irony-cdb-json--ensure-project-alist-loaded) (irony--aif (irony-cdb-json--find-best-prefix-path (irony-cdb-json--target-path) (mapcar 'car irony-cdb-json--project-alist)) (expand-file-name (cdr (assoc it irony-cdb-json--project-alist)) it) ;; If not in the project table, look in the dominating directories (irony--awhen (irony-cdb--locate-dominating-file-with-dirs (irony-cdb-json--target-path) "compile_commands.json" irony-cdb-search-directory-list) (expand-file-name it)))) (defun irony-cdb-json--load-db (json-file) (delq nil (mapcar #'irony-cdb-json--transform-compile-command ;; JSON read may throw (json-read-file json-file)))) (defun irony-cdb-json--exact-flags (file-cdb) (when buffer-file-name (mapcar #'(lambda (e) (cons (nth 1 e) (nth 2 e))) (irony--assoc-all buffer-file-name file-cdb)))) (defun irony-cdb-json--guess-flags (dir-cdb) (cl-loop for e in dir-cdb with buf-path = (irony-cdb-json--target-path) with found = nil for dir = (car e) ;; keep the closest directory if (and (string-prefix-p dir buf-path) (> (length dir) (length found))) do (setq found e) finally return (list (cons (nth 1 found) (nth 2 found))))) (defsubst irony-cdb-json--compile-command-directory (compile-command) (cdr (assq 'directory compile-command))) (defsubst irony-cdb-json--compile-command-file (compile-command) (cdr (assq 'file compile-command))) (defun irony-cdb-json--compile-command-options (compile-command) "Return the compile options of COMPILE-COMMAND as a list." (let ((command (assq 'command compile-command)) (arguments (assq 'arguments compile-command))) (irony-cdb--remove-compiler-from-flags (cond (command (irony--split-command-line (cdr command))) (arguments (append (cdr arguments) nil)))))) (defun irony-cdb-json--adjust-compile-options (compile-options file default-dir) "Adjust COMPILE-OPTIONS to only use options useful for parsing. COMPILE-OPTIONS is modified by side effects but the returned list should be used since elements can change at the head. Removes the input file, the output file, ... Relative paths are relative to DEFAULT-DIR." ;; compute the absolute path for FILE only once (setq file (expand-file-name file default-dir)) (let* ((head (cons 'nah compile-options)) (it head) opt) (while (setq opt (cadr it)) (cond ;; end of options, skip all positional arguments (source files) ((string= opt "--") (setcdr it nil)) ;; strip -c ((string= "-c" opt) (setcdr it (nthcdr 2 it))) ;; strip -o and -o ((string-prefix-p "-o" opt) (if (string= opt "-o") (setcdr it (nthcdr 3 it)) (setcdr it (nthcdr 2 it)))) ;; skip input file ((string= file (expand-file-name opt default-dir)) (setcdr it (nthcdr 2 it))) (t ;; if head of cdr hasn't been skipped, iterate, otherwise check if the ;; new cdr need skipping (setq it (cdr it))))) (cdr head))) (defun irony-cdb-json--transform-compile-command (compile-command) "Transform a compile command in the JSON compilation database into a friendlier format. The returned value is a list composed of the following elements: 0. The absolute path to the file. 1. The compile options. 2. The invocation directory. Relative paths in the compile options elements are relative to this directory. Return nil if the compile command is invalid or the compile options are empty." (let* ((directory (irony-cdb-json--compile-command-directory compile-command)) (path (expand-file-name (irony-cdb-json--compile-command-file compile-command) directory)) (options (irony-cdb-json--compile-command-options compile-command))) (when (and path directory options) (list path (irony-cdb-json--adjust-compile-options options path directory) directory)))) (defun irony-cdb-json--compute-directory-cdb (file-cdb) ;; collect flags by directory, e.g: for headers in source directories or ;; new files that are not yet present in the compilation database (let ((dir-cdb (irony-cdb-json--collect-compile-options-by-dir file-cdb))) (nconc dir-cdb ;; collect flags for header search paths too (irony-cdb-json--collect-compile-options-for-include-dirs dir-cdb)))) (defun irony-cdb-json--collect-compile-options-by-dir (file-cdb) "Collect the compile options per directory from a file compilation database. The returned value similar to `irony-cdb-json--transform-compile-command' except for the first argument which represents a whole directory (ending with slash on Unix, `file-name-as-directory') instead of a single file." (let ((dir-cdb (delete-dups (mapcar #'(lambda (e) (cons (file-name-directory (car e)) (cdr e))) file-cdb)))) ;; TODO: remove directories when a parent directory has the same flags, for ;; example, writing the following in CMake: ;; add_executable(exe foo.cpp sub/bar.cpp) ;; will result in duplicated compile options for the subdirectory 'sub/'. dir-cdb)) (defun irony-cdb-json--collect-compile-options-for-include-dirs (dir-cdb) "Guess the compile options to use for directories in the search path. The returned value is in the same format as the input value, see `irony-cdb-json--collect-compile-options-for-include-dirs'." (let ((include-dirs (delete-dups (mapcar 'car dir-cdb))) out) (dolist (e dir-cdb) (dolist (dir (irony--extract-user-search-paths (nth 1 e) (nth 2 e))) (unless (member dir include-dirs) (setq include-dirs (cons dir include-dirs) out (cons (cons dir (cdr e)) out))))) out)) (provide 'irony-cdb-json) ;; Local Variables: ;; byte-compile-warnings: (not cl-functions) ;; End: ;;; irony-cdb-json ends here irony-mode-1.2.0/irony-cdb-libclang.el000066400000000000000000000047661317254426700176050ustar00rootroot00000000000000;;; irony-cdb-libclang.el --- Compilation Database for irony using libclang ;; Copyright (C) 2015 Karl Hylén ;; Author: Karl Hylén ;; Keywords: c, convenience, tools ;; 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: ;; ;; Compilation Database support for Irony using libclangs CXCompilationDatabase, ;; http://clang.llvm.org/doxygen/group__COMPILATIONDB.html ;;; Code: (require 'irony-cdb) (require 'irony-cdb-json) (require 'cl-lib) ;;;###autoload (defun irony-cdb-libclang (command &rest args) (cl-case command (get-compile-options (irony-cdb-libclang--get-compile-options)))) (defun irony-cdb-libclang--get-compile-options () (irony--awhen (irony-cdb-json--locate-db) (irony-cdb-libclang--server-exact-flags it))) (defun irony-cdb-libclang--server-exact-flags (db-file) "Get compilation options from irony-server. The parameter DB-FILE is the database file." (when buffer-file-name (let* ((build-dir (file-name-directory db-file)) (file buffer-file-name) (task (irony--get-compile-options-task build-dir file)) (compile-options (irony--run-task task))) (irony-cdb-libclang--adjust-options-and-remove-compiler file compile-options)))) (defun irony-cdb-libclang--adjust-options-and-remove-compiler (file cmds) "Remove compiler, target file FILE and output file from CMDS. The parameter CMDS is a list of conses. In each cons, the car holds the options and the cdr holds the working directory where the compile command was issued." (mapcar (lambda (cmd) (let ((opt (irony-cdb--remove-compiler-from-flags (car cmd))) (wdir (cdr cmd))) (cons (irony-cdb-json--adjust-compile-options opt file wdir) wdir))) cmds)) (provide 'irony-cdb-libclang) ;; Local Variables: ;; byte-compile-warnings: (not cl-functions) ;; End: ;;; irony-cdb-libclang ends here irony-mode-1.2.0/irony-cdb.el000066400000000000000000000203531317254426700160220ustar00rootroot00000000000000;;; irony-cdb.el --- compilation databases support for irony ;; Copyright (C) 2012-2014 Guillaume Papin ;; Author: Guillaume Papin ;; Keywords: c, convenience, tools ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Commentary: ;; ;; This file defines the compilation database interface of irony-mode. ;; ;; Note:For compilation database that looks for a specific file, such as ;; .clang_complete or compile_commands.json, favor `locate-dominating-file' to a ;; handwritten logic if possible as it may be configured by the user to do "the ;; Right Thing (TM)". See `locate-dominating-stop-dir-regexp'. ;; ;;; Code: (require 'irony) (require 'cl-lib) (autoload 'irony-cdb-clang-complete "irony-cdb-clang-complete") (autoload 'irony-cdb-json "irony-cdb-json") (autoload 'irony-cdb-libclang "irony-cdb-libclang") ;; ;; Customizable variables ;; (defgroup irony-cdb nil "Irony's compilation database interface." :group 'irony) (defcustom irony-cdb-compilation-databases '(irony-cdb-clang-complete irony-cdb-libclang irony-cdb-json) "List of active compilation databases. The compilation database should respond for the following commands: `get-compile-options': Takes no argument. This function finds the compile options used for the current buffer. It must return a list of cons where the first element is a set of compile options and the second element the working directory expected for these commands. The compilation database should return an empty list for files that it cannot handle." :type '(repeat function) :group 'irony-cdb) (defcustom irony-cdb-search-directory-list '("." "build") "List of relative subdirectory paths to be searched for cdb files Irony looks for cdb files in any of the supported format by checking each directory from the currently loaded file and recursively through parent directories until it hits the root directory or a cdb is found. At each level of the search irony looks at the subdirectories listed in `irony-cdb-search-directory-list' for the files. Customize this list if your cdb is held in a custom directory within you project, such as a custom named build directory. " :type '(repeat string) :group 'irony-cdb) ;; ;; Internal variables ;; (defvar-local irony-cdb--compilation-database nil) ;; ;; Irony Compilation Database Interface ;; ;;;###autoload (defun irony-cdb-autosetup-compile-options () (interactive) (irony--awhen (irony-cdb--autodetect-compile-options) (setq irony-cdb--compilation-database (nth 0 it)) (irony-cdb--update-compile-options (nth 1 it) (nth 2 it)))) ;;;###autoload (defun irony-cdb-menu () (interactive) (let ((compilation-database irony-cdb--compilation-database) (working-directory irony--working-directory) (compile-options irony--compile-options)) (save-excursion (save-window-excursion (delete-other-windows) (let ((buffer (get-buffer-create "*Irony/Compilation DB Menu*"))) (with-current-buffer buffer (erase-buffer) (if (null compilation-database) (insert "No compilation database in use.\n") (insert (format "Compilation Database: %s\n\n" (symbol-name compilation-database))) (insert (format " Working Directory: %s\n" working-directory)) (insert (format " Compile Options: %s\n" (mapconcat 'identity compile-options " ")))) (insert "\n[q] to quit")) (let ((pop-up-windows t)) (display-buffer buffer t)) (fit-window-to-buffer (get-buffer-window buffer)) (irony--read-char-choice "Irony CDB Buffer" (list ?q))))) ;; clear `read-char-choice' prompt (message ""))) ;; ;; Functions ;; (defun irony-cdb--choose-closest-path (file paths) "Find the \"best\" path in PATHS matching FILE If any paths in PATHS is belongs to the same directory or a subdirectory of file, we disregard other candidates. For remaining candidates, \"nearest\" is measured as abs. difference in path depth. - We prefer deeper paths at level +N to those at level -N. - If multiple paths are equally good, we return the last one. Returns nil if paths isn't a list of at least one element. " (when (listp paths) (let ((paths (or ;; if we find a cdb in cwd or below, don't consider other candidates (cl-remove-if-not (lambda (x) (string-prefix-p (file-name-directory file) x)) paths) paths))) (cl-loop for path in paths with best-depth-delta = 999999 ; start at +inf with best-path = nil ; we keep the best so far here ;; all candidates have their depth compared to that of target file with file-depth = (length (split-string (expand-file-name file) "/")) ; for candidate-depth = (length (split-string (expand-file-name path) "/")) ;; Our metric. We use signum as a tie-breaker to choose deeper candidates for depth-delta = (+ (abs (- file-depth candidate-depth)) (* 0.1 (- file-depth candidate-depth))) do (when (< depth-delta best-depth-delta) (progn (setq best-depth-delta depth-delta) (setq best-path path))) finally return best-path)))) (defun irony-cdb--locate-dominating-file-with-dirs (file name subdirectories) "Convenience wrapper around `locate-dominating-file' Looks up the directory hierarchy from FILE for to locate any directory in `subdirectories` which contains NAME. If multiple files are found, chooses the one located at the nearest directory level. if multiple files are found at the same level, picks the first one encountered. returns the full path to file if found, or nil otherwise." (let ((candidates (cl-loop for subdir in subdirectories for relpath = (concat (file-name-as-directory subdir) name) for match-maybe = (locate-dominating-file file relpath) when match-maybe collect (expand-file-name (concat match-maybe relpath))))) (irony-cdb--choose-closest-path file candidates))) (defun irony-cdb--update-compile-options (compile-options &optional working-directory) (setq irony--compile-options compile-options irony--working-directory working-directory)) (defun irony-cdb--autodetect-compile-options () (catch 'found (dolist (compilation-database irony-cdb-compilation-databases) (with-demoted-errors "Irony CDB: error in compilation database: %S" (irony--awhen (funcall compilation-database 'get-compile-options) (throw 'found (list compilation-database (caar it) (cdar it)))))))) (defun irony-cdb--string-suffix-p (suffix string &optional ignore-case) "Return non-nil if SUFFIX is a suffix of STRING." (let ((start-pos (- (length string) (length suffix)))) (and (>= start-pos 0) (eq t (compare-strings suffix nil nil string start-pos nil ignore-case))))) (defun irony-cdb--remove-compiler-from-flags (flags) "Remove the compiler from FLAGS read from a compilation database. When using ccache, the compiler might be present in FLAGS since the compiler is `ccache compiler'." (let* ((first (car flags)) (flags (cdr flags))) (if (irony-cdb--string-suffix-p "ccache" first) (cdr flags) flags))) (provide 'irony-cdb) ;; Local Variables: ;; byte-compile-warnings: (not cl-functions) ;; End: ;;; irony-cdb.el ends here irony-mode-1.2.0/irony-completion.el000066400000000000000000000402731317254426700174460ustar00rootroot00000000000000;;; irony-completion.el --- irony-mode completion interface -*- lexical-binding: t -*- ;; Copyright (C) 2012-2014 Guillaume Papin ;; Author: Guillaume Papin ;; Keywords: c, convenience, tools ;; 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: ;; Handle the search of completion points, the triggering of the ;; completion when needed and the "parsing" of completion results. ;;; Code: (require 'irony) (require 'irony-snippet) (require 'cl-lib) ;; ;; Customizable variables ;; (defgroup irony-completion nil "Irony's completion interface." :group 'irony) (defcustom irony-completion-trigger-commands '(self-insert-command newline-and-indent c-context-line-break c-scope-operator ;; electric commands c-electric-backspace c-electric-brace c-electric-colon c-electric-lt-gt c-electric-paren c-electric-pound c-electric-semi&comma c-electric-slash c-electric-star) "List of commands to watch for asynchronous completion triggering." :type '(repeat function) :group 'irony-completion) (defcustom irony-completion-availability-filter '(available deprecated) "For completion, only accept candidates whose availability is in the list. Maps to libclang's CXAvailabilityKind: - https://clang.llvm.org/doxygen/group__CINDEX.html#gada331ea0195e952c8f181ecf15e83d71 Due to a bug in Clang (https://bugs.llvm.org//show_bug.cgi?id=24329), candidates that can be validly accessed are deemed not-accessible." :type '(repeat symbol) :options '(available deprecated not-accessible) :group 'irony-completion) ;; ;; Utility functions ;; (defun irony-completion-symbol-bounds () (let ((pt (point)) (syntax (syntax-ppss))) ;; no prefix for strings or comments ;; TODO: Use fontlock faces instead? at least ;; #warning In the middle of a warning| ;; will be handled properly but things like links when ;; `goto-address-prog-mode' is enabled will mess up things: ;; #error see bug report XY: http://example.com/XY (unless (or (nth 3 syntax) ;skip strings (nth 4 syntax)) ;skip comments (save-excursion (skip-chars-backward "_a-zA-Z0-9") (let ((ch (char-after))) (unless (and ch (>= ch ?0) (<= ch ?9)) ;skip numbers (when (eq (char-before) ?~) (backward-char)) (setq pt (point)) (skip-chars-forward "_a-zA-Z0-9~") (cons pt (point)))))))) (defun irony-completion-beginning-of-symbol () (car (irony-completion-symbol-bounds))) (defun irony-completion-end-of-symbol () (cdr (irony-completion-symbol-bounds))) (defsubst irony-completion--skip-whitespaces-backward () ;;(skip-syntax-backward "-") doesn't seem to care about newlines (skip-chars-backward " \t\n\r")) (defun irony-completion--parse-context-position (&optional pos) (save-excursion (when pos (goto-char pos)) (irony-completion--skip-whitespaces-backward) (point))) (defun irony--completion-line-column (&optional pos) (save-excursion (when pos (goto-char pos)) ;; `position-bytes' to handle multibytes and 'multicolumns' (i.e ;; tabulations) characters properly (irony--without-narrowing (cons (line-number-at-pos) (1+ (- (position-bytes (point)) (position-bytes (point-at-bol)))))))) ;; ;; Functions ;; (defun irony-completion--enter () (add-hook 'completion-at-point-functions 'irony-completion-at-point nil t)) (defun irony-completion--exit () (remove-hook 'completion-at-point-functions 'irony-completion-at-point t)) (defun irony-completion--post-complete-yas-snippet (str placeholders) (let ((ph-count 0) (from 0) to snippet) (while (setq to (car placeholders) snippet (concat snippet (substring str from to) (format "${%d:%s}" (cl-incf ph-count) (substring str (car placeholders) (cadr placeholders)))) from (cadr placeholders) placeholders (cddr placeholders))) ;; handle the remaining non-snippet string, if any. (concat snippet (substring str from) "$0"))) ;; ;; Interface with irony-server ;; (irony-iotask-define-task irony--t-complete "`complete' server command." :start (lambda (file line column compile-options) (apply #'irony--server-send-command "complete" file line column "--" compile-options)) :update irony--server-command-update) (defun irony--complete-task-1 (&optional buffer pos) (with-current-buffer (or buffer (current-buffer)) (let ((line-column (irony--completion-line-column pos))) (irony-iotask-package-task irony--t-complete (irony--get-buffer-path-for-server) (car line-column) (cdr line-column) (irony--adjust-compile-options))))) (defun irony--complete-task (&optional buffer pos) (let ((unsaved-tasks (irony--unsaved-buffers-tasks)) (complete-task (irony--complete-task-1 buffer pos))) (if unsaved-tasks (irony-iotask-chain unsaved-tasks complete-task) complete-task))) (irony-iotask-define-task irony--t-candidates "`candidates' server command." :start (lambda (prefix style) (irony--server-send-command "candidates" prefix (cl-case style (case-insensitive "case-insensitive") (smart-case "smart-case") (t "exact")))) :update irony--server-query-update) (defun irony--candidates-task (&optional buffer pos prefix style) (irony-iotask-chain (irony--complete-task buffer pos) (irony-iotask-package-task irony--t-candidates prefix style))) ;; ;; Irony Completion Interface ;; (defun irony-completion-typed-text (candidate) (nth 0 candidate)) (defun irony-completion-priority (candidate) (nth 1 candidate)) (defun irony-completion-type (candidate) (nth 2 candidate)) (defun irony-completion-brief (candidate) (nth 3 candidate)) (defun irony-completion-annotation (candidate) (substring (nth 4 candidate) (nth 5 candidate))) (defun irony-completion-post-comp-str (candidate) (car (nth 6 candidate))) (defun irony-completion-post-comp-placeholders (candidate) (cdr (nth 6 candidate))) (defun irony-completion-availability (candidate) "See `irony-completion-availability-filter'" (nth 7 candidate)) (defun irony-completion--filter-candidates (candidates) (cl-remove-if-not (lambda (candidate) (memq (irony-completion-availability candidate) irony-completion-availability-filter)) candidates)) (defun irony-completion-candidates (&optional prefix style) "Return the list of candidates at point. A candidate is composed of the following elements: 0. The typed text. Multiple candidates can share the same string because of overloaded functions, default arguments, etc. 1. The priority. 2. The [result-]type of the candidate, if any. 3. If non-nil, contains the Doxygen brief documentation of the candidate. 4. The signature of the candidate excluding the result-type which is available separately. Example: \"foo(int a, int b) const\" 5. The annotation start, a 0-based index in the prototype string. 6. Post-completion data. The text to insert followed by 0 or more indices. These indices work by pairs and describe ranges of placeholder text. Example: (\"(int a, int b)\" 1 6 8 13) 7. The availability of the candidate." (irony--awhen (irony-completion-symbol-bounds) (irony-completion--filter-candidates (irony--run-task (irony--candidates-task nil (car it) prefix style))))) (defun irony-completion-candidates-async (callback &optional prefix style) (irony--aif (irony-completion-symbol-bounds) (irony--run-task-asynchronously (irony--candidates-task nil (car it) prefix style) (lambda (candidates-result) (funcall callback (irony-completion--filter-candidates (irony-iotask-result-get candidates-result))))) (funcall callback nil))) (defun irony-completion-post-complete (candidate) (let ((str (irony-completion-post-comp-str candidate)) (placeholders (irony-completion-post-comp-placeholders candidate))) (if (and placeholders (irony-snippet-available-p)) (irony-snippet-expand (irony-completion--post-complete-yas-snippet str placeholders)) (insert (substring str 0 (car placeholders)))))) (defun irony-completion-at-trigger-point-p () (when (eq (point) (irony-completion-beginning-of-symbol)) (save-excursion (cond ;; use `re-search-backward' so that the cursor is moved just before the ;; member access, if any ((re-search-backward (format "%s\\=" (regexp-opt '("." ;object member access "->" ;pointer member access "::"))) ;scope operator (point-at-bol) t) (unless ;; ignore most common uses of '.' where it's not a member access (and (eq (char-after) ?.) (or ;; include statements: #include (looking-back "^#\\s-*include\\s-+[<\"][^>\"]*" (point-at-bol)) ;; floating point numbers (not thorough, see: ;; http://en.cppreference.com/w/cpp/language/floating_literal) (looking-back "[^_a-zA-Z0-9][[:digit:]]+" (point-at-bol)))) ;; except the above exceptions we use a "whitelist" for the places ;; where it looks like a member access (irony-completion--skip-whitespaces-backward) (or ;; after brackets consider it's a member access so things like ;; 'getFoo().|' match (memq (char-before) (list ?\) ?\] ?} ?>)) ;; identifiers but ignoring some keywords ;; ;; handle only a subset of template parameter packs, where the ;; ellipsis is preceded by a keyword, in situation like: ;; template class X {...}; ;; template ;; Keywords: c, convenience, tools ;; 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: ;; Interface libclang's "Diagnostic Reporting", see ;; http://clang.llvm.org/doxygen/group__CINDEX__DIAG.html ;;; Code: (require 'irony) (require 'irony-iotask) (eval-when-compile (require 'cl)) ;for lexical-let macro ;; ;; Irony Diagnostics Interface ;; (defun irony-diagnostics-file (diagnostic) (nth 0 diagnostic)) (defun irony-diagnostics-line (diagnostic) (nth 1 diagnostic)) (defun irony-diagnostics-column (diagnostic) (nth 2 diagnostic)) (defun irony-diagnostics-severity (diagnostic) (nth 4 diagnostic)) (defun irony-diagnostics-message (diagnostic) (nth 5 diagnostic)) (defun irony-diagnostics-async (callback) "Perform an asynchronous diagnostic request for the current buffer. CALLBACK is called with at least one argument, a symbol representing the status of the request. Depending on the status more argument are provided. Possible values are explained below: - success When quering the diagnostics work, the additional argument is a list of diagnostic object, diagnostics fields can be queried with the functions `irony-diagnostics-'. - error Retrieving the diagnostics wasn't possible. A string explaining the reason is passed as a second argument. - cancelled Retrieving the diagnostics was cancelled, e.g: because the buffer has changed since the beginning of the request, and as such the diagnostics are considered no longer relevant. A reason string is passed as a second argument." (lexical-let ((cb callback)) (irony--run-task-asynchronously (irony--diagnostics-task) (lambda (diagnostics-result) (condition-case err (funcall cb 'success (irony-iotask-result-get diagnostics-result)) (error (funcall cb 'error (error-message-string err)))))))) (provide 'irony-diagnostics) ;;; irony-diagnostics.el ends here irony-mode-1.2.0/irony-iotask.el000066400000000000000000000414761317254426700165750ustar00rootroot00000000000000;;; irony-iotask.el --- Abstraction for I/O-based tasks ;; Copyright (C) 2015 Guillaume Papin ;; Author: Guillaume Papin ;; Keywords: processes, convenience ;; 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: ;; Purpose is to work with both processes and network streams. ;; ;; Unlike tq.el we want tasks to be composed of 0, 1-1, or n-n communications. ;; tq.el only supports 1-1. ;; ;; 0 is useful if a server command has been cached, it means there is no need ;; for communication. We may only know if a task is still cached when the ;; callback is called (maybe after some other asynchronous tasks, so the context ;; may have changed since the task was initially posted) ;; ;; n-n is useful if the number of request depends on the answer to a previous ;; request but we still want to be able to push new tasks. ;;; Code: (eval-when-compile (require 'cl)) ;for lexical-let macro (require 'cl-lib) ;; ;; Error conditions ;; ;; `define-error' breaks backward compatibility with Emacs < 24.4 (defun irony-iotask--define-error (name message &optional parent) "Define NAME as a new error signal. MESSAGE is a string that will be output to the echo area if such an error is signaled without being caught by a `condition-case'. PARENT is either a signal or a list of signals from which it inherits. Defaults to `error'." (unless parent (setq parent 'error)) (let ((conditions (if (consp parent) (apply #'nconc (mapcar (lambda (parent) (cons parent (or (get parent 'error-conditions) (error "Unknown signal `%s'" parent)))) parent)) (cons parent (get parent 'error-conditions))))) (put name 'error-conditions (delete-dups (copy-sequence (cons name conditions)))) (when message (put name 'error-message message)))) (irony-iotask--define-error 'irony-iotask-error "I/O task error") (irony-iotask--define-error 'irony-iotask-filter-error "I/O task filter error") (irony-iotask--define-error 'irony-iotask-bad-task "Bad I/O task") (irony-iotask--define-error 'irony-iotask-bad-data "Bad I/O task data") ;; ;; Structures ;; (cl-defstruct (irony-iotask-result (:constructor irony-iotask-result-create)) -tag ;; 'value or 'error, or nil when unset -value -error -error-data ) (defun irony-iotask-result-valid-p (result) (and (irony-iotask-result--tag result) t)) (defun irony-iotask-result-value-p (result) (eq (irony-iotask-result--tag result) 'value)) (defun irony-iotask-result-error-p (result) (eq (irony-iotask-result--tag result) 'error)) (defun irony-iotask-result-set-value (result value) (setf (irony-iotask-result--tag result) 'value) (setf (irony-iotask-result--value result) value)) (defun irony-iotask-result-set-error (result error &rest error-data) (setf (irony-iotask-result--tag result) 'error) (setf (irony-iotask-result--error result) error) (setf (irony-iotask-result--error-data result) error-data)) (irony-iotask--define-error 'irony-iotask-result-get-error "Result not set before call to get") (defun irony-iotask-result-get (result) (cl-case (irony-iotask-result--tag result) ('value (irony-iotask-result--value result)) ('error (signal (irony-iotask-result--error result) (irony-iotask-result--error-data result))) (t (signal 'irony-iotask-result-get-error (list result))))) ;; FIXME: quoting issues? I cannot write "#'(lambda ()...)" as property value (defmacro irony-iotask-define-task (var docstring &rest properties) "A task is simply a property list. Each of these function are called in the buffer they were originally created (at schedule time). The functions `irony-iotask-put', `irony-iotask-get', `irony-iotask-set-result' and `irony-iotask-set-error' are available to the task's functions to set the task's result. Properties: `:start' (mandatory) Function to call to launch the task. Usually the function sends a string/command/message to the execution context. If the task do some caching it's possible that nothing is send, instead the execution context result should be set to indicate that the task is ready. The following additional functions are available to call inside the `:start' function to communicate with the underlying process: - `irony-iotask-send-string' - `irony-iotask-send-region' - `irony-iotask-send-eof' `:update' (mandatory) Function to call when some process output is available. The function should determine whether a message is complete and set the result when it is. It should also detect if the message is invalid and throw the 'invalid-msg tag with a value of t in this case. If the message is incomplete, the function should do nothing. The process output is the current buffer. `:finish' (optional) Function to call after the result has been set but before the callback is called. Usually performs some kind of cleanup operation. Note: it makes no sense to set a result or error in this function as it is necessarily been set beforehand. `:on-success' (optional) Same as `:finish' but called only if the result IS NOT an error. `:on-error' (optional) Same as `:finish' but called only if the result IS an error." (declare (indent 1) (doc-string 2)) `(progn (defvar ,var nil ,docstring) ;; Use `setq' to reset the var every time the macro is called. ;; This is useful, for example when evaluating using C-M-x (`eval-defun'). ;; Trick stolen from auto-complete's `ac-define-source' (setq ,var '(,@properties)))) (cl-defstruct (irony-iotask-packaged-task (:constructor irony-iotask-packaged-task--create)) task args result plist continuation) (defun irony-iotask-package-task (task &rest args) (irony-iotask-packaged-task--create :task task :result (irony-iotask-result-create) :args args)) (defvar irony-iotask--current-packaged-task) ;dynamically bound (defun irony-iotask-package-task-invoke (packaged-task prop-fn &optional ignore-missing &rest leading-args) (let* ((task (irony-iotask-packaged-task-task packaged-task)) (args (irony-iotask-packaged-task-args packaged-task)) (fn (plist-get task prop-fn))) (condition-case err (if fn ;; let binding for irony-iotask-{get,put} ;; and irony-iotask-set-{result,error} (let ((irony-iotask--current-packaged-task packaged-task)) (apply fn (append leading-args args))) (unless ignore-missing (signal 'irony-iotask-bad-task (list task (format "no %s function" prop-fn))))) (error (apply #'irony-iotask-result-set-error (irony-iotask-packaged-task-result packaged-task) (car err) (cdr err)))))) (defun irony-iotask--chain-1 (packaged-task-1 packaged-task-2) (while (irony-iotask-packaged-task-continuation packaged-task-1) (setq packaged-task-1 (irony-iotask-packaged-task-continuation packaged-task-1))) (setf (irony-iotask-packaged-task-continuation packaged-task-1) packaged-task-2)) (defun irony-iotask-chain (packaged-task-1 packaged-task-2 &rest others) (setq others (cons packaged-task-2 others)) (while others (irony-iotask--chain-1 packaged-task-1 (car others)) (setq others (cdr others))) packaged-task-1) (cl-defstruct (irony-iotask-ectx (:constructor irony-iotask-ectx--create)) started packaged-task callback schedule-buffer) (defun irony-iotask-ectx-call-callback (ectx result) (let ((cb-buffer (irony-iotask-ectx-schedule-buffer ectx))) (when (buffer-live-p cb-buffer) ;[GH-427] (with-demoted-errors "Irony I/O task: error in callback: %S" (with-current-buffer cb-buffer (funcall (irony-iotask-ectx-callback ectx) result)))))) (defvar irony-iotask--process) (defun irony-iotask--start-next (process) (let* ((ectx (car (process-get process :ectx-q))) (packaged-task (irony-iotask-ectx-packaged-task ectx))) (setf (irony-iotask-ectx-started ectx) t) ;; erase the buffer before we call :start so the next :update starts anew (erase-buffer) ;; for `irony-iotask-send-string', `irony-iotask-send-region' and ;; `irony-iotask-send-eof' (let ((irony-iotask--process process)) (save-current-buffer (irony-iotask-package-task-invoke packaged-task :start))) (irony-iotask--check-result process))) (defun irony-iotask--start-next-safe (process) "Run the next task, if any." (let ((ectx-q (process-get process :ectx-q))) (when (and ectx-q (not (irony-iotask-ectx-started (car ectx-q)))) (irony-iotask--start-next process)))) (defun irony-iotask--check-result (process) (let* ((ectx (car (process-get process :ectx-q))) (packaged-task (irony-iotask-ectx-packaged-task ectx)) (result (irony-iotask-packaged-task-result packaged-task))) (when (irony-iotask-result-valid-p result) (save-current-buffer (if (irony-iotask-result-value-p result) (irony-iotask-package-task-invoke packaged-task :on-success t) (irony-iotask-package-task-invoke packaged-task :on-error t))) (save-current-buffer (irony-iotask-package-task-invoke packaged-task :finish t)) (if (and (irony-iotask-packaged-task-continuation packaged-task) (irony-iotask-result-value-p result)) ;; we got a non-error, we can chain to the next continuation (progn (setf (irony-iotask-ectx-packaged-task ectx) (irony-iotask-packaged-task-continuation packaged-task)) (irony-iotask--start-next process)) ;; no continuation or an error, call the callback ;; and skip any potential continuation (setq ectx (pop (process-get process :ectx-q))) (irony-iotask-ectx-call-callback ectx result) (irony-iotask--start-next-safe process))))) (irony-iotask--define-error 'irony-iotask-aborted "I/O task aborted") (defun irony-iotask--abort-all (process &rest reasons) (let ((result (irony-iotask-result-create)) ectx) (apply #'irony-iotask-result-set-error result 'irony-iotask-aborted reasons) (while (setq ectx (pop (process-get process :ectx-q))) (irony-iotask-ectx-call-callback ectx result)))) ;; ;; Implementation details, internal mechanic ;; (defun irony-iotask-process-filter (process output) (when (buffer-live-p (process-buffer process)) (with-current-buffer (process-buffer process) (let ((ectx (car (process-get process :ectx-q))) packaged-task result) ;; if no task this is an error, a spurious message is an error (unless ectx (signal 'irony-iotask-filter-error (list "spurious output" output))) (setq packaged-task (irony-iotask-ectx-packaged-task ectx)) (setq result (irony-iotask-packaged-task-result packaged-task)) (goto-char (process-mark process)) (insert output) (set-marker (process-mark process) (point)) ;; go to the first char, so the `:update' function is called at the ;; beginning of the buffer, it is more convenient (goto-char (point-min)) (when (catch 'invalid-msg (save-current-buffer (irony-iotask-package-task-invoke packaged-task :update)) nil) (irony-iotask-result-set-error result 'irony-iotask-bad-data packaged-task (buffer-string))) (irony-iotask--check-result process))))) (defun irony-iotask-process-sentinel (process event) ;; events usually ends with a newline, we don't want it (setq event (replace-regexp-in-string "\n\\'" "" event)) (unless (process-live-p process) (irony-iotask--abort-all process "process stopped running" event) (message "%s process stopped!" (process-name process)))) (defun irony-iotask-check-process (process) (let ((pfilter (process-filter process)) (psentinel (process-sentinel process))) (unless (eq pfilter 'irony-iotask-process-filter) (signal 'irony-iotask-error (list "invalid process filter" pfilter "did you call `irony-iotask-setup-process'?"))) (unless (eq psentinel 'irony-iotask-process-sentinel) (signal 'irony-iotask-error (list "invalid process sentinel" psentinel "did you call `irony-iotask-setup-process'?")))) (unless (process-live-p process) (signal 'irony-iotask-error (list "Process ain't running!"))) (unless (buffer-live-p (process-buffer process)) (signal 'irony-iotask-error (list "Process' buffer dead!")))) ;; ;; Public API ;; (defun irony-iotask-setup-process (process) "Call after creating the asynchronous process to let irony-iotask setup the PROCESS filter and anything else that may be needed." (with-current-buffer (process-buffer process) (set-process-filter process #'irony-iotask-process-filter) (set-process-sentinel process #'irony-iotask-process-sentinel) (buffer-disable-undo))) (defun irony-iotask-schedule (process packaged-task callback) (let ((ectx (irony-iotask-ectx--create :packaged-task packaged-task :callback callback :schedule-buffer (current-buffer)))) (irony-iotask-check-process process) (with-current-buffer (process-buffer process) ;; append the new task to the queue (process-put process :ectx-q (append (process-get process :ectx-q) (list ectx))) ;; run task if none were running (unless (cdr (process-get process :ectx-q)) (irony-iotask--start-next process))))) (defun irony-iotask-run (process packaged-task) "Blocking/waiting counterpart of `irony-iotask-schedule'. Return the result (or signal the stored error) instead of passing it to a callback. Returns nil when quitting. This function isn't reentrant, do not call it from another task." (lexical-let (run-result) ;; schedule an asynchronous task that set result when done (irony-iotask-schedule process packaged-task (lambda (result) (setq run-result result))) ;; wait for the task to complete ;; quitting is allowed, in this case the task will still run but ;; asynchronously, it won't block the user interface but the result will be ;; lost (if (with-local-quit (while (not run-result) (accept-process-output process 0.05)) t) ;; didn't quit, task was completed (irony-iotask-result-get run-result) ;; C-g was used ;; TODO: We cannot abort the task because that may break the other tasks, ;; the process will be in an unpredictable state. Howewer we can cancel ;; the continuations. ))) (defun irony-iotask-get (propname) (plist-get (irony-iotask-packaged-task-plist irony-iotask--current-packaged-task) propname)) (defun irony-iotask-put (propname value) (setf (irony-iotask-packaged-task-plist irony-iotask--current-packaged-task) (plist-put (irony-iotask-packaged-task-plist irony-iotask--current-packaged-task) propname value))) (defun irony-iotask--result () (irony-iotask-packaged-task-result irony-iotask--current-packaged-task)) (defun irony-iotask-set-result (value) (irony-iotask-result-set-value (irony-iotask--result) value)) (defun irony-iotask-set-error (err &rest error-data) (apply #'irony-iotask-result-set-error (irony-iotask--result) err error-data)) (defun irony-iotask-send-string (string) (process-send-string irony-iotask--process string)) (defun irony-iotask-send-region (start end) (process-send-region irony-iotask--process start end)) (defun irony-iotask-send-eof (string) (process-send-eof irony-iotask--process)) (provide 'irony-iotask) ;;; irony-iotask.el ends here irony-mode-1.2.0/irony-server.rst000066400000000000000000000040211317254426700170020ustar00rootroot00000000000000============ irony-server ============ ---------------------------------------- The back-end server for Emacs irony-mode ---------------------------------------- :Author: Nicholas D Steeves :Date: 2017-10-01 :Version: 1.2.0 :Manual section: 1 Synopsis ======== irony-server [*options*] [*command*] [<*args*>] Description =========== ``irony-server`` provides the libclang interface to irony-mode. It uses a simple protocol based on S-expression. This server is written in C++ and requires the following packages to be installed on your system: CMake >= 2.8.3, and libclang. Options ======= -v, --version Show version and exit. -h, --help Show more detailed command help. -i, --interactive Run an interactive command loop. It accepts only the ``Commands`` listed below. This is primarily a machine interface that the user interacts with via irony.el. -d, --debug Write debug info to log file. --log-file PATH Write logs to path instead of standard error. Commands ======== **candidates** PREFIX STYLE Print completion candidates (require previous complete). STYLE is "exact", "case-insensitive" or "smart-case". **complete** FILE LINE COL [-- [COMPILE_OPTIONS...]] Perform code completion at a given location. **completion-diagnostics** Print the diagnostics generated during complete. **diagnostics** Print the diagnostics of the last parse. **exit** Exit interactive mode, print nothing. **get-compile-options** BUILD_DIR FILE Get compile options for FILE from JSON database in PROJECT_ROOT. **get-type** LINE COL Get type of symbol at a given location. **help** Show this message. **parse** FILE [-- [COMPILE_OPTIONS...]] Parse the given file. **reset-unsaved** FILE Reset FILE, its content is up to date. **set-debug** [on|off] Enable or disable verbose logging. **set-unsaved** FILE UNSAVED-CONTENT-FILE Tell irony-server that UNSAVED-CONTENT-FILE contains the effective content of FILE. irony-mode-1.2.0/irony-snippet.el000066400000000000000000000113471317254426700167570ustar00rootroot00000000000000;;; irony-snippet.el --- Snippet support for Irony-Mode ;; Copyright (C) 2013-2014 Guillaume Papin ;; Author: Guillaume Papin ;; Keywords: c, convenience ;; 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: ;; Snippet support functions, support all version of YASnippet and ;; maybe further frameworks later. ;; ;; It is possible to check snippet support by calling ;; `irony-snippet-available-p'. ;; ;; Note: Some YASnippet versions require to have ;; `yas/minor-mode' or `yas-minor-mode' enabled to work. ;;; Code: ;; Private variables ;; (defvar irony-snippet--expand-function nil "Function to expand a snippet at a given point (by default to the current position). e.g: (defun my-expand-snippet (snippet-str &optional pos) (do-stuff (or pos (point))) Will be set to nil if no snippet expansion function is found.") ;; ;; "Public" functions ;; (defun irony-snippet-available-p () "Return t if snippets are supported." (and (irony-snippet--get-expand-function) (not (irony-snippet--yas-disabled-p)))) (defun irony-snippet-expand (snippet-str &optional pos) "Expand SNIPPET-STR starting at POS. If `irony-snippet-available-p' return t then" (let ((expand-func (irony-snippet--get-expand-function))) (funcall expand-func snippet-str pos))) ;; Private functions ;; (defun irony-snippet--get-expand-function () (unless irony-snippet--expand-function (irony-snippet--init-yas)) irony-snippet--expand-function) (defun irony-snippet--init-yas () ;; find the snippet expand function (when (require 'yasnippet nil t) (let ((yas-version (or (and (boundp 'yas--version) yas--version) (and (boundp 'yas/version) yas/version)))) ;for old versions (when (stringp yas-version) (setq yas-version (replace-regexp-in-string "(\\|)" "" yas-version)) (setq irony-snippet--expand-function ;; (string= ... "0.6.0c"), the 'c' suffix is not supported by ;; `version-to-list' in emacs versions < 24, treat this one ;; specifically. (cond ((or (string= yas-version "0.6.0c") (version<= yas-version "0.6.0b")) 'irony-snippet--expand-yas-1) ;; `version<' thinks "0.8beta" < "0.8", we want to consider ;; anything starting with "0.8" as "0.8" and more. ((and (version< yas-version "0.8") (not (string-prefix-p "0.8" yas-version))) 'irony-snippet--expand-yas-2) (t 'irony-snippet--expand-yas-3))))))) (defun irony-snippet--yas-disabled-p () "If the current yasnippet version offers a minor-mode, check if this mode is disable by returning t, otherwise returns nil and it's partially safe to assume that yasnippet expansion can be used." ;; XXX: work only when yasnippet is enabled, otherwise some ;; variables used for the snippet expansion are not set and it ;; causes some errors. (if (boundp 'yas-minor-mode) (not yas-minor-mode) (if (boundp 'yas/minor-mode) (not yas/minor-mode)))) (defun irony-snippet--expand-yas-1 (snippet-str &optional pos) "Expand snippets for YASnippet version <= 0.6.0c." (declare-function yas/expand-snippet "ext:yasnippet" t nil) (unless (irony-snippet--yas-disabled-p) (yas/expand-snippet (or pos (point)) (or pos (point)) snippet-str))) (defun irony-snippet--expand-yas-2 (snippet-str &optional pos) "Expand snippets for YASnippet version < 0.8. See also `irony-snippet--expand-yas-1'." (declare-function yas/expand-snippet "ext:yasnippet" t nil) (unless (irony-snippet--yas-disabled-p) (when pos (goto-char pos)) (yas/expand-snippet snippet-str))) (defun irony-snippet--expand-yas-3 (snippet-str &optional pos) "Expand snippets for YASnippet version >= 0.8. See also `irony-snippet--expand-yas-2'." (declare-function yas-expand-snippet "ext:yasnippet" t nil) (unless (irony-snippet--yas-disabled-p) (when pos (goto-char pos)) (yas-expand-snippet snippet-str))) (provide 'irony-snippet) ;;; irony-snippet.el ends here irony-mode-1.2.0/irony.el000066400000000000000000000774531317254426700153110ustar00rootroot00000000000000;;; irony.el --- C/C++ minor mode powered by libclang ;; Copyright (C) 2011-2016 Guillaume Papin ;; Author: Guillaume Papin ;; Version: 1.2.0 ;; URL: https://github.com/Sarcasm/irony-mode ;; Compatibility: GNU Emacs 24.x ;; Keywords: c, convenience, tools ;; Package-Requires: ((cl-lib "0.5") (json "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: ;; ;; This file provides `irony-mode', a minor mode for C, C++ and Objective-C. ;; ;; Usage: ;; (add-hook 'c++-mode-hook 'irony-mode) ;; (add-hook 'c-mode-hook 'irony-mode) ;; (add-hook 'objc-mode-hook 'irony-mode) ;; ;; ;; Windows performance tweaks ;; ;; ;; (when (boundp 'w32-pipe-read-delay) ;; (setq w32-pipe-read-delay 0)) ;; ;; Set the buffer size to 64K on Windows (from the original 4K) ;; (when (boundp 'w32-pipe-buffer-size) ;; (setq irony-server-w32-pipe-buffer-size (* 64 1024))) ;; ;; See also: ;; - https://github.com/Sarcasm/company-irony ;; - https://github.com/Sarcasm/flycheck-irony ;; - https://github.com/Sarcasm/ac-irony ;;; Code: (require 'irony-iotask) (autoload 'irony-completion--enter "irony-completion") (autoload 'irony-completion--exit "irony-completion") (require 'cl-lib) (autoload 'find-library-name "find-func") (autoload 'lm-version "lisp-mnt") ;; ;; Compatibility ;; (eval-and-compile ;; As seen in flycheck/magit ;; ;; Added in Emacs 24.3 (mirrors/emacs@b335efc3). (unless (fboundp 'setq-local) (defmacro setq-local (var val) "Set variable VAR to value VAL in current buffer." (list 'set (list 'make-local-variable (list 'quote var)) val))) ;; Added in Emacs 24.3 (mirrors/emacs@b335efc3). (unless (fboundp 'defvar-local) (defmacro defvar-local (var val &optional docstring) "Define VAR as a buffer-local variable with default value VAL. Like `defvar' but additionally marks the variable as being automatically buffer-local wherever it is set." (declare (debug defvar) (doc-string 3)) (list 'progn (list 'defvar var val docstring) (list 'make-variable-buffer-local (list 'quote var))))) ) ;; eval-and-compile ;; ;; Customizable variables ;; (defgroup irony nil "C/C++ minor mode powered by libclang." :group 'c) (defcustom irony-lighter " Irony" "Text to display in the mode line when irony mode is on." :type 'string :group 'irony) (defcustom irony-user-dir (locate-user-emacs-file "irony/") "Directory containing the Irony generated files. The slash is expected at the end." :type 'directory :risky t :group 'irony) (defcustom irony-supported-major-modes '(c++-mode c-mode objc-mode) "List of modes known to be compatible with Irony." :type '(repeat symbol) :group 'irony) ;;;###autoload (defcustom irony-additional-clang-options nil "Additional command line options to pass down to libclang. Please, do NOT use this variable to add header search paths, only additional warnings or compiler options. These compiler options will be prepended to the command line, in order to not override the value coming from a compilation database." :type '(repeat string) :options '("-Wdocumentation") :group 'irony) (defcustom irony-lang-compile-option-alist '((c++-mode . "c++") (c-mode . "c") (objc-mode . "objective-c")) "Alist to decide the language option to used based on the `major-mode'." :type '(alist :key-type symbol :value-type string) :group 'irony) (defcustom irony-cmake-executable "cmake" "Name or path of the CMake executable." :type 'string :group 'irony) (defcustom irony-server-source-dir nil "Points to the irony-server source directory. This should point to the directory that contains the top-most CMakeLists.txt used to build the server. By default it will find the directory based on the irony.el directory." :type 'directory :group 'irony :package-version '(irony . "1.2.0")) (defcustom irony-server-build-dir nil "Build directory for irony-server. If set to nil the default is to create a build directory in `temporary-file-directory'/build-irony-server-`(irony-version)'." :type 'directory :group 'irony) (defcustom irony-server-install-prefix irony-user-dir "Installation prefix used to install irony-server. The irony-server executable is expected to be in `irony-server-install-prefix'/bin/." :type 'directory :group 'irony) (defcustom irony-server-w32-pipe-buffer-size nil "Windows-only setting, the buffer size to use for the irony-server process pipe on Windows. Larger values can improve performances on large buffers. If non-nil, `w32-pipe-buffer-size' will be let-bound to this value during the creation of the irony-server process.") ;; ;; Public/API variables ;; ;; Non-customizable variables provided by Irony that can be useful to other ;; packages. ;; ;; Note that they shouldn't be modified directly by external packages, just ;; read. ;; ;; TODO: make this variable public when the CDB API stabilizes. (defvar-local irony--compile-options nil "Compile options for the current file. The compile options used by the compiler to build the current buffer file.") ;; TODO: make this variable public when the CDB API stabilizes. (defvar-local irony--working-directory nil "The working directory to pass to libclang, if any.") ;; ;; Internal variables ;; ;; The prefix `irony--' is used when something can completely change (or ;; disappear) from one release to the other. ;; ;; -- https://lists.gnu.org/archive/html/emacs-devel/2013-06/msg01129.html (defconst irony--eot "\n;;EOT\n" "String sent by the server to signal the end of a response.") ;; ;; Error conditions ;; ;; `define-error' breaks backward compatibility with Emacs < 24.4 (defun irony--define-error (name message &optional parent) "Define NAME as a new error signal. MESSAGE is a string that will be output to the echo area if such an error is signaled without being caught by a `condition-case'. PARENT is either a signal or a list of signals from which it inherits. Defaults to `error'." (unless parent (setq parent 'error)) (let ((conditions (if (consp parent) (apply #'nconc (mapcar (lambda (parent) (cons parent (or (get parent 'error-conditions) (error "Unknown signal `%s'" parent)))) parent)) (cons parent (get parent 'error-conditions))))) (put name 'error-conditions (delete-dups (copy-sequence (cons name conditions)))) (when message (put name 'error-message message)))) (irony--define-error 'irony-error "Irony-Mode error") (irony--define-error 'irony-parse-error "Irony-Mode parsing error" 'irony-error) (irony--define-error 'irony-server-error "Irony-Mode server error" 'irony-error) ;; ;; Utility functions & macros ;; ;; TODO: remove and use `if-let' when supported version jumps to Emacs 25.1 (defmacro irony--aif (test if-expr &rest else-body) (declare (indent 2)) `(let ((it ,test)) (if it ,if-expr (progn ,@else-body)))) ;; TODO: remove and use `when-let' when supported version jumps to Emacs 25.1 (defmacro irony--awhen (test &rest body) (declare (indent 1)) `(let ((it ,test)) (when it (progn ,@body)))) (defun irony--assoc-all (key list) (delq nil (mapcar (lambda (c) (when (equal (car c) key) c)) list))) (defmacro irony--without-narrowing (&rest body) "Remove the effect of narrowing for the current buffer. Note: If `save-excursion' is needed for BODY, it should be used before calling this macro." (declare (indent 0) (debug t)) `(save-restriction (widen) (progn ,@body))) (defun irony--buffer-size-in-bytes () "Return the buffer size, in bytes." (1- (position-bytes (point-max)))) (defun irony--read-char-choice (prompt chars) "Wrapper around `read-char-choice', available since Emacs 24." (setq prompt (concat prompt " [" chars "]: ")) (if (fboundp 'read-char-choice) (read-char-choice prompt chars) (setq prompt (propertize prompt 'face 'minibuffer-prompt)) (let ((cursor-in-echo-area t) k) (while (not (member k chars)) (setq k (read-char-exclusive prompt))) k))) (defun irony--shorten-path (path) "Make PATH as short as possible. The given path can be considered understandable by human but not necessary a valid path string to use in code. Its only purpose is to be displayed to the user." (let ((relative (file-relative-name path)) (abbreviated (abbreviate-file-name path))) (if (< (string-width relative) (string-width abbreviated)) relative abbreviated))) (defun irony--split-command-line-1 (quoted-str) "Remove the escaped quotes and backlash from a QUOTED-STR. Return a list of the final characters in the reverse order. Only to be consumed by `irony--split-command-line'." (let ((len (length quoted-str)) (i 0) ch next-ch result) (while (< i len) (setq ch (aref quoted-str i)) (when (eq ch ?\\) (let ((next-ch (and (< (1+ i) len) (aref quoted-str (1+ i))))) (when (member next-ch '(?\\ ?\")) (setq ch next-ch) (cl-incf i)))) (push ch result) (cl-incf i)) result)) ;; TODO: rewrite the function correctly to handle things like the following: ;; ;; "/usr/bin/clang++ -Irelative -DSOMEDEF=\"With spaces, quotes and \\-es.\" " (defun irony--split-command-line (cmd-line) "Split CMD-LINE into a list of arguments. Takes care of double quotes as well as backslash. Sadly I had to write this because `split-string-and-unquote' breaks with escaped quotes in compile_commands.json, such as in: /usr/bin/c++ -DLLVM_VERSION_INFO=\\\\\\\"3.2svn\\\\\\\" " ;; everytime I write a function like this one, it makes me feel bad (let* ((len (length cmd-line)) (spaces (string-to-list " \f\t\n\r\v")) (first-not-spaces-re (concat "[^" spaces "]")) (i 0) ch args cur-arg) (while (< i len) (setq ch (aref cmd-line i)) (cond ((member ch spaces) ;spaces (when cur-arg (setq args (cons (apply 'string (nreverse cur-arg)) args) cur-arg nil)) ;; move to the next char (setq i (or (string-match-p first-not-spaces-re cmd-line i) len))) ((eq ch ?\") ;quoted string (let ((endq (string-match-p "[^\\]\"" cmd-line i))) (unless endq (signal 'irony-parse-error (list "ill formed command line" cmd-line))) (let ((quoted-str (substring cmd-line (1+ i) (1+ endq)))) (setq cur-arg (append (irony--split-command-line-1 quoted-str) cur-arg) i (+ endq 2))))) (t ;a valid char ;; if it's an escape of: a backslash, a quote or a space push ;; only the following char. (when (eq ch ?\\) (let ((next-ch (and (< (1+ i) len) (aref cmd-line (1+ i))))) (when (or (member next-ch '(?\\ ?\")) (member next-ch spaces)) (setq ch next-ch) (cl-incf i)))) (push ch cur-arg) (cl-incf i)))) (when cur-arg (setq args (cons (apply 'string (nreverse cur-arg)) args))) (nreverse args))) (defun irony--get-buffer-path-for-server (&optional buffer) "Get the path of the current buffer to send to irony-server. If no such file exists on the filesystem the special file '-' is returned instead." (let ((file (buffer-file-name buffer))) (if (and file (file-exists-p file)) file "-"))) ;; ;; Mode ;; (defvar irony-mode-map (make-sparse-keymap) "Keymap used in `irony-mode' buffers.") ;;;###autoload (define-minor-mode irony-mode "Minor mode for C, C++ and Objective-C, powered by libclang." nil irony-lighter irony-mode-map :group 'irony (if irony-mode (irony--mode-enter) (irony--mode-exit))) (defun irony--mode-enter () ;; warn the user about modes such as php-mode who inherits c-mode (when (not (memq major-mode irony-supported-major-modes)) (display-warning 'irony "Major mode is unknown to Irony,\ see `irony-supported-major-modes'.")) ;; warn the user about Windows-specific issues (when (eq system-type 'windows-nt) (cond ((version< emacs-version "24.4") (display-warning 'irony "Emacs >= 24.4 expected on Windows.")) ((and (boundp 'w32-pipe-read-delay) (> w32-pipe-read-delay 0)) (display-warning 'irony "Performance will be bad because a\ pipe delay is set for this platform (see variable\ `w32-pipe-read-delay').")))) (irony-completion--enter)) (defun irony--mode-exit () (irony-completion--exit)) ;;;###autoload (defun irony-version (&optional show-version) "Return the version number of the file irony.el. If called interactively display the version in the echo area." (interactive (list t)) ;; Shamelessly stolen from `company-mode'. (with-temp-buffer (insert-file-contents (find-library-name "irony")) (let ((v (lm-version))) (when show-version (message "irony version: %s" v)) v))) ;; ;; Compile options handling ;; (defun irony--lang-compile-option () (irony--awhen (cdr-safe (assq major-mode irony-lang-compile-option-alist)) (list "-x" it))) (defun irony--extract-working-directory-option (flags) "Return working directory specified on the command line, if any." (catch 'found (while flags (let ((flag (car flags))) (cond ((string= "-working-directory" flag) (throw 'found (cadr flags))) ((string-prefix-p "-working-directory=" flag) (throw 'found (substring flag (length "-working-directory=")))) (t (setq flags (cdr flags)))))))) (defun irony--adjust-compile-options () "The compile options to send to libclang." ;; TODO: if current buffer has no associated file (will be sent as '-') but is ;; in an existing directory, we will want to add -I (directory-file-name ;; buffer-file-name) to find the relative headers (append (irony--lang-compile-option) (irony--awhen irony--working-directory (unless (irony--extract-working-directory-option irony--compile-options) (list "-working-directory" it))) irony-additional-clang-options irony--compile-options)) (defun irony--extract-user-search-paths (compile-options work-dir) "Retrieve the user search paths present in COMPILE-OPTIONS. Relative paths are expanded to be relative to WORK-DIR. The returned paths are returned as directory (`file-name-as-directory'). Note: WORK-DIR is not used when the compile option '-working-directory=' is detected in COMPILE-OPTIONS." (setq work-dir (or (irony--extract-working-directory-option compile-options) work-dir)) (let (include-dirs opt) (while (setq opt (car compile-options)) (cond ((string= "-I" opt) (add-to-list 'include-dirs (nth 1 compile-options) t) (setq compile-options (cddr compile-options))) ((string-prefix-p "-I" opt) (add-to-list 'include-dirs (substring opt 2) t) (setq compile-options (cdr compile-options))) (t (setq compile-options (cdr compile-options))))) (delete-dups (mapcar #'(lambda (path) (file-name-as-directory (expand-file-name path work-dir))) include-dirs)))) ;; ;; Irony-Server setup ;; (defvar irony--server-install-command-history nil) (defun irony--install-server-read-command (command) (read-shell-command "Install command: " command (if (equal (car irony--server-install-command-history) command) '(irony--server-install-command-history . 1) 'irony--server-install-command-history))) (defun irony-install-server (command) "Install or reinstall the Irony server. The installation requires CMake and the libclang developpement package." (interactive (list (let ((command (format (concat "%s %s %s && %s --build . " "--use-stderr --config Release --target install") (shell-quote-argument irony-cmake-executable) (shell-quote-argument (concat "-DCMAKE_INSTALL_PREFIX=" (expand-file-name irony-server-install-prefix))) (shell-quote-argument (or irony-server-source-dir (expand-file-name "server" (file-name-directory (find-library-name "irony"))))) (shell-quote-argument irony-cmake-executable)))) (irony--install-server-read-command command)))) (let ((build-dir (or irony-server-build-dir (concat (file-name-as-directory temporary-file-directory) (file-name-as-directory (format "build-irony-server-%s" (irony-version))))))) (make-directory build-dir t) (let ((default-directory build-dir)) ;; we need to kill the process to be able to install a new one, ;; at least on Windows (irony-server-kill) (with-current-buffer (compilation-start command nil #'(lambda (maj-mode) "*irony-server build*")) (setq-local compilation-finish-functions '(irony--server-install-finish-function)))))) (defun irony--server-install-finish-function (buffer msg) (if (string= "finished\n" msg) (message "irony-server installed successfully!") (message "Failed to build irony-server, you are on your own buddy!"))) (defun irony--find-server-executable () "Return the path to the irony-server executable. Throw an `irony-server-error' if a proper executable cannot be found." (let* ((exec-path (cons (expand-file-name "bin" irony-server-install-prefix) exec-path)) (exe (executable-find "irony-server"))) (condition-case err (let ((version (car (process-lines exe "--version")))) (if (and (string-match "^irony-server version " version) (version= (irony-version) (substring version (length "irony-server version ")))) ;; irony-server is working and up-to-date! exe (signal 'irony-server-error (list (format "irony-server version mismatch: %s" (substitute-command-keys "type `\\[irony-install-server]' to reinstall")))))) (irony-server-error (signal (car err) (cdr err))) (error (signal 'irony-server-error (if (and exe (file-executable-p exe)) ;; failed to execute due to a runtime problem, i.e: ;; libclang.so isn't in the ld paths (list (format "irony-server is broken! %s" (error-message-string err))) ;; irony-server doesn't exists, first time irony-mode is used? ;; inform the user about how to build the executable (list (format "irony-server not installed! %s" (substitute-command-keys "Type `\\[irony-install-server]' to install"))))))))) ;; ;; irony-server process management. ;; (defvar irony--server-executable nil) (defvar irony--server-process nil) (defvar irony--server-buffer " *Irony*" "The name of the buffer for the irony process to run in. When using a leading space, the buffer is hidden from the buffer list (and undo information is not kept).") (defun irony--start-server-process () (unless irony--server-executable ;; if not found, an `irony-server-error' error is signaled (setq irony--server-executable (irony--find-server-executable))) (let ((process-connection-type nil) (process-adaptive-read-buffering nil) (w32-pipe-buffer-size (when (boundp 'w32-pipe-buffer-size) (or irony-server-w32-pipe-buffer-size w32-pipe-buffer-size))) process) (setq process (start-process-shell-command "Irony" ;process name irony--server-buffer ;buffer (format "%s -i 2> %s" ;command (shell-quote-argument irony--server-executable) (expand-file-name (format-time-string "irony.%Y-%m-%d_%Hh-%Mm-%Ss.log") temporary-file-directory)))) (set-process-query-on-exit-flag process nil) (irony-iotask-setup-process process) process)) ;;;###autoload (defun irony-server-kill () "Kill the running irony-server process, if any." (interactive) (when (process-live-p irony--server-process) (kill-process irony--server-process) (setq irony--server-process nil))) (defun irony--get-server-process-create () (unless (process-live-p irony--server-process) (setq irony--server-process (irony--start-server-process))) irony--server-process) (defun irony--run-task (task) (irony-iotask-run (irony--get-server-process-create) task)) (defun irony--run-task-asynchronously (task callback) (irony-iotask-schedule (irony--get-server-process-create) task callback)) (defun irony--quote-strings (strings &optional separator) "Like `combine-and-quote-strings', but when the string is \"\" or nil, `irony--quote-strings' will convert it to \"\" instead of . That is: (irony--quote-strings \'(\"a\" \"\" \"b\")) => \"a \\\"\\\" b\" (combine-and-quote-strings \'(\"a\" \"\" \"b\")) => \"a b\" " (let* ((sep (or separator " ")) (re (concat "[\\\"]" "\\|" (regexp-quote sep)))) (mapconcat (lambda (str) (cond ((or (not str) (string= str "")) "\"\"") ((string-match re str) (concat "\"" (replace-regexp-in-string "[\\\"]" "\\\\\\&" str) "\"")) (t str))) strings sep))) (defun irony--server-send-command (command &rest args) (let ((command-line (concat (irony--quote-strings (mapcar (lambda (arg) (if (numberp arg) (number-to-string arg) arg)) (cons command args))) "\n"))) (irony-iotask-send-string command-line))) ;; XXX: this code can run in very tight very sensitive on big inputs, ;; every change should be measured (defun irony--server-command-update (&rest _args) (when (and (>= (buffer-size) (length irony--eot)) (string-equal (buffer-substring-no-properties (- (point-max) (length irony--eot)) (point-max)) irony--eot)) (condition-case-unless-debug nil (let ((result (read (current-buffer)))) (cl-case (car result) (success (irony-iotask-set-result (cdr result))) (error (apply #'irony-iotask-set-error 'irony-server-error (cdr result))))) (error (throw 'invalid-msg t))))) ;; FIXME: code duplication with `irony--server-command-update' ;; XXX: this code can run in very tight very sensitive on big inputs, ;; every change should be measured (defun irony--server-query-update (&rest _args) (when (and (>= (buffer-size) (length irony--eot)) (string-equal (buffer-substring-no-properties (- (point-max) (length irony--eot)) (point-max)) irony--eot)) (condition-case-unless-debug nil (irony-iotask-set-result (read (current-buffer))) (error (throw 'invalid-msg t))))) ;; ;; Server commands ;; (irony-iotask-define-task irony--t-get-compile-options "`get-compile-options' server command." :start (lambda (build-dir file) (irony--server-send-command "get-compile-options" build-dir file)) :update irony--server-command-update) (defun irony--get-compile-options-task (build-dir file) (irony-iotask-package-task irony--t-get-compile-options build-dir file)) (cl-defstruct (irony--buffer-state (:constructor irony--buffer-state-create-1)) file exists modified tick) (defun irony--buffer-state-create (buffer) (let ((file (buffer-file-name buffer))) (irony--buffer-state-create-1 :file file :exists (and file (file-exists-p file)) :modified (buffer-modified-p buffer) :tick (buffer-chars-modified-tick buffer)))) (defun irony--buffer-state-compare (old new) (unless (equal old new) (cond ((irony--buffer-state-modified new) 'set-unsaved) ((null old) nil) ;noop ((irony--buffer-state-modified old) 'reset-unsaved)))) (irony-iotask-define-task irony--t-set-unsaved "`set-unsaved' server command." :start (lambda (process buffer buf-state) (let ((elem (assq buffer (process-get process :unsaved-buffers))) temp-file) (if (eq (cdr elem) buf-state) ;; early exit if already cached (irony-iotask-set-result t) (setq temp-file (make-temp-file "irony-unsaved-")) (irony-iotask-put :temp-file temp-file) (irony-iotask-put :buffer-state buf-state) (process-put process :buffer-state buf-state) (with-current-buffer buffer (irony--without-narrowing (let ((write-region-inhibit-fsync t)) (write-region nil nil temp-file nil 0))) (irony--server-send-command "set-unsaved" (irony--get-buffer-path-for-server) temp-file))))) :update irony--server-command-update :finish (lambda (&rest _args) (delete-file (irony-iotask-get :temp-file))) :on-success (lambda (process buffer &rest _args) (let* ((unsaved-buffers (process-get process :unsaved-buffers)) (elem (assq buffer unsaved-buffers)) (buf-state (irony-iotask-get :buffer-state))) (if elem (setcdr elem buf-state) (process-put process :unsaved-buffers (cons (cons buffer buf-state) unsaved-buffers)))))) (defun irony--set-unsaved-task (process buffer buf-state) (irony-iotask-package-task irony--t-set-unsaved process buffer buf-state)) (irony-iotask-define-task irony--t-reset-unsaved "`reset-unsaved' server command." :start (lambda (process buffer) (if (assq buffer (process-get process :unsaved-buffers)) (irony--server-send-command "reset-unsaved" (irony--get-buffer-path-for-server buffer)) ;; exit early if already reset (irony-iotask-set-result t))) :update irony--server-command-update :finish (lambda (process buffer) (process-put process :unsaved-buffers (assq-delete-all buffer (process-get process :unsaved-buffers))))) (defun irony--reset-unsaved-task (process buffer) (irony-iotask-package-task irony--t-reset-unsaved process buffer)) (defun irony--list-unsaved-irony-mode-buffers (&optional ignore-list) (delq nil (mapcar (lambda (buf) (unless (memq buf ignore-list) (when (buffer-modified-p buf) (with-current-buffer buf (and irony-mode buf))))) (buffer-list)))) (defun irony--get-buffer-change-alist (process) "Return a list of (buffer . old-state). old-state can be nil if the old state isn't known." (let ((unsaved-list (process-get process :unsaved-buffers))) (append unsaved-list (mapcar (lambda (buf) (cons buf nil)) (irony--list-unsaved-irony-mode-buffers (mapcar #'car unsaved-list)))))) (defun irony--unsaved-buffers-tasks () (let ((process (irony--get-server-process-create)) result) (dolist (buffer-old-state-cons (irony--get-buffer-change-alist process) result) (let* ((buffer (car buffer-old-state-cons)) (old-state (cdr buffer-old-state-cons)) (new-state (irony--buffer-state-create buffer)) (task (cl-case (irony--buffer-state-compare old-state new-state) (set-unsaved (irony--set-unsaved-task process buffer new-state)) (reset-unsaved (irony--reset-unsaved-task process buffer))))) (when task (setq result (if result (irony-iotask-chain result task) task))))))) (irony-iotask-define-task irony--t-parse "`parse' server command." :start (lambda (file compile-options) (apply #'irony--server-send-command "parse" file "--" compile-options)) :update irony--server-command-update) (defun irony--parse-task-1 (&optional buffer) (with-current-buffer (or buffer (current-buffer)) (irony-iotask-package-task irony--t-parse (irony--get-buffer-path-for-server) (irony--adjust-compile-options)))) (defun irony--parse-task (&optional buffer) (let ((unsaved-tasks (irony--unsaved-buffers-tasks)) (parse-task (irony--parse-task-1 buffer))) (if unsaved-tasks (irony-iotask-chain unsaved-tasks parse-task) parse-task))) (irony-iotask-define-task irony--t-diagnostics "`parse' server command." :start (lambda () (irony--server-send-command "diagnostics")) :update irony--server-query-update) (defun irony--diagnostics-task (&optional buffer) (irony-iotask-chain (irony--parse-task buffer) (irony-iotask-package-task irony--t-diagnostics))) (irony-iotask-define-task irony--t-get-type "`get-type' server command." :start (lambda (line col) (irony--server-send-command "get-type" line col)) :update irony--server-query-update) (defun irony--get-type-task (&optional buffer pos) (let ((line-column (irony--completion-line-column pos))) (irony-iotask-chain (irony--parse-task buffer) (irony-iotask-package-task irony--t-get-type (car line-column) (cdr line-column))))) ;;;###autoload (defun irony-get-type () "Get the type of symbol under cursor." (interactive) (let ((types (irony--run-task (irony--get-type-task)))) (unless types (user-error "Type not found")) (if (and (cdr types) (not (string= (car types) (cadr types)))) (message "%s (aka '%s')" (car types) (cadr types)) (message "%s" (car types))))) (provide 'irony) ;; Local Variables: ;; byte-compile-warnings: (not cl-functions) ;; End: ;;; irony.el ends here irony-mode-1.2.0/screenshots/000077500000000000000000000000001317254426700161475ustar00rootroot00000000000000irony-mode-1.2.0/screenshots/cdb.gif000066400000000000000000010422241317254426700173730ustar00rootroot00000000000000GIF89aP!"&"$*!7& !!!"""$$$%%%&&&''' &- (/(((***+++++,+,--*,,,,---,-/,./...///&-3+2:1*.7)1000111222333444666777888:::<<<>>>*9IUUU0CV.G`1Kf6Ql7XxUP1J)9g;o"MtC@@@BBBDDDFFFHHHIIIJJJKKKLLLMMMNNNPPPRRRTTTVVVXXXZZZ]]]```bbbdddfffhhhjjjkkklllnnnppprrrssstttvvvxxxzzz|||~~~U:^;b=hUUUUU@mBsCvDvDwUU***ԪeINQvUW\fUUwbehhiUUUUUUUUU˕ݡUU9! NETSCAPE2.0!,PH*\ȰÇ#JH`3jȱǏ CIɓ(S\ɲ˗&+ʜI3j!̟@ JѣHKdxҧ-gEAFLʵׯ`Ê=uA4z6NTy5!~˷߿,+"kKxb $WN3k(!6HPǁ!(mC5ZE tZ@A\@>nʧtq?vJtZ‰ (pDzov-GtoޝtV4i,ـhe9pc(G!(}i兀 1D"]E"A H)bN?d7@l!0Rq'ЉGFgc1@v}%l (Ae㌀)t"UVV"ex= H<"o%i @F@$~bi\IV*M֟ @u JfECDVu6ІCo%WF}$zU~N8ȶ'tR¦{ˮ9mBڒj$1AP܆+|ƑklbU6]PWlZȮmpcqiov{8"+F"-|Z@yT {+^w]\UՇv|:ry%]aû5mp(h]۳A "2Dn or]+nWX6ۥl|nsCph ]CU"||dzvMk ՉGM!h;Y %vB.Bb* <Wԥl#0)5})l0H HDheD$:QE*ZX̢)n` H2&ċ_4@б~`CH:ĎjT#F72_0HߘǽbZ;H)#B:q I# Cs#g(A4䱄d $ByXR ӠG>f¨@b41O2D ʁJ  y0bpda\eSTDGU`t=غU7ray$ìg]:ZPayb h Yr"*.q듑*1ac.A=PzTBP8 cH#PF aH hApT@`BDΈ]q v݁DxF i4d  %\E>*pp c/[O$YX , ao~[ ̸wevC@ϜfFA<0Y}V7 |LXaGyW,ʲLz`T"EP@." !a V@fQlj*9&f@ C 4 WƦ<̱ Z g01Dw8 ObQX}(-B-C3\`ԥnd}{gI+Ph,\ $= XPW"} 2$ W )?j?2At{cܭ8q_YumdR oR ,)VC =`Lc h_/ _G_z[T |wXW` }}tJ ( Xy7'ZX'] !0pzWp ^S0q P {k7'|Մ|AFvVPQ$gN q P epqu&.0HR-1x1b(|h 0eeHZxv 0V0S  @a]`Ds؊r8XX]xyZ  5ƈ!  W`/ ^cn PL/  ۠P@}QHt&f}ƎLWɸ )p 1g`40 Aֈv9&y%H}(-P 0x3ɍOSL@ 1  p@N RILQ9D]HLHEjҀtk`tt Jp#XzyErI'{j cYGԗ h٘<^㘒9Y~dęZ4H)鄍y`J9J@ G~J`LVKچKALѤeYL$e_IJZ1|QY0"p/ZOy@-` wO Oi%PQi9)UVjebp%WOP@e ``0QPus_6X+*ҊuZuYM9g+;SWu=r0A6\kg[PA|c 0 0W;Q` 4 ~Z{P+p z'W{w ~GQQPg~Q3k{i:((ƛ+  P=mUAL `40 JIM  @ +e `,Ʊ| `7;o>Sz\у? }k;&@!z|d@ 0uW  ɞ8S(a:|Ɖ\ܼv;zsP:h 88q,RZ\ A|d <q@` Z w+,J  `8Q瘎AَCIH A*Sk"IA0 ),§@EiLĕW]Lm^> oFʱ;9r A1lL޹EQ ZA BB7CE>}<F2tR]ESDIK^ qG蠡W}ֆT8ln5jo=t])vzח1MI ˍd p՚꘰ mɖJJ ћl׻ Q q'4  ApM$LPq9L]P PPLYꙬ)]*mBX PePPضPAZu *:M0)-Z/N^VbEVKͣ/hKJv[}> -SzVHP*W.% er!]0q:,xA<]+/=F.'JG= r :jzjŪba0{dgl簚`Ƹ>_jaZaiNƊw {JI1 Q:-ⶠ(M^gb6٦v:]k<=`:P*Nn6( o:Ď   K1 7+Ka.u[T"k (Kj $t^kKA;ElNn QO @0 Fpn G.d+ *4ls[pm@9p= 2p,p+twVz˷~y+RoT/aKqiwƐ|ιG^Ke=7Z9WoߺSzJ 'z [@ {u~xGx1vr'P; uv(>o6Mջ{ [˾!2 0~0Vп1' {hlL/l "zqƔTjul- G`!@z.  2ӣM ؙs 6촱BC(dh`9uOA%ZԧMIjc%` b،]0dܒyIRU+X55,["V^Ӥɸ͗fA|.U0_%S:M##uLtLQC9/%+Q  IF =ڀ-5xPȼ@ ΟC+, \ !kPMõ²nJEٱå5sPΚF@$6B%z\:9va2B 3p p&f6[k3Q~zb"K)eFr4h=wIs90g9L%|Ҡmԡu22&S*Dz)f^gm tqG u9N8[p=Tr5\t].cW]^z^|w߅_w` V]׈dG #xb+F7`3b;V݄A.K~E P'tec\kۙsyg{gsozh6hVgv楣zjf}zk-;Y >סNm3z3=[m≺˺;oVg+$@ yVقbzYhoA)Zι'@ %{EF/tPs:-mCZ|{]ix8!Vj R" 1,WhW&_a {@B HO}pg xab?}c\bH;(, Ϩ9\&_<j`f>03*h@d $k1$/QbVZ6n tDcg=a e2yxz:1 H|s"XP)GjinyK [0%I4N^Y=,}z0q Muh; ȖzжmȶXh۾! 4&`C8*Px ZJy$TnJ]rıT!@!a+ g2ᨀʌLc@(G3 `Ҡ$cH#PF!miJ`|1 eFYpy9G9-w\;-`g |"I B>$f@;ǍB ra$r@6d[$PDX!g@JL9ށ8pقU*W-+ ό/SpGF^ Ĭ \b$HD< GF* 9<-fZ2MWahbcbB"*d Fbyғ )ŭ0; pI kv$h `&wAptڑƬvW!`\.b^JEa/?ܫe@$cOF:!g kɔN\7=Ec["qsba s8)o ЯTQ(>rP~P9-\R`, 2X?#,&Œ$mV dFekSe2!C|q!7*˛ AF5\b VJ4Q<27$ʐXp;@aD- MSrI)Nʙ bh-h1wR6Z)f{/La48e2؅D4i̸ּ [yp8#qe09©c(`VPg.`Vrp{y ]J uA ``Hʃ"4!9Cl4@A.}hD%jwVԢG; #Geuo0I/w-K`$m4MHTT;vO}SyCXգ"IURڔP)dTR l02UfHE:aPRHH2Axa\Ҁ*W%gg[}|ɋ٢}oI^DV/ a[cf_?گ;)QyK@?? @ @y@  @@@ AA,A6֜x Hp͘(-c@>c}hqhIvn؇$HLMV 5G#uV,Hse`#Z\.nx rFI-A+`"xdeY!^JdinGddGe-2Dfef~f{vxG,A8A(@ S+.6@<.~筺ӄ6MjzM5B6A#`i9W+'Sv@vHC6НA,D?K:Y<ti(QVjz`$]zu($Uzd,p8h8h]>q`RAQTCfzp{c,@YPI$UA%{(@Fk@.h{4Ifj X)-EFlAQA(hl@ͅb4.N<H9x74l ԐV‘^n-10AchE8 AT*^.A(`8oBEDp|nn.NZp3ut^p!|Anp p Ap@u@qq/qOq_qoNJqqqqqqq r!rq"?r$Or%;_r'r(rir)r+7r,r.r/s1/s2?s4Os1s5os7sr8s:Aq;s=s6s?3s@tBrA/tDO$?tEotGqFtItJtLr9tNttOuQ:KuS?)uT_uVoUouXyuYu[W?Pu]u\s^u`7 Zvba/vdcOvfGeovhgvjivlkvnGmvpowrq/wtsOwvGuowxwwzyw|{w~W}wx/x\OxoxWxxxOxy/yߤOyOoyywy3_y߿yz/zOzwozwzz/zzzzz{gz/{?{'z_{o{y{{y{{gy{{'y|/|xO|_|Ƨx||gxʯ||'x||w}|}ҧw?}xO}gwo}t}'wٟ}p}v}l}ާv}h~gv/~d?~'v_~`o~u~cz7{~g{~{/{O_{'|ÿW|,hP` 2l!Ĉ'Rh"ƌ7r#Ȑ"G,i$JWL%̘2gҬi&Μ:Yp'РB-j(R> H)ԨRROZr+؅X{-k,ڴj;m-ܸr>m.޼zi``Ċ3zgȒ'S`̚7sxy`ТGK4Ԫn5_[ˮmvRڸwMS‡N8*M+oҧˎN:vֳs^oo׳7=V˯o)? 䟁 * :C B8!Rxab!+5!!8"%x")"-"18#5x#9#=cA 9$Ey$I*$M:$QJ9%UZy%Yj%]z%\~&ey&i&m&V'쁉uf+RĐ}d QTYKZAƕM$+QL&zsy/,H,hQ eʩxNygrJD/c9/%l^d1 Z̢+-,)C-f+ĐOB6 /!Hqb ).DZ$g2KK6dHA %QH B6pėB گf PRMA B5Ē-2˱ !P BKmrXR dH π&D RvE?ͱ)A A^A A!ĪЁ%}wW${TbA!{PBI@1${XkC.BHRITnn`vGD-DjE7JCVqFM`<$v@DBK n>d[YD/Yw@w +URA2ъ.,J E,Z"!E!.VB Z ZGR+P,w# fš>{^p_[^X%IS^ W XZ .lafx @BLa=A,=h8!*H G9LD+ԩ*4* ^Dx!ȤY/H+lQ( VA/*6YdYH.6qH +#wBB.D HB01Dbx-WEЅ Aau$RvqiPhA@C T Ɛ;SR, tG`3)M!qxcՋII, U"I20B,h`Ju KC&ONJ nP$ LL.H]U;NCk:0VdjVH! qcKD  C#[K$Y 8b ^C4!ͱbOER繭!vB*-0^ye =wPY dY!HN%@"ТU(UpHJH&{ *$"œr#I %AIv-3Ђ, &hIk~8f:FC\DkQbt$,aRndmdWg5,j%ix`#= ‚G &_^6U7[$$/UH8@T٢s^JeQk+*±DZ:% -%_~#QaBZ[6܁wB/0;$&W2R#Հ](ޛ& Vr7LP"cm؁VU'd Bڨk%(BfE: iMڂUiF5\瑅l~]&88/6O!kx¤d: ; Vځ1GҸAGEs%M@tBլ&h!uaCZŌɪ2`h!LTx-:2A ~p'dT%8(ζB"[n,0ʕqݕ3-JEu ;K"E}Wqi<$UEp>Z]h D t ) p0.$3dG`oO`C.0\0ЋD-X5I -e3ȹ`\ $G$N%bG%^&>G&n'"G'~(G()F)*J!S,@  H*\ȰÇ#J(Qŋ3jȱǏ =RIɓ(S\ɲ˗0c<!s3̟@ JѣHGZgϧP/&cҫXjʵׄC!hwNM+۷pʝ 6-ݿ Lؠ!㳨YǁC A@EC,YtyCfΞt(Ḣqگ+_μ9LgE~ ">|F=dk :T5jDYw^ЂQE (ѐ!; ܁& A@AeY_{-"= !$d-Y@ (aQyGdYhQ$U$~ӂ@)\!0ExVd*^DbjXh8bC0Vf-rȊ8 H0>i!eDtiJA @-F9θTZ)2GI&kb8h\htӝv@L_?$F'ㆈTyaQcj}vCT%Qu667o ٟw*&)G>zZ"fw:k A"M%ElGA0D ,1WlQiEƮ_ ll(G*˱ 4E@ AL7_O?)sL 4Ӗ@s;Hp-ܓ i!)}VOB8O=tr 6 Y5Q3#L - ^-+_OwQӜDbH=52lPD:"LaOMJtp;ej͐},CAR+4?ꭿ8 SkQLqs0+[0mvq[%0@=Xd'+c;n&Q@ ȅ9摌 dS.aX hun-(\4;q+^0K /6© .# E> HX ȱql!gHE,hbBoC0.C!2~Bz̸ ƁĄ,"#8_qH'y;.B<_Lc퓇9vEX@ΐBqK807d!Bzh.Gry1n6W P&d FBzҙД&59($%y,Ej DlQ(>`=!PoE0?~8G<>BQED,MP@yTlg[@*RfQ)0& X70@0AXH{hA?š1`-M}jT *cB]МW9M~rV`'/ҡ "{\-8թb X!КV1 D-QҨ 3GZc:o m1-jU[ֳ|)x&Ԯ@4xvjHF j}ybXF7b.`VlY x5[ v I/7w# 8~@CF 1 D 3QV\%LalC:*xp'\Vo8;5gb PG=qB:q  zȃ1HA{c m@ %7QrYld$?Wm OF1+3Op,:y. hB;Caڀ6ء| F9-:% ptBMyQu=$Ξ6!OyI=>N3d߭O>]?P8 ?pn0̡ ͶCn$pF7Ӎ|]=3û=ؠȁ 0s]trk xH 82| !IGp h} O7{O 3ջ?G>ȽwOg i`=` q}ŗ}BQ0"p/~`fF &@-`:#c:s&;Ӈ!GAZ@Z'   =50(~75/T ݔh@Cu>lvvml'h79Ƃ?UGĐ" P 0 裃Uff?nHVHPVCA4Dn&oQ8Aojs0;`o=`{b83  t۠ 1 ;`jApH WpqIeyWqq'shXa6ovJzǷ1p@a` X~بXr`[ptcN$XsvlxNrhhΘ`hԘ{@V8߈JV`hrNum!czTFus Ј|oZx3; M {  ڰ0 HxqviBIFoL0&@@10YuYo(qrwY9lhwfzqwh𖐷ӓ? ` `  xx{ٗw8e~3 јT_`D4smy6@m'mg}spx Sx9IY9I329Yyșʹٜ9YyؙYНٝ9Yy虞깞ٞ9Yy P ٟI ʹÙ1p U IU9 p , 0y 9JP2jId0E:JD ʟiX:`pZ\輱jL @rɊNlu|0߼Gk-)| C+!0p ϰ ܠ45lS8a`P ``P?O $Xg7L8cz!W/0 >8 y n ] F1*l 3U@ ֽf3Zҹ{"@'O2 5M 4K"Ҵsfɓ)RWp  ȺU@ׂ~ۗW6iC9k@fc@@s؜(gu4Р9=H kرeϦ]mܹu 7a܋)Y<',0ƪ1Uj#rE\ȕ9ڗwno Ք nv d r4?f qNi bKE P& lo /jq*L2"%CK؋P@9P*3qdR&QF bǙ)gL9vࡇЁଇlС6Ma8eO@tPB_Co eh 1YP8$ r=M9QBЃ~f9p wze^NY~ zGY)B.ৈ(R Jvټ\lEfk^FyϷ-]%uiD@b|SUoiW\}WǕ 5:L;)Mcc9V`;2`PSVyeP-ŸإQ" Ƅ{X`v;悤ᇟ\ Zh&hjFV }z"tԑGbZ|${cuz11ǜs18;amQJ9i+"BG>ݺ%h~([k.'< ha;l(`9#cX_ew~PTK'`:j#fe!S}_|;|[ţX)G fcFt ?E,D}p A\`30BQ<0*<ȏ#$+]8Lh VC>%rsmKRzXat\7~_vqO sl`;rGԐc=@AQQX\9[fAGY3rqQQISV.d,dpPiJEɛtTe*! b2'` n0Y=4WԤ.H1RAY-OrB"nAp-\@59! *qo K[yIY!I%A jL+ Y␇#"HђR80fQ{Dm\>ȑ$DK\蚥lEŏA,_4t>3@6wCZ?A_ Qx<J~wϻZԤ*u}?K/[WXZ)[\ٕ^`ιgj1 <} mr9=vw!(zv2 A Йt o0fyh ?0,B?0-\7?2T2ٙ48|Iæ9:;I}+@ʃ9:"8Ft 89)~<,JěK4ӑiXq9ϓ ʋQTS1>AQP|>8/#D9Wd!L1 +t@OĩOD.ÁXƒ(CpsSSATB-3TE]TFmԂ@Խs2?ITKE.J}THͻJURˮIR_H{`\x̅5Y+3PɵT6s]=>3]34A#4H{]`0;D* g)~|X\h cyc老/h)BV"fg`R,L%MTIݵOԼ؀"H9$ۜo  Zs[=^`6xcic6bK5hC6eXl۶@mXP{Ȥw]F^'I @wX.i,X(Ir4LZf凗/ux @{|y9{7(C<=8}vd;:3 Ӫ8!% ܼqxQq]$ڳ=*5A9 s>|0BCV^ڂۏSA|==c~ ێ[=0w\@(lU;| (=%aK>⣒Ђ`x>_ ~X*؇3$ xW]]sY8$ށ xVBC6%Fw~X!] <^O@?JLO->% N @G T )Bx{0KA\V {0 ¼GA!s~50b(Vxx|xaqX!]aa O~`Zpwm`Џt(DYN;>TCChèf:gbHDb,QIJrDH'7&kDfgD1Y8Ƀ seTUĽqŁ֡E]h\+^`0vWd#@*Qv)pYPUapr]Ci&q*a_t8GD(uf3;rN."粱GyIjFw^L}aGd H,HOJH߈n nJ!#BGzPE$9!6IHIˇqKV] o0VX}h,ʾ =&*0j>`?4$#?<^nK\Ͱ$IJ4^腘SB~DLXތMl+NU x`\$(q-"R؀g0t@ >d(`6пJP2H ɒ< F 3 ͋-QceyX G ou=nmxz V tQWX{fߨ""G/:5ag.- vd_7d'S:SjfvF]v$vbb ;/<0vrWTlrOwusow^wxwswzGwy~w}zwyT[=PQ-S]j23Ye'3h73,3V3`@3cMe9 >t imUguyb%Q#_}X%Eyۼs5 5Z--[5h/z6m/@Yeٞ_ Xrؕ[کUaZmj5{˷}Kۼ񵯸U|\uVCܫ8{-=e(m9t!dc9\98}>i0V'pzP}y{R <`\b.p<p_S۳ __~ehv-(vdEF /Y ؅`2V&haF Heh PĊ3SQڭ0 ƻ@)DTIРB-j(ҤJ2m'ԂΆ%LY1*`װ_-[J>Xe׶}[VTU,j ~2&u[C!xdTC6ӂ ,52C >,f3˳~"&6_~Ţ-84+kZ4r8rʙ;ߓN EH{RPļq!”27`;w}:? WO. 9JBDC8ݘpz" /`ArHЂ=yކ[XA Ѥ<̵-H^z%vTECAUm1&CO1@?c9HU5d&44=#LQievm?.$^(uW]^ ADEaCH:T QBu$G!MË DӦ`Dy85O\0?4 jRb~JB3F,̌QW*T+Q AVE=ZD 4s!+ XK`U4d`KcOBuK  7=j +$Vs[Z@|l ŸFU JX7XOA#A^@3=' 5N`#KXAe)0L W#" @D+Xkm14,`=i1`ΒSP@xA1ɔP@ (l[{"2QÖ]p xTPĀSvmtS6`0MqQN L.@< 0;oOT4{A - 5J$2A L0M. 3[:LPEPR}')Ѐ25hYP*tM:6-v z;4(;*7;rAZ6!;$̡@ȱ@y,+@`Ģ҂zG:xi#a^SJ3bTT l;d"(Ah 6Pttɭ7f]Ff&+{Q2ԠFO1%dA R e-2c "8¤XJV/1퀬\+lA08Gr-wsf fF3}MdwY8(PdF\@V@ #Ѓ.4 (`b41$l+ ֍4(%p ƐF, huEG],@KB(Pd&ȇt0jKfYHbٕ5iS*R+9%UV%T8-1VU:T012uYV%VV%2}e0ɋ7u8Q8[KeX%[%`` _`&b&&<&cƊb>&dFASc^&WECI,m&g~+YT!AW`&j%Y.-`T%DMTOEO  T54VUQ%UA&NeUr$szEX -ejf[!2jf)Um_`aa%VdTqgWVdqy'U}gzzEEiFvr'T=ؖT0XoVAAp Wu9ׂ%u u=.rz2wyxxCyz({|YO~9Xe_X5(yQW\ ֆއ{|!LBvǴ؋XfʠEQ"eZiyYBYTYhY,C;7lA8#))*yQyEmZWujDZy JDL@ܓWLZa8A횫#*C*1JT ۹-Y1Vf')c`A\VlEW5\čŸ2Li#e\Enu˷h*Ae`8z @CUEjX *ȡopGIPsir\X(+5+OځN+Q5ȃDȄGݝߑaHܬ(I᱈ȌȍuJ^]IlIX*XO魪 @CU\^>>R,A8 !ʠ-^Liu2%eBln Z4vŴ_XDK%˲4˳=-]K 7 .`DjKY\ ںi1y`q ^LH ^E`ߦ 2aBJ(T>!NXtb/TzF\Nl٘!؈\-axp b8O" VI|NT8bAJ$O`",0"B{\*6-f./2e}53&nAL7b9"dT18V㩊1=jпţy>md@AaB C @DڑSd}-iV dH6 ёC$?DjNO 8!YVg/I+i2(&Oo( '/PXXSa+(g+{)c*\rS]e9Ͳ/J,k/31e03e1'2/R034G/;I*y5{FDg665+ɒl% ;| 1<0TDOUXThr.qEt>dDUOЦW<?:":xIGM CيC77 @8+g7+L9Ԁ;ܲH2P@CL8z Vp&>DNVesE(WtEACHsjyb=S =7@WLT'G3pM\Uyhxh͋N)3)rE vT{-DW -D N{D]w ^ie.A ,RE$-<L$0XH4̀B=Wqo[xs(A`d_a# l/8tb/xM/߯'DۼF' 2N 0g@x=4)C[2t"BpGhKTf0A}(B!>>!&Y{R5jMÿ@>L Vc}B@zYNwTzz?WcXX*ddZykB,6=yb3Q!hu37rψ%XeZINw$4SPG$bRhʇAd%+Aa*4CoLkOh#s_=mS1^O!|Xe_KBڷĥUo^{p` {qbŋ7v\Ƒ?&rf͛9wthѢ 6}ujãYfMFjOkݻyjÉ yr3 zWbPQ۹wq}zٷw||׷~w[ڏ@,dL9|ph9t'*llsKzE@ y@0 iɇ3看0 *Ā!"Q,tÆ"R ?'Dٮ<nF,jrZ0"/ BEyrCl gϔB *Ý)D8IJܲK<3MARCT&{2ʨtbQb(tcqug*0G;)@!PE%ծU[XCâlUWX5{FFq@!/ȡ  :6ey(`[A' nmP@x,ew!Yy}7yEp4Tl1 ƔS!uuoU =ZGʈ@azh_dF6昣 sP;a(A9zh@>ZgiYw:illۈzfmF xacTxBqc%\`gaJ.X`rZs97pm©@i>la&@n,]f@!c eT|ww8Yb(ĎB +gMYu=wnWou:~H~@b"rziud<@B&<r g;A Alv `Ѓ!`B4'B&m9\ L",SÀ!gDLP$@B\ E)*O/cn +1-0!z6&Y0% t|E8\EPѯ+*d}T+T!&yt^*>69B 9 pS!IIRR]\H$'Y,'C`@mP0_1Y:>S.u9ЁѬ`>;tM5wC/v/& C4$/ɆJ-BCL'h@60*"`v06ecc˫k`{6 [AbI 0p ]3C,@z N} {[=V̿}3'(ɓqqycb076QU=ꡎqt:|v!6atk#:]PG=!$~d-b-$i&5IH";O'' Ax}%yNJ$t9tC M왭!A3 [D6镯fNmmF9U!z /}9Rk.x_^Aw<yM4 n8_zӿG==pbSS>eWHTPEUXXrE{HW\EXV| P38`ʧ(EY&YZx+[R[%H\%JF^^E_ =^p``1ZF ! FbOcdb "a )$ ""fʆjh|h K4ibgP.mjy nfor'y .Qqqq1Jgs sB'.'s1h|\1bgv.jy|g߄x ǍxzHg{c# b "~.`ơr!EȂ"h*(퀁栂fJNRTF܆Q'QKȐHhHb#123, hh9r+hȸC I"rI gɕjɒ0 !)q2: | $/B**Enir zF @ 2I2c2c'w.֩ b&$v &Z`$d\3fA @ 4$7mr#6ssVs{ovpCIVV!FdFl/uGd(&PLdI~Cdw1+37C74G7ArG01%QīE(XP=9 OPP0f֊xc}sy 9SE pOVke EWfe ! 73LXe Y!Z[Epcҭb9&e_ `!$29!@Fd*eVf@/j_lW4m&nqBo&#G'qp1:E9s@GtH'3Tuvdvy8y$y"GYA5TB9˳@kB8#K-Egn+ZEŌEaE FkFF &DzGGaGQxK!Խ:kkdl4HLaM&4\"NYl]"fƖONR#Z5QRʲl˺QI"Sl;u>U V[NL$՝f V- B9 9"UX"V8WtMz֠ZUk-Jĵ٬ܼ-O}9nuB[!U^ NGQs_ 1V` XVa'.[6>)bKn\hVHvckNMZPNeabv6>0|v&.eiv"ҖT61 -k?i}q+7lv7sՖr|QV3ü-}o:qټ5<\}4]3 ;ѹ/{՜ot# V1Z"&$w! i!%!]]wGz57W=z}Qz{cM$W6oP! ɘ.}OOEC2v}{] y~"UX ] '*Pc\!6F?xi.фu8FYEQ]1R/= ↟"dX18kWQJhlshmXXxъ&2`5ɑ1-UX"8xǎ^q3 R!@~2r>"=&}֧} g(J@&Ah$omi牯(z臂蕉(g*0{r!Ȍ~Ò3Ruj)0B鲛r>|S!(Jab0Y!Wwf44QZz5 ?zk3ɟ6=cSCj'6AZGShʤ‚rY0aB6BOD&!A%7eHJ]@- WaZN*ODɘy3' ;6ڰ5jSm9piӄTb/رd˚=6ڵlۺ}vd ~, *䍉l̴#P/P†ݻX@Ñu\,VREXj4- Sl) X8.̙ E=R ̏ai`ND(D -}~"s<%^@zVS;=PG@B CBmЀW>a%6=P3$; E:0OEV)X(XB"2QsL#" @&Aj &K:M#FcH\:M1CB$SB1`cBdfdI 0 2 Dfu(32AA NB_[-0 V3:ui AqO*6`0 1@=ņ M;C V X> mB( | 3B(?R@=4rNyp(S禋#V3+ލ.sMb30=3J*UHҳM1zV 9. `A.sq"d2*B, * 1 cO7F$B#g\:"-[<́1"|1cC:v+@=1G sABtۍw촎?5NNy_>98E9碏Nz馟z5Fz뮷z첓9盟nD |o<_bWJҿ"T:`,}_{IUPL{$ÅBXlIb95sS a{`&%BXTh!`SITWTZԤs Q]ճA,]i ɖ#" PB37+C%Bf@' |x5p[gMdFa"b i' fʄVEТd ,B ? -B }}b taUТhhV!al[ -,A/؊2" e*K{Zl!,B:ʊtM %ju^uiR@Ixp2OkAK졤V%$, LZpXLT,!qZ [Bp Cxդ*f?[Vw)^$$BJ\viYEdЋPS"7.rA + VA/f). ;łdd7+e^PمBh!!ar1NEB0 B0b6,[:pYT ]h!fψ0 8?;@!؂+[COa}tXV '8 x*cX<Yw,iH[8CeTeLRLA)֡ nC谤**dX&pcn4f)1ERƒ) - KXGŲFVhiwTp !7kiFWĕ DPiiIߔ جcbP' e?p @r?^H"9O91M xa h1_i [hg/B(s]0 ~^}}U?0(-Q/bIz.uQΦc{>ضo5c-B=p4|c5jXJ*dMՄX WkA ۽h +:wb#Y)ja +s3  t,-"䢸aBY}~Zs cBz S0?,lHZEѧC! Qw\"PvC a|I@ KgZT0 uRGbpg`dJGxbCpRBQyUzn*Tzz{TMTfUdq'Fp)`pmc]VVgqGF q `BQPMJdW-Mf,p tgH `lVvr×~l}8C/cE8GBO wuuvxq0w`:DFdeGP }]L'8( Z& D 1'8]eU;&@HyUR0SC䦄f6F /z)Vb61VR,mW%'HU p)7c2fc{ds$ UNud E-TdCP@+KgxEpEX" lQ 7QOJ.dYG dx! |E!Lf0 pIhp UB apaA HdgPKٔOf&9W-O. "gvg64i WS?H bRByyT Ta1T,eT9`(y_\SGT%U36uݨkVl[RS6$x5S0 (IƉZva1 `Rb1T/ ` g`ja^k&gOmw0V :T0 0 Xx 0.YZ0NC gC] @3^UYc:)YP 0U'K/Ք7Wyj@ W牘BX L*` IBoT8".UC֣frrh2L O"m i@_ WҤYDBAD8-Bɲx#B4"pM DOo>c&@TC8ɧ@&-2O(1@C ĸ@n8#d=KN߀ xBXRD A-PN7[@HAhw!P \)6 VhC.Ale'2RDH kdrУ;"BaasR+jxCK1dprn`P 7E5+Ql@8h%& KČb~evS^Z$ǻSWxpo49x ц8LGIGRb11!DY0z}BEt}s 2$+9  %B\0LĂ΄fo w!QGzK򊵣XKTp 8F4 6 OT@)`& [) Hl' *I\ `Gw)H0+c. _BXp-icFGYtHi 3(@Y%xR* *f(Z@Ui F2:Չ1!IQ*`hc1דR0u[W@VZ Pɤ.5/+;8m7q U ĪKmji: -O*TU%ޡC QfE)/#.1cHQ 3aSplR܆:Qu86z ƀzClU.Qs <H+DZ841 ј=q&bfƠ<1v8P 𥀜651ס\=jabi|#P-Cp xC H.X`> Თ`ȗ=nbg f!3H>Ј64%ЁvH;b@Σ7NϤҗ4}*Of AD^VbTPG<ЁASָiRcMbNf;ЎMj[6Ins[MrNvvMl7~׆w7ɽ Bv2\P0) zcد :8=^sC:8mgHOl 4=G60A(ȃ 9Ml P3<حGh# Q:Rzu ?8 ],C; VD0PxA/3ol֏ӎ1nC06ay߁.F/iEF=3-xD x};Px?Ȇ8uc` al֥vllG}֧lYvaml0 Ɩlxl6C@s; eVp9rk0w T @D l &Q8Wl\^x}TxlexӶz&! lc &@p@vt " l` - Nl   - Hlʠ 3@ `(k؉f0^V \@@ z˖~wll4@Ȁ'R@(l  xF )C0->k9|a><H&Hllg d'R ll 969mİ %{ň@l !l Zpl Wlx O 6OlQ@gv9l@lIr yi * l$@3y˶JДOldfxxCm.xrA`ux]Fx]'phF7xO Ё | ¦Iy llIm wlzhzU0  G{xl{7Vp )"{ɜ Ƕp}Iylw gāT|ivC]l_p7l l)PVw!5z=x]zmɈ l ١V'Ƣ(mj{l]:y 7|3ٞƖGl2lB lE15PlѠSjlH@6 v0 @fR:MiPp65Jfڧ4lZF q9݆x=(9@@||6ymЈDPO f<'K@{zl ʡ: @-&{p0 :I3?*ǚ EZ+ @X!0 x 4F*lzll@ P v!"hlV E 10y&-,l 2p J{ׯ*lZ::x&mḂxv٨90(0ʃ78;rFmFp s :tc3@yZlTkXֵzs w P Ѫ`P[}ސ jlE @` 9 es:l[vv¦s򀐸(rl`lJ 4tzȆu0  Hlf{]? l`C)6=@8سX8xڻ&cl[n` [x曾xlmPpn{V @B@B=D]F}HJLNPR=T]V}XZ\^`b=d]f}hjlnpr=t]v}xz|~׀؂=؄]؆}؈؊،؎ْؐ=ٔ]ٖ}ٜ٘ٚٞ٠ڢ=ڤ]ڦ}ڨڪڬڮڰ۲=۴]۶}۸ۺۼ۾=+ лŝ" bP/Up!Pݞ Y CPUݘ\V. ] ޘ |ʡޖPp@]D FPUp ߔT0 p:D!,N@H*\ȰÇ#JHŋ3jQc CIɓ(S\ɲeɎ0cʜI͛8s貧ϟ@ YVH4ӧP-WqիX,z4NZ;s6'zd Ꮖ@-($6PW:(+@p.;v 5Vʹg\D{]DH[ȵ0ϋv*Y`dCx`SDyicϮݼӶ=S3Ë1`mĺ pFX:wOA!XYs`zV@ {!t_~zwA_E8h#19 Qh!Y< -Ѡ =Aۄ!+2 Y: c@)(AdB].V$< b/2DEqD ]4/y@\.dCs:?:F_' р ?"4ިr7HЄ?9X1t*WƺBB+J쮽:윉8PP@2 @N+!V{m156T4&40 B9أHCJp 1҄`2| ۯB Wd?Hax)K> 6D_fګf붫ЛC G+_@8s:l1 `;f? R%T(dנ=4 -0ь":L9 A3h74wwvW4 L2!x)n+$T?d ̍Ջ潲S]O1~  ' P4\A]CezRB( d4"p^Cn; =L_=aG4mpӜO>LW}1 Q]Aptcbڑ'& ҘJ.?&8 ZЄ$X`ȇYkHD]j_ٝfQ{hk܉|1Wil!posy} @~} kyٜhة@ mPp ϰ $Jk)ڐ*Jm)kp y :|(kx%i.V P6fXvDw 4pt ZIk9 1:k+ j v>  ާ \ kڇP V&jZkk v'm^jR`s2Z&-,{px00PꩯVf+IYI3p @!`^~ J @-&j6b"(P/ ⰭDǖt*V 1 VIkJwP x}8z xx8種vɺʂLs抮ʗPH: fC &f&K@jN DtZmX0H``ˀ|g iKk;P倔H} ɠ_  P`a <)yjb+k>:HCj(kb qqUJ}Kke`! {HCrzkr[k2Gs6smqk? py`j˸k[Pc䛚X{'ck+m` n&|{klPp \F`XV "<µ`Y, m0k44\6|8:<>@B@B=D]F}HJLNPR=T]V}XZ\^`b=d]f}hjlnpr=t]v}xz|~׀؂=؄]؆}؈؊،؎ْؐ=ٔ]ٖ}ٜ٘ٚٞ٠ڢ=ڤ]ڦ}ڨڪڬڮڰ۲=۴]۶}۸ۺۼ۾=]}}O [&0J /j% T8jV !j3 uGjejO` =P}>!$,^@H*\ȰÇ#JHŋ3jȱǏIɓ(S\i˗0cʜI͛8s*dɳϟ"u V0H*]*3F:J*UX BF/&Oru_3 ''?[4nZ:($k@ ف3^xE=wWF( :,v !؆QYLBcUI1^i6뉈)0ōp*Y`y3M)"Pwpȵ0 w ?*\!F9o*膤]E.T`H%FrL:<=PF()0ڈcyAN=C@#x@Vw@ :l Ӎ17"MM%##7,d.3q!NB3,d8Y$ tdnȣ H:ZHcLC4 g8d4̠E.".6SVy@TV8*@sD^/,jPY6 X{]ȝr F59ށ"p'| K$" X@ =]q,i #,@R/`EpJ uB@r9,!ENjs:#rleE9Q ,009%d "Z|E :|$# Iš#f!Z? ,hgR4@qZBQv6ȀGpsdqXA)Vq * Lr C$HdH f! E9(-.ЀQ2`? w4H')$dH.6 ?LpbĜ,74G@X8MĬpRyRQ<٧ ЅR)f:SlJ41 שey΁@B^j`,ct)L_S˞AH3ؑ0c2 DL Dx?f #AUGɹ' `&@:61Q YRz׼*(&;hi>  dxun2B U  V8L@xɱWb]̶= IHUU4^WR!c @HXVzHw ;B:0@yI 腠O} ԾC, $S?SC\c@g00zC-$zlE Agc`c@= Y 9 Ex-N h\1 ј=!'pj80=adkdQ8Ysb̐,ь48 7H)ϘTN*;~r͕0c >=c`o<ܯX?sfxŷ] {~?>_R?2`]QCB=: c5>x{wمqx(cu]w"_y}3\du̢!h[W>MЂ*& dQ0Q`d60A(#f/}[&E'R0RX7Єuiy߂iC%ȻX wۼ m%@۠ xx@HmUQ`qGpѠ ˠP` v)&*rgn& 0md` փ?@E 0 0 2`nE @ " &3@` /m6Hfh6 sa_`6&Th覃15h } @n hHn0Cp/WmnG$m ߖwQm n v {nm7犗'ЋsmFrp ^X @D m 1ሇ(&! mc uHy8/  R(n(d -ʠ 3@ rxYW  F v8na  in4p \@n⦔L_N 6 # w7'GFBmm- xjihu>4n\, oT8png t ljoypAYn⠐XV ϐiF; 6Hm6: OmQ@rؙƛn/P^ Yn0p8xs8XԈ; 40v ^詞  {{1psFp WwP . }9x@x¨*nqv:s.  *n\ P Ow΀npJwmN. ߐw~57 9 6I*GmCzOoInPӠ0WvY. }2mepK'+ qoyn VXLئ;fZnAyE]m_ (QynlZ)%E dFsGE Nh5"ꪰxhh8 ρ k: m 򦮠y .y _ Mh:nCy٭`/ Ex50n*n k{v ހn nPՠ Z|X Jn*@6o/.ȡ;Y ޶!砻؋7pP7|ހh+`n\0՚Q0x؅ pmR`u1'-5'Oݰң3_Z0pIjn PpkSI3pط @!`^ zm ;1` " n<'@fP/ 9'}wk:In~ m 2p @gW5wˁ۽0&~G wv ``KXXX3P p 3PC&cSd߆v@ZDP{ Wy,p{O+n * Lx P _É`P̛ᖅ PVpp  1lRm|en2 u *k1< \qtކZOln7| "ʀFVyTWI&DU',7vewv3 & K%˿Z(yOKx0CPp 7JFʱ*:Sh;:W")*<͡'cn@B=D]F}HJLNPR=T]V}XZ\^`b=d]f}hjlnpr=t]v}xz|~׀؂=؄]؆}؈؊،؎ْؐ=ٔ]ٖ}ٜ٘ٚٞ٠ڢ=ڤ]ڦ}ڨڪڬڮڰ۲=۴]۶}۸ۺۼ۾=]}ȝʽ=]}؝ڽ=]}=]}>^~ >^~ ">$^&~(*,.02>4^6~8:<>@B>D^F~HJLNPR>T^V~XZ\^`b>d^f~hjlnpr>t^v~xz|~>^~v芾kL NG HT ztcD =.@Y Ȱ>D@ LD lr pR ygbp pG!,^@;H*\ȰÇ#JHŋ3jhǏ9Iɓ(S\ɲ˗0AI͛8sFULϟ@+V4<*]tdmR]{հzS"0̼) [CӨ_BZ t^<\ n/; I)WZ DJB-CHw?;miSz*4#P7Ē5VU͘z5VЈbDس;G< >uĝR *ʱ+t:HG2B 5_$MBaFӵLݻ*'P+G%_ItUk cL7(@dc<Ĵ Z(P47L42(T6|yc@0# 38D0c - >P4,8_[H d#:v,2 A섡`!̃/0AAoƉ)C !+lDcYvQ.HL i$W?D:N:c }ꨥ΍ MJj~CzD(3 AP4`/ S !0@cHaځJKPR'Ȣ7ƴ ?e9 6-0 ) /8CL9o6;"d8Sxa?`C\nbK ) ?ȴ7֐%+\!AX,.'4MHce^0t3Lᅰnvk/7FP8:&w-9tM.[QVJG1T3@T)l$HL]&.A0M \BHrQzzqz$IN :v(J / )^B YB;ZTXe(C@0"Q *^#"1cDL @ugОe<BˈrC"R)\kxOHB,BwɐOҦUxd&Ihp2%D8n1a-F-Rv ]@ i@ ):\с$BI"c8 Zɜt`g>^2LULHBP-ve|&%$_h P@wO~6bSdA 7f $CQC#I( p BiID.,;4:I Jq̴T8 @f&,$!38v ^HJ U*A Z`tjin: p V`E^ q KU8i)F7dn/!`+YS;9:ֲv},xCsC,(0GhKhTZ"ȴk,CHؤG]G WVհgMZָεw^Mbظf;Ўj[ζn{Mr;Nwo uXj(l8nMl_ȅb~C %ȚĀM #;dMs#gX5Qxb%?yU|W=A Uo k g 3Z`-X..h\Z%``,;[}=ϜtU+G|]oXx6,{| !u& ؈f PY]LAGxa)أ 9Z`xxa?}WǺ d U#c+:=镟@4x<= @;`O#=L^./Ot:`${wa;*OPykk0pxmTxà rb@Fh (V    k/ ƀ ' > f0 j>BHk0PkE<8ekJ)-Zk P`{0 6 kmqjs(k f f@t jА tGj & jۧjep7y7vֈf9V  @  `o6`+` UjǠ #N+kȨ(@ j\ x hVw)$k0 :ȃ0 @ j !`p֌X(kf Pk~D' օ_b(`8&`@ fHyi"Oe`^j -1kVjb!zl 9H& @}F ЉaK鈭FDwIt"0 `` hoXjb  ǘ m")k0S 阫FiX V  j򸑲EvPv󠄇jyf.Fk` 9`ȅ^Iۀgx75 "Xk0 CwS40 p )։k+j9^90@ eU, j`u fuH~(SyƞvV xPO jvԖkҰ ިjDtTWp):&Z759ʣ3k9`jXѐ kC*EZ68jW JaH` )IE`pJ_kik@!QpS #H\:+WjY@V 0-29kk ?jAytDɞFUtY#j&P hZ0Щ*rp\BuA pWz[*lwc 0rk  @ JSZٙ dUj.`f~&jj @kZ `}! xV ހyzzЇ*$k xɑ])XހuZsGt5&P5 7ۄj](JƊGQXt `7ܺk{m 0VpH6 ~谮@v;ڥ( ``x/`{(Xi++j" 3Q ) Pd@ ju{jmqj  v P藥p-𹨫ټϻ K9 Ъ D`]8:j\և}} @ LY}G黾L@]@FW[-@_lkjg Y2ǩl\2Mj 00b` . {P0 2 `@@ 8@u u3tTj pr9j +qck4 `0rJAk)z@` )jdZsdj|]rLkY0s:0t5о씪 xvkuZw (xuɠr@4V{ ̐,H|l <\j׬lk\lFP |Ȭ̹ \ϯ{TOml!,1 >=E!L(T녿xE%"VZÌ\F8^,b'\ a_@!,HH*\ȰÇ#B|LEfH_/.ŽsT@7Ċ"4)m -}B^<`:u,'BRVlA B] `vС[Ќ/) +ȵω +:WQP@cBXPk̅f1#'-W˄+7c e+`~E$dirIt=:2?d*`b\;gܹx\0ds(4 tPL Ā@3#\Y ĖpL0 g0q =]S9g;Ӎ8}!췠y_Oېw~Ӛ@Ģi@ rG[n`@'>QGm}9f|Vo!>@& 1aO@uOrCu}2f55.@hcdH\L6QNF)eBQ!',I H*\ȰÇ#JHŋ2K\x(S\ɲ˗0)P%Ƌ%TJ1 Jѣ ѡ@ JիH_eaeLMC!ٳhы   xݻ t2,:]̸cQf5 0UV2RʭFdE13װc2+"c W Nȓ+_μУ;@سkν;vc{gOy>Ͼ={O{|ߏ?߀ghy&xՁ S5(}VHԄ^gAea҇hJ$bx8!0R䢄1Q@!<,^p;H*\ȰÇ#JHZ, ULG^{+"g%!Fɖ!Rwo:I=101.\DaL<)F]88`]iۖ$/y-ɘ'X /]HKV(0v`po8"s/]XP0aÈ_3#pX >KVmelI)&6nBTm g<ȑ`.m-DL1é$ğ/d_.PgCi&N@BO7q{"`UmR(?)M%B>\|/CV434h?u?dѸk覫! ,^P (P*\ȰÇ#JHŋ3jȱǏ CI`(S\ɲ˗0WYr͛8sɳϟ@N_̣H*=I)QP^LիX.|#U "4٥Ni]4zx6y6$sҝ8=00Ebn33n޽)OCzjq`_4*1s#FaqD?K˴3*WEL\z"b.iq+ J.̝#,o#/N~.r-"uܗ~U kx#@#CEdFaC gYƅhr5u -Ǥ@3ɨ"]8QC 8\`0AdEU-Ѡ =%Cۄa+2 Y: cA6xfkJ0Ny?IHy9fLY:ZXßL40tiv8$d4̠E.FHn)A&<_ pl΀N;*flC2,A+h.q$38 ư15k 9I8PP@2 @N+Jrs4lɡhd bCz%:4Edj `rjĝPP2 ZaR>5<d*4SZ308Zz\h7Cf@GB X86aeھ%2<v@ᶷAVxCўvE^~bATPG@B8DXFxHJL؄NPR8TXVxXZ\؅^`b8dXfxhjl؆npr8tXvxxz|؇~rMPx؈8؈P؉8X؊8Xx؋8XxH؊* Ќ8Hؘ؍8hh0 騋戎Ȍ% HЌ_@88V@HHG ,[(^ % e ɑ)8(8 ВYO-Z0 -. 2M @ b FV (@&Dp0 9TɊR Xphbi% U0HP0|]%9%`2 E`&x`iJ / 5KFP5V %4 +IP3@`x 5 y0`&\ЊٚḐ(_% W ". \G  4"P9 { `{@ 0 {@ {P {i`( Bȡ* * @ '#ZТʞ稊 (B ٟ4 DyHf c)U% UpBFP OeP`d 0CZP ZL ER  ~g q`@ b@ FPep UsZw bP کk[ O ^ڜl0h XN [0f2dP ҚءP{  `PZ*0 ؊Z JꢔP * 1*Y x8j*{p ܚ {PH xhJP FV0 (PIV Wꊯ` eg R9H8e F =0!~!0LP D`pW ]P0 يQ F3-[@& H@c`+d۳p۸x=ꑽ蜫d F 4`!bjĈ * P 0;@@0ͻ*Pۡ{9 #J h+ +ݫ`8@К]T 8X@\. u[a 9ېٍ)O !Sh^%@ R+@`j0 g@G Y aN Z[iO3\ïZjNPR+̊H*䈺_id@-&z~lВO +j̛˞8׻* 4ʞ!:/j y 4<ؽ@KX0Yce±`]`dPJ| \ͩdp iɦUpQP U fI@ Rpb0 yΰίH9 ĚvU ά,̊לƻڊ#ZriMU`. VPȫhۼ h%:} {ڡ ʊQ Kkӵ ΪhBPYή|+QPѫp Y !0Hl`p٤ɚGhg 8Tc|@I5},d^P)OٳHتDګXMħ;] 2V@ .nY/` Ӭ89| םݼ*Ӫ8J裑Dɩ)< { Աx݊ K՘@{B\-$ ޴"K+Oˇ]"4 [G ̊,@ EpJ`%PFQxhYG dp! o4 J,R L 5ɵQp  G@ hgQ`. "Oۊ Y-P7 p.t^HΊd0"" d] Ib f" ;)ުzi $:-\],ھLʩ:z>Y:> ` =`\  gН`۬퉊 @V`U *9 pXz P c^, e ޝݽ1 Z/|OYP 0D "$49 `H@%0D$J<טY۴PǺ8ݍVƺXi \o+XpM=m>eu?P̈́LٴwyΞB}@Α0_?_(N  Wȟʏ 0;ԯ?˟ڿ,n 0yo/8oV;%l/$XA .dCLXD7y4jl@1cNj!q#b8lT85yVY/`6uR fg(~NYɓIlgQRQFXUYnWfTSV IDFg̙i'C]Aҵݦ: :5aĉ/fܘtVMܠ w܉OfW/ę-"F`L_=~ZƩu}ZlSU5VK6!LXee:kLjaQ4ͦ.s*i"[0ԧt"t)"s1xy-r豔" 7ަꉮ/^If" )z(H~ɥiesKG=q*/ "Ut$`Mf:00;:DW0q]H`rII@Fi;`pCІ/z<$cSpE(rd1pdp >7)̠(&R"΀Dd/o{&Ф c\ȩ,jCQ-G|pC>фxG<6YAl2m34.Y Ij+f)E 8pqy7Hvb6[.Qs"S;TI\:],`"6Az|E< JQm99j2FŕQ (i|#А)QJ^sy$g9͉S1Dv/G;N{Dr˧H O%hA%O&&/`EVxC uB'{ѢEjP4%iICyʥ8Ljn|/5GEzSN$iOyJSUC%E|zT&UKejSTFUSjUzUfU[NUUc%kYzVUkek[zTUsk]zWU{k\WV%la ;XVelcXj5le-{Yflg=YІVMhM{ZԦVz-j]ZVQmlm{[ֳmo}[5$%nq{ܵensS>Wӥm[]fW !,H*\ȰÇ!,  H*\Ȱ!CR#B @ŋ3jȱǏ C鐠%S<)@˒%I͛8=dyV-"1GϙIxТM3bΫXjpMڔO @cUgҥe58$ʪwk[Kl+QaB!;䉗T˘3J[E 04zڈ$2(c :|z\%#o[վ{?۸u kݻa:u,'5/skUP0@n$47c,׊tT @ެ# q)A81҄`2hy UWc"1r,c?2L}(>N8aMy#ITгq$q0c}(\ԕ/-zH&OP.4Ӌ2T<=Jxobd?1d2c9Ds9Ov8Jxu)Wf I&kܘ?;dA)$ '胧 d'AάB_vd-i^ y?䷟+j?ިs4 1`I j$SB1`[F*켓vW | 3]Oӎ.TߵR<\ ސ 12YN=K ,V,x-l@:f<@sDgURH'4H!I,  H*\Ȱ!C#JHŋ3jȱǏ CIɓ(SH˖ 4&B4m괹ϟ@ Jѣ_T&O qGS>#W%e|H' ڶFѭ-[F!^J]6u?PHŚT`:<*./`Rv32 %6yۍ[S}]MƖLnA&yE=w W-A.t p=h^3q>Fu')}^HUܳ2&r#E_R5eAtSmeCS]aE@yU $w5n `CgGVz8h#1D -Ǥk;Hr0D$7c3N>蠳 D(<ް0QY%@B>0XT9;@MB4hC]TZ:&n YB4dGqOFjjj >~1J2 o%' fJ @g`IyAN=CD-Q|"4NC-T4@M"EU#4Xpt?H=wYlh YowO 0*蘏>7*4FClD+4LuM{-iQp3Q+߁|}D\ ]R#Ϝ !KrS "2)`vha&"]!WƓB~ Zqil@ Gl. AsZJTDqsR4^MG⦤P,O@xQtrX=pz "?r*LC8:0Ӣb.زю~49mISҪzDZ< UӬ6A"hCw;ޏXP*)TJ[)fXO<]=KYQ#P!o9y׈W" E2qH1D{X1oD^aLP3"]X28#$Nr2!CSЩNT2ڱux3(kk\z8Ȩ#}@!@ `I|tV6iP/ElUA:"oD:t-[rLk !řgݭ>1DYTteC8 \cǨص%c@s ug䎸G$`ƀzC zC{D~#<892^hWh0Sm?lw;= cS ]>`EaZx1tg,zl2F]}D| ]knb07 E֡,ɡ5cM%{vwp(_kM+vu 1;:p]`GpBAVVE"$Mx 'q%pE:h" >JD@jGq%(HFP 'L(΁blʮkiL2ɣMQ=ko']y,& 2ӨSJI%)}&2USaF{f3+sKNݡoڙ_/Fm ie874zڈen^l D[mw.60^bW=A-djh!Y,JsX0D>\`3h*0L aD (ڑ(8EB#?<i>j`ÀSU f*ȣD6A 8 qЧX[\;xb}@!@4BŹM 0=hCP-17L h6IƨJ 41Q@NE(grɶ9.Yvcw>mx{ y-4qhUOFM# gW?c A  >q<8\lgI煠|x\怆30r9׵anXfeJ+>6&Aҕ- @09O_{pYNFd΍Xv\6)Oׇx=^:WV3k^w5Lm'.9#>8ǝ[s*k)5Asiw-7:eO۹cmO}>%l/5}2D#Gy?}A7'~r^ ՏG?7aֳ+%GmeVDm {%8>/q"qp"(_ #xUn,o*x71e-Xl^tuvu6؃_^GAȃ>XS^9y;HF؄]C(QhNXO8]Ihf\Hnyku]X8F]svWjgn(cw}hox&R4\x$w hma&VXa/8(AH~^3xSXi8Ys !pKW]g8'x~guO~~؋%Gy'}fjh;wt@Gw~u|&q|7wfXsxX^1wr1J'\f(W~;]0kCxvAgrwxYű؏!qdži'7u|0wrGv4'kH`f{fzHtu.WHtqtt2Xv/ }ɖ\!)7הf~'}/'nHeJi'?~EacЗ(hhٖ/oIq)s閘ŀonkyYY6֗~i&v阙'H~ QI٨I i9>GRHQTISg)kVrӘ)YשY~'))7Jp67ɜ<)ɓiAѝ ўsQAz>aq( ً7Tr\)sX~'r¨~yyfٝ8'Gy*ע$Axrщxr7v)G! aymL*pkؓ<uiHOJ-iVLIitQ7!pdqNIɉtЍ-zlZ:Zbi؎yrglkGtgw'X!;d(+5VlNUmR\fb8g\lEFI]=5`\ \p7*l<(*Ԩ# 0NmQT#^'+N m>PcPNԅ؊؋\C0|@0Ψl|Έ 锾^@ꂰʆ`Ӝۓí\B@ ҝ- ݳm=Bpݾ%0^ R`Dp" ` - @\館 0/ Đa= elc /@@ "GB.R 0<B, @K4w.n?"O&PA T=EK!?%?'^  ѐʃ؋#>@@ͪѓpo؉ڦ@ط.BPܿ> @`ѓ0 mјoܔ@܍f ?IŐ <ެ\ ϰz-O O`__"Tf A vn^Kv~FRO73k1[b:{RxjpءֳjHD`zq{{ML-5´Tg̙ E"L9D,(5@R#۰Ε]y[_z*"@!b @-Z4A@R@D;,nCgʩtD廆3lڳP$ *0 g 'Mm@0m=[$ˑ#j_!_i`i͓v1KOTё"%*"> H#L:B-^Ω'4J!B-(`X:H ("u``Ǎ6ܯLpPTŁg,ZkRJ |(E$,S\E^t(f@:./ kPD1 1s R4lJSPD4] B">s0pN U}mINpK!@͹VgU IlzU jɦj0XI(9iRkۜε"in=d+@Ǖ5Bgb(~ 2 pv]kw^~jrab @ qڎA^;+:9`tD!H?ti2AQd?Q:c1mAzC#3N,Ac> K!QaK:;Pax-YD.lo^~rS h8TDr`0bsTK{(a. !/a$:!scV [vZB0"u嵜({hWu]]oǙnZ"f8G^h@{(fbPሃLb~,{`u{]Wuw'> D H7d"B1E){8FRJ7b%xE\F0PO+Ԡ>XSY[hqǤ2Hs@ ~X (e0Q ! V@pC&q!Ţ s+ ᨇ;P&. iÀ",6d.|d$Ou5/@u!HP\fx[:ēH*Wٳя@ A"8~?ᐡ+Ht@&5^2ق8bmc1YYC~3}lK]җ\g>)'{] 9> qG(FG $R%iIGtPǰ`X@3@d4ndiMkX!0:`џt~Ij^|S=q + +;Bj@ࡣ=O8ңnM(c{ N=/.NwI}kZN[}=/v5wq]'0;ؑ ea+@**( ~yHAjF keºw|y.MjeG\pV0VHv_zW򑗜@]&F`{@@͗ aHAyk. ;чC\[aq*者 v=G7T=8_HgZL/uz#uȃ{Avhౌ+D/!B 7.? u<#{z {;_A=Ph_BGcWxF<H~O'/>꥿iYqńMۿ7N =+J8W ^8tHG|``s@ 4=SE3 sz~0hAc?A`3 @A3@%|cvЅ rxb6?w`Vȴ ,A+zA#N 3?7{{A;7y{7pC!ȾZ?0q@ D@@[BE?KK FDK 3D44BI=aF{H?R + DD:[DW$A1|DR\0DBJ9F4`Cs:O*ÃE@c:{EDGE\ kB>A7B>{Z8C胪C,*yB.+@y*,mh@h@8*0q Vxb;h / z`iSOtzc O ؅uxa0Sxo"2 O+c$ܰ@;hJ0;9<L99Hͧ9~pKx؜M > N yICHRDD > AXC2/ɥB9P Pui( OsL$X {O?TVi @{ Oce`<iP ѿ՜$O0}tظ\0˾勔M+)0RDH8R! Eܔ٬kxt]R\ӍjSC xW|=E~ik-<}kچLbkm`ȸt(ɋwWʾxe-d "#p(TTYЛqY|"-6 V VQ<8K$7lPN&Rt# %@[C`S3 h[[7P:- ݸ{O}XBoC#<)pYP 'tPyx۳[Y]`s0s8x09 rDǾx{']x=[` `HG,vȂ553Cb=VH֨;h- D؜i @[?VMWqr%[ >)wxyBz_=Ͽ..髾Y3>2NO(o{Ђ싅$\ͽ G03p]@W[ a],@ld,Z}0Q9893އ4Bi@>? l^ַYppp-,!!X3=yE5c>0܅q0" "aat4@|M)}t`8aX#]tpocp~pFooNql6ȏl?pp\l!r"/r#?r$?r 3b&_r(?$r*r+&rіdphDu (2HGcIHIOcs,vs8osB[<؁l[(M (pEPn| Py#<z) L1Q\˶oJX:3s+O5HuZw4\rCDtNMFGD@PNy&uBقnح{[Nۍ|9P#}vp0 0\ovhav-]BuP[-P 5l۶nhz'  fs6h P 6t6pzoaFP{z iim /=:OO?g= Pn۳4ŏz*Lwzrw9Qѩ M9`MДRCAEȽN$cEXߨ@0+"]M7t=0!(SGt@d"-2d)h"9Lp H2u iI@QHO) h4mqRM{ 'ɤJA"weA, [ +ȵ-\X 6lB`KE{O?cBXP dv!a1 ĪkٳiK1gVo4 pMFg"*ua&v W.] [0 M`|qaoָl#Olf&î8Pn9s>P }gor .Cq qw{oǎy/ 0Wc  ~@b@TDpwE0DID)"X҈JDHHE/[D^#hI? 3<e  LHSRѤ'9T /rYtO`s:9B@&!Ke:6`&%E x#D$"C{h>H'= Z!Sd') IB;&: HG1ےT 8vVs9`'H6{~WNs`  N6/}#"EX5ϡX0cxOѤ.8.D|\JIT!9,`_H$'a p3?Cpa ;`0: dgz~E&! PD~@TI & %@AL" B4#3E2җt)P_r[O.@{H!ƠD? Q@PM)Z:u)VX >feY#أt4KX#%(@ 2A\@{D^ H*$CW d 3(@;Qֲc[*WYg 0h Z5Ld6z4=msJ mR}R8\ ɩ-8gƵ;88]ss),"'u|NrO~6́?I~F!A0r>}PS>\C@DKCSР?24ADfyDDAbIAd!,ֱ,w$h1(3BPª [[ z !:QxF0!/=Q `m unI.`8 uУ0eZЄc@ƀ67N:9SXsׇxc5z`F4]{zAvl`Xs0Ϊ[jȚ [Nwor1qro}OrX4I~Zp gc!.pNM~\~/Ω9D{(.V` bHT 85~b_q|8yȇ;|~7Ӡ _0}V:y.b=T1gθדJ|&'y^@>w߼z8VPy ?:|>qǜF?d%8YeƟT<5=xɓT@ ? ]ԫKanpdW#/;gx-ք^DY+/v+w~9Eu`0+/1cCɯiQɝ^]) EI^̱ΡS:! 1__ٙaf_ڝ_ҝ` ^! >̕ a ^N.aUFN!V^!fn!v~!!!:!!:!֡!! b-Ȃ!"&"."#6#>$N?l<(SA"'v'~"((!%)2&+",Ƣ,.)֢-Rz,R}͢/"00f-1aI>ZH3? #5V5^#$6n*C 4܅ڥ6:#;!7#<&E*Tc;>#?ƣ?%?$B&0#Cn%UVQJ%VUn%WdV~$vX%BY!YZY%\e0%]>\%^"b]eX_`%`a`&[&c6*&dڥcN&eed^Tfj&fvf{l&h:gfRi&i&H&kjfkƦ\&m`9!!n6oʝn IR`edF&vr tjL a pަo`s ]Uq tWf'~f2h:Tf)Z 'ZI6I@djd~V(CI<9 @@C.T U0 hG>.5_0ZIhn6b&NuZ#2JHx(&"НFHQg=ՌNđ1bH)b䐮@x9C<Cl2:HL=I({H* ė©z-D:D ġu3-, 0էilè*\X&<ީ20B(w?tjLI"**i^/.E.b\FflFgkv+nԅ9jydy0tm>I4EoUўFFE3aG+AyDG4L4tԴM4FGDP5QQK PQ/5S7StR&S#9Ѓ3<5V+~f"Ef254BEӋq2=t}iÉ(Ek.quיq큵f29@E59B`5FCR)E]^G5_SQ:;1$1;B0qT99;qĪ 2/ypghw[i҃O͒kkklp/537Z@rHpi#@w:S7vnhl7v.Rve;&S`Ox8þ L܃Ow9a` 4 Zr d^9~Q^wTL$ȩxU1yѾ?^._4)o,s-KW$AM@_CrW=}zGᮗ}fas ?o{ C|WVfVmyZ_}._~W~_APP#P'+/3P7;?CPG OSPW [cPgkosPw{PoP  P P P  Ð P  P א c PS P Q '+Q/Q7;1CQG0KSQW A_cOQko!',pH*\ȰÇ#JHŋ3jȱGXHɓ(S"o-cӡij2EG$9#x+OEdق iU8BgjuҗWsUcرeDX+(V `'r3vSCd#5 L:rAߌ)[V`ۆ~b R8] 0c`n%ܻ n-Jtpô3vAس%P7z"[.sm Y tCJ`)dznqmsK4D3Ǡ;#c@/HCO7lMql%vܡp ڊ 4`zܗ~)A1d4!X/ěTt3UJSV9r 5G7e43$@K6dDcCHs!`᚞# CD@[veB}(\eT4u iE4LЧ$ `"Y PPfs bF%EЬw P) BGBS@GS9@ 3asAd.d@^#9ZT $04"Ll߆!,l@+*Dm(@ P2x+-ֺn$0$@,ç=j$?MADs9O[&t,P,M =EAm!E Q{$MV + }-7O-۴@t,?\ KƠ̇cmt㵱aʴӁ#laM9ųA&Ē 9S$A1v;A*d@ 0˅n783Cз'T3pM0'ڸd|C3_/C|]A7gܚ1(Ab,~IFM!&$PІ/  V!T.V!OVXΰQǡQ@`:H{p G$2T4#cC80B-BBz0R$X(A5ˉ:@ؐfMA+(krz oCp xCH$𨆐  8pqy;T"!),cBiAR&Ĕ .IPJ$`ǤHH.dh@xH&6Az#f6<x1X kd-%)bRf;+)́R @<ΑJ$DT4pt hD #G@*%'C'P#@2 *Q iB!"MTxs%9hEqsl$!,H@ paÃF!,'e ϟ*\@#&t(PE/jdpdž?(#ɒOreĖ.9q&ͅ Ts'O7@PEMTRM.*u(!,0CtHA[/…6ŋ3j/@= 92ɓ'!,t c Hv0p„ >D1bM*F QƎO#I4 RH(xJ SSgI6w~' >, W`d` >c%/&2@+? !, h H*\Ȱa #JH"‡-jȑ"~C)#ɓ('~̘ˁ+MI2fLr@B@wI PmYiɣ7 )ԨrVuzu@~xn-U)^,Rua赊A+v2ٓVO| F pWaakAWrE?+KNإ_ $6+;p!w I~&ɫsP#Wٸͤ`~d ~ֽ5)ׯ}{SWsP%1/SN:( QdMxOW> d ;-=f^?V@!NKHф$83_}Tχ Q!O9!$> aB㑈&H61:)CP2?Xf\CP`e0ifbY%pbiƙoy&| 'P)f hvIhla]vcL0inF$3h{r= `)L<~$LSd0:2륵*3rʚf4dά]aO#@+-fY 0i 9ma!,2: HPÇ2Ń3^X1G z#MDRʔ.cr&M6o9ؾ*:!xK&A l)ѤJR)ԂRIVV\۷j|K.Yu W/]s,.aЖ@(HcYeE!, H*\ȰJHŋ6ٸ1ǂBIɓ(S,Ѥ?0cʜI̕8s#J@ ݸ*$=g(YU1I+J^Ɛ/e3!J0֓,zT{&iدiL2$F?ܳ/M % HVԲK%JRF "}a3Ջ eD:_#LG./DD.Ă UbHWfI5B .EHVсt/!~OPFE?fc?ɧdp.19яrAFIZGg{$mPAetbHYZcK`zM1VXEޚ+tDL]3,GD1Lj^T&$YIQ"*XtpF/eH!VAdQȑT-D?`l0ΖT$`B-P5P*<Tv@/FP-T !EJtnPMTbŞ8scL3dݓgJE² e @-5"qn"՜D4C/_ K#FR,79Ϳ8ם(y0 ! ,oD l",| :Dh%D %N\P#Ƌ$rr$~&A$p%˂^”2L6Y !,pq 诟A 4xP!&|da& U|5nҭBd!J!*|)_ˍ!m6Ν]ɳbFA'424SPhݪ!֧\ !,t" HA*\_B:|"-VĘQD#rtȒ GNDip%ȂMbztr`LUxF I S|dr˗3h́VDb[1>SNk&^{zגKg:eAp.Hz 3d%MlILd-kP2 !,iY- H*\Ç#J诟C3jDhƏ #vI#I\1J0'|fÙ6s2iQσ*R!,w 3a.D q`921HK 9dʕ,G|̔5!,N,]΄QK-R8/* $ß*PYox"ӈ!,]'A4``…HD^ĨaC; ǃ!#~D$K. )"ͅ7G\RO?%~4鏤P;UY(Ĉ !, LH~ DP@ Bx!Ŋ NQcF+^h0@&)LJ ypJ&YҜ%N }!, n *\ȰÇ#JHŋ3jȱ#Á=Iɓ(S\PKYʜI͛8E ϟ@ 3УH*]ڱ?PJ)իXj%iuׯ`*ٳIɢ]˶Lnʝ.ݻx"| ޿4a^8k@!, H*\Pa#JHŋ3jȱcdž CУɓ(S\1B:Cq<Ȳϟ@UK i4ĝ> JU"0̭LT(ɫhӪ(𨏧b(5 L4w`TE{x8;Ӻ8J,9VΨ 8(=ׁFU@Q=9S;hܹ9̭ܥ(tLV9abͽ;ʽz Լf=c*_/*}Rk[~dx?'Pomd@e A9}5wX=`n(G~afm97kQ7Zf!f@[ i^\GBWgx9T6{8>j䗽Ք lJ{Yӊpy gZv"'B{ZU@!Y,P H?*\ȰÇ#JHŋ3jȱǏ Iɓ(S\ɲ˗0cI͛8sɳgB@ Jѣ{"cҦ虪0S|` xx!Sk 3E N^XF6=+T-Wװcǖxtڴ8 jᲉ#D;oOK\#艊wn#kRG֮V÷3DIߜ|x֩gr/So[}ygT=To8GX5`gn[Ia\Ru(@Չ0~nՂƐ8И`Y\bB>Ψi% IgyI.C!ƐTI*ٔgZ"TA8pd=p=SqVUjP-(蠄zA~RUuKiGQeV'fBP椕 ]G)vPQ!UVfp4GO+)A끿6Jٜ J,Fiؗ2 .hC:zUv^\k[ղ&X\,0ۘ7et;ԭE簹~-zra~ѻZdꀄWؤ 8 cNW;r(\cdu%dniti$S1saR]%oc[JӾFU15*fr}?bdG߀ͬVD&sy'*rt.Rl9#SȧYUh*sN%Ţ] hl .v9t7D|31WQWLKe<ꔘ[o觏Pϒ/w HL:'H Z̠7z GH(L W0 gH8̡w@ HLH&:|%ZX̢;B BXAo4A:4"{#/ -4D #ZB"]\l$ #92")&,1=(` iv(=!4 dCNP>!TEL| qIS%C ȃ BędHQDB%1M~D(2# E@y G$wBzPCh>uf292@> BHF|XF9C`BjQ@8D"${+B,uc ю|(i`D!V@6f2ֲ;e0 @!r-a@lu+\K[׻ laXlE&B&p#($Ӡ6WrVnhKڹvek͋*,f5{_֬ 8EQU?! a|E>@ ?A(aDB;#@p"!A\~RH>@5iOӠN5%@pTH"BT##{&%jS\̆p WX@o" HX l#P4Xi conB {񋄨sqG<υ>t hAZpV0> `ҠK{җ}s,Y~ !gq*C(! @縧!+ϞvϽwG)[{(p_} >GH ON7 ?Vo `)(N #bEWq#EA_` !<%6GtKdB`JsהXup75 cHZ\U{e @fyS.yFU@\Gl5  G@KR 4h8:ȃYXGKX dX^Py8Gx(D\|Q`(8e'| a P ?pV~ ~fR2Rr"w"U'q)br d0Xs{psJ sM'Pt@ B0CwJwtIdtWb@[ط"&pZpIp]\'wp` IOXYx (Hx^(ȅx.`g o 1ⴆq  dPlthV@  Y5v^!;ip[\|C#k%u @a@Ax~(|0b@Fb 51F2 QCC`F0?rfDdE&8JdMƀp aee]v`fs P)d=W q0 fVp Q@SYh°F`@(Wǐ %P10pkDdpNgٛ)gw6S0 1ɕ Iw@\p uxy%Sp&)rhƜ9p癞В p Zؠ0I詞9L9 p 7#?0 EqPQ~R| RE:r(b-嗎$MxSUU@;Nge:T?8'SUC7NT{pn1d<fb` :]z X P.g S۠Pyz_  8@ z*fڑ| N<ױFp2JI+K۴Q[AL]R>& װ>AApp|$B1Y% KMT`p\з~+z/w{kQ>{VDOtA;[{A=۹۹D4?{빥>ۺ;,ۻ.⻷+{AۼH;2K؛)a 图{ۼ ;{K{A2SR\DqL \,i<2!I?1)ӈ.>X>Ie!8 p+s@+MLf*'ZZѓP[ ܿGQ@`:<,?tSBpn\ܦg1m*K`Ҧ{X B"jjG<†L4P c`qȉ3܋6G?wW=]5?&5AS]%+rpTA$ ?L}V˻ %MTp̈y -ǻd1 A4λsHW  O`(9<#=v 7 1ڼT=tIǮp + ]<3|]LA@p0tHMB`iso-;M ::  -ic|= do֙-1, +؃ ,́B3Ϋs)V&42Qdں}@}=;]E |RqSxQe4ޒ,],=== uL~/|;)!GϨ¢B G*Q[ +9&!+]o"A5 $AP Y}AK`K-=qJaL, &'umߪ}^:E^,XH2,@-`[PN4O O~d>b3/P)Q F%!ߎu r1t0m]eA~PQEQ%Q7F(G5'R+/%!TNZ> / t&a_  RG_'\\%%Z:WAXXq^e`9 yy!a)a )}py tsp=; Qn->cb"$fb(bb/EJc F)7;P@fF+α*LKHPV o$㍌Ȋ~fjs1lƆllmzgG\ohiFio6d$e0e`?_&0T>6!ߖ>y`ZR`q$'qgqWr} !'cEZr )+rs+t7{WuX'g>O8 p@ t `zzzbgYG@wE0p3n^OmOڷ&1 ovu0;psayp0 ?H/d7k"#/","!D@`Q AmCc *X{,!A -dΔPҥ?KA% feA*]y(K|TVLk?tGrJq1KOS'D+]E;gܹxXU^1 I0LxA~8xO  P%e1hԩUfkرeϦ]yg (8a!Fv2yrb\r58 x&`P1Bi.!\F#)Z-&¸Ъd€s41*qE& Tɓl tdf)4lK0sL2t*:<7;.%fÂQ$>{("c飢Y$"p!OM@Dbx?O,RUu.eNjX9N,j\ZsX% c A*Jg{L`)iY{dJ( ,Pb:(VlJb*_<] [4LVxaaka#xb+b3xc;cCydK6dSVye[vbyfkfsyg{gzh6hVzivi~8jjzkk:l'lw.A$ ߎ;Vd饈Ȣm}޻p Xh-JC(!·"sJh{`W@2Zg/UtaEfΨEXy-feəeŌXg Wte ͨbx\fWǏfC e Zxak1u@Of  4)g+f@ ԦI.h ]Є` K8ڈ 2# ƀb=;-L03($ \ `1s"3B 5xfւVw1/V"Qf^XP&L DmZ(PhLn# z! ttZ`ZElxÜf&ތ>+Ȃ8LBf*DHm: L3,$*9_As*8$hiLЗ,%z3 a-c&crtk/Mnz؃Jh3pVB*D+a_ӞLH3.!fD ~6t@z+8H3ZDf+@gFQެ gAD`' 3cA/4f/ Z;1ӂff^Lai͞ҙ/@ E+ ' j += 3H!f!`Ԧ>5fM`-lyU3#X*,2S*S*3)XWAY0,$ځ-Ă,HQlH'\+R0KA xفPbD4m BJt{ Y2D(al)-{~T8) Z: .,c[m-! W%eѬ\ 2,^2mfН @ެ+bYP`P" m-C d+oģ<3Vtf.gY0ToNM@,4;,J \` Z-pBB0(!ax8bo.͖ 9)gbȜeB-0V^s̤&!.8M20B,h`V<{D=Fs鍥p9Wh.!Lc…3[n )K`vS4.Sf5,g3Wnjk]k_cbF+B J]:/>;% X3'92B.,3p8A/pVH `gv-3{/F_׭GBsoB[̼@4A8cU@ x.d@3% 63',aX x3Z\@Jj󞌜˹:b^Ű4$;\DN@!:d$USn6XGI!ӥ4Ri!-m'G٢MՁ&Д&qHLHyv'}ZN-$1\p+X[38?W0"s@=(iLF\Yȅ3йߓTˤO\1 ڱX\ȅ5P˩Z@3ek2,v 0O ʴl;YYj%0! UQYhQ%U\XPZqƟAYPKQ!qY";3GOB3 1EiSɟ\ӦSɂ."WSӮARSFu*H-(JU&T)D]T H$UqT*lATmUWuVW͙Z8?QU]U^U_U]}aVb-Vc=VdMVe]VfmVg=hiVjVkVlVmVnVoVp WqWr-Ws=WtMWuhWw}WxWyWzW{XW}W~WW XX-jWMX]vWؑusew`Wpp/V0 tX~ijXVbX֕Օ-|ؘ}Y kX=XYeXݑ_qwӘ5WeZ5~( ~j؍Xmm0ْٓ凢V(Zh~}[n=Zoi[pٶV-p[EZ[֛V֠}\ȥסU[m]{{؂ u5W\30o @yiZpXq-[\ WV\n]pl5%^kUV}mȭ^ܵu )zh3\0^-W3i-ȅxZ/ z` cX bX؅uxa j\0yH ޭ E[]ӈim  Eu(g th|@tXnhXjuZ8tpc\abVavk _sr`w h@"^_&~(F `&` ` ~6baq0 p a!fF "ٚu1^^,Na-F Pxo`8Vc<a?⨠b+^@ V&vb(b>?3`-2^ǵ^]Z5 $+Xh؀z(1+0W Pf/h8.@Xrh q _(0i P_VoXiӸY v n h{`i`0V0cPY݆sq'8gohh`^j5fdui@{ Piphh^.i 8>t^g}zln6F p (df.YgpjjNniZXi*iFcF geeV^v `(iEmȞlX_ @rk6WX ob~#vzgcI{eVh1hxYu`hZ`gP|hqnjӰ Ƞ plm`t@m0h~f%FmfFݮm~lv)prX_f` ~@xd؋(5~omj@V&wpE 2Vgj̭xmYAGEhOr"_  ޥgD^GHw s m3w)o!7 pr>7- ]0 V?kuC"8z_~\tg A'u6Y./w$GOwu hV91of>zfӈix i1l*'1s̛;;9lcѷ83ZiagQx}7PXg]\'ĥtgk2wJn't3&9XO xoNaTU&I*$Vh9Y@s_-ND :+j%a9&cJIeiY)Ro%]y=rZ`J3_crULzO>) ((J:)裐bjuo r*TtꚈW:*z+%kvs9J:i7L7*h!,J;-Z{-5mZz-;.Yks YZ&3-I?+Rd{˗az ߛ_ 0,ZkpP ԁ@sܳM ^l}"ˣGb{w׻!qx;^( @$|axu{%$C~Y̹k'GB7ND6F1,p`q(@2vaE0 TiF㑑#!)IJr X9泍pT@P! 4N4(%p:z1)cb$L1CV%1I6[MnzsRΈ0,r/ɍ2dqa+e9IzR $1IP" ! HcAƺsjH)Ozҕ%3IZd%`Biu}bSr/ :̎ d&/Ɋ@`1AFrZ?ff#l #:$89wfk? zy W ׍8XjVˀI50[Sd"dʙK3Y@:ɨڶ⮀I*âSk!jUNΰ@*&՞8B-&~=n:>$p+]:եB TEg3 Ej⵫ k_EO{k\2׺6U._"WamoX~7칖j9>ɩH, &XEt b[Hxux}3pa!(5ݚ2"09FD"p>e:zB4ҡy=2z0?']G$-G7LpD{vTb -x3ih[@ 62uXc{Pq\"3d*1)<op]ɵ!Yt'lߘqD1]Xb0氩sjA󐋜DOr9xP|b(V7tQ^5G,bnW$"G3[^}ko£baa2F\`(< fAگ#6c-}$b!MHry;l  Yq%YU-oTWT@AB;wPsF/TnE>#{`V.*cS-6g[#!8^`b-[}L(-y( hXHᦧ$!lCd[ vS<)Ki2/H"`G?|"3FƗ7lf_? [d;qEN.Ϳ3INx#E)LqJA*/\Lyq "M}D$ qD_O[2`㥘EE_T\iyeWS`F  ]]@@E@4̃=t] ȋG%Ȃ,mn`fduE\x|Gc8dH@ϔAqrrH|-0Ȁ ! @yY;L@Na@ ҈h_ EhؠjF@mGUaxa b"~h_@ %RݼbHva6;E!n Y - ɠLK b"0jɟ .B 1Ĩ K3:#6bQdcJT㭀\#7 4ʯ3c9 ;rcM;;+#>\9:K#@@$AA$B&B.$C6C>$DFD TE^$FDn$GvG~$HHd$dCdJ$KH$LƤL$M($UdKOfdMP%QeP*CO$S6%%TFTN%UC>E IYl줬8Z%T[%\eM2REIQ-*1RY/u( [aj\&b.&c*1Y%YHe̕*b%Ja~Z6h&i&Z eYH?XAsE5Rz&hOn&oMjC^X&Z&n.gKs>'t$jFV^GPXYYԦr2xbdty'HngGx'g.x֧'~~&$l t@E/΢ا~&.(G( N(*h:*F&Iv~脊P#((*rʊÿ`Y$T(Z\hYG'GQ i@ƌˑZʋfY@ Z &B D1"*c ͓li@ ̠@A$@ 4?LJwr`#E`D_S,)6i)> Z)bi0ppx)Z4*4@ @MQ&ͩ^?KY8w&AzVY%YQ-|lVQeY?LP *~CYdyCVV~AREWOEŷ~ +>Y ?iYTl+Pm~KQm )NT> 4 0DM88 O & l4hZt Yl^&ld xq`f?l&YLmYHUj??s\Es2o:YYV9qC۾??xEh?PfM-ԍoíY &/r5p=/oxm$C>ݶW~ZGI| |e*<|Ehl$'FFrή_YCQ@G0"1fZћr?DYpjNXқ9ey??CYDYDGY&CJiZ 0kkvDqq2ˈI1Y?lN??Gqk!ޚYDK'/ )wWX%@Er0, jgz*o&oA?j2 e1)nqw&CYxYDYۺ~e֪?[YCZ`ev1si@ gG3";<á(oLlsYU16e.4->OЦ 3s\[ @1`Չ[{uZ?Kɪ s1'2[,2LPO@v~^A 4+_~3%/ts3Y؞Y]8k&DG$6z-RR"Q>%Tx&DP^'5Hs:HvjsU1DQYȟX\ӭ?Y,_A RN0,IfZsuT^l|BN)g1,)!#SEE]EsŏXf!t~!ix[gZju xb+Hx?rw[(zgYԈ2+&kP(b72,{wᄋ&w=lC10':$ &Zpn J111SιجOC2⬈@4ˍ"kK(qx*qڷYx]i,XJ=ʋd'+ $䚳虛Eo:d n19j;A  Tʘhsː~d2cƣCyP::Y>>ɢ+iFƺE׺K纮7ɭh6%=T:'/{c* n44@BN%o;wDDNC-/C!C9PAU~;ǻ;nkvu3GNC?,?klG::j|뿒ź&W'<ȇ<.K't,Cj .v :P뷾3K3_IpKJL+7t8(y>Y@@_?shG>8yElCIK#cDdz @D 4xaB 6tbD)VxcCtdH ׹y.NdL7 i̜ =FfHgDZȥ޹35BSQJ=)5R|o-z4k1Zpsֵ{o^{.N/;G $.278d\qB *gOȁ'c̹r\GlrϿYvvlKovnќ\LA}7~B =J4OD}H/TMI-Lcz:PEMRM-UTU]BNwtXeHa\uunɍw`nbA%Pk]}YL"6Dcm≸iYp\Ue35kCVv{bp=jzuܚD`pq'uvAU:as}#:#+Й'َ6aޒM3_Q"#?c`qZt_+0Yo֓^͔(hPi~ډǹ`jN߭ZfL~d(;9[*qnoa{fuƍLy|n֚"h(#M]1eau|X)sv)f^tL7uuGץ^Kp_orqxeO!bLFzQsYqD9 pZa]*`:>. &[Փ VXlשNA)T YB1<قhXCO6̓>A$D#6DD(qN'E:QY\E/~Q \ɈE1ƩitF7PXG;yG? YHCT"HG>$)YIK^*PINb(IYJST! qXUF2E ]r Pd`B2 ^P/yYGbtlA/0G)b!+dqjұ Jc8=q!.I|R%V@'&hX\$dY I쁓BP$ :ӑ,,_Ѣ`:VA%`\%=]p #zQӚjtɤdnjԥф4y=n 2j-4 KBԨNU*0̹vvg=)JS! ! Jӏp$A'J` 5DX٩U|+tQ^? NZ,DP)PR2XP HkCtlE3z% W{$B/r; b;DiG9r8y܊ }*P#Q(b>;a%(D_Ё0+mo Ć{򦷛O g9 G,`s%\)P:u$l9aJ&+X\Q` b*T'MGx{%aO} \n%`PS?rQb'\q8 \Pv zR:ގX-Hxo,Ёs'c8r0'^,j^Ϸ[?G#=Sc+v^TN1oG3T ]1thv~_ե65~LTו%hkT͝.AR M Ύ˾Ə:` PxA: fK4oTjVBp; X!nNMQz!ͶȎ dAn nb\`A mOoR/PPaL)xᛦ `D @ߚ@(:@(a ]OJ.vJ- ( !IntAxr{x(p;/X bjH  ,NJ@ p @qߒdq Q-`:( J j- d Ԋ܊ [-k-n ŒҖKiIS#AJ "% 00ZM̑*nxBxx' 1Z 8B`)l` Li(Ch vdw0΀bl`! GJ B@|Rhv_-q'+f p茎fnU)髞 0K*&+L 0s^i"j81( ^S4k%=J J Ա)i!.S: ZX p`]H lpaӦsL+o\ `,}ʀ l ^A-0.;@r+/H h?t9s= B@>CI*VsDW4` rLEYthG @fAvT`B3TTHԏX 2XaVnIJԑ,*a4d5LdMB:MNtHTNt5TO45MP]NQ.QmP :Q]&R#U[ TC"f @ OqTUXLD&5M5#UU?V%W+TqU75V)?ϠCMӨS^4€!, ^ ϟDB.T(`Xџƍ=$Ȳ?UʜI͛8sɳ.fvbfӆLJԫXfr?9$]TkM+xټzߪm܉h+Va߹ fȵ!C;2`:hN،Y䚭|93޴ZӨ}fyB"skPn;pb*L"H;Wuy7M۹wK5-]*+0vP(Yyht\}ׁ{Q鸣O:Ēv ^|A4?J8n9qOP|(aZl).˨f]aCR 2`D=x`7&x䎰FxEX}fFPk- DCDcHa[9. 0kB1M+ ŢDQE 3x!:IP, yf 7ƴ =u63 1 M "L1~eꨥ)*ijviD!%v(jvP B*`P4P1kZh/hcdCAC: j*滒4kQE<ӛXB w(tNC %,,nL?H( {0BQУ.!`9M<=O@Q MS *$9݁Oz?<9$E98|ۭ9;͞^DCCЁcwZC_wg(?%߁ia 7)x#d `# 7q ta`>j!/dz ;A٩d[YbDNF B] "vxCTn!5y\bi= FDcG"ؐ'm<BD#"эmqЁcA7ћHNr!!Rwc ԟfGtG(S` B`Mb! !9Y"Xա[r ,k9$[QIMBDOhF.\A),̌!/3Q"&7)j.ęЄ8A.mPwŒ1̆!Dd4fL]r!`a '*A \lސF16#z,8(I…v܃ ]E1y HMzx#z/atPua)8c+KmPjT(VZԣB;ĜT0:B֮&R´u5kV&=TjU6~kfW.JkSnRN(QB"18dwr [l&hgKږ# iMZd ȧMт8:w:mL@[Tw"ߘZru#ۅxK^6Mz|K ͯ~LN;0| L [7",oﻚD</Ad̕.g,x;ȏFF&;m w?e h)/>2yL(`" *vsy,砤Y0n`z09F^ /mvPh@zfI%wfz%3yl(mVnܘ'`:Y HV3͌:p^ ` k_vα `d }t)M[ߺ& @*W^,ȅ Z#AO @b0 zhc.aX3@G;ѻU G_?yǔ׉8 XyH@> cEbbq`:ѐ @,q3g @Šses2f|kٖl,cPhkyI|A77vT&d-(g8Gg=ȉtm ِ{c-Hm7X#锦Ghs`G;m uFA vg+`L|uU^*,.02<4\6T>cҹ0 *- `Y0)ҐLnP>U~xUSp 4F `۰h5) H-,A>>S- ^^I]^n4nһ`k,ꤞʮӦkIa잎~~~.N֞^_YӒaN~a_~" ?_ "?$_&(*,.02?4o~]:`8@o>?|FO _AyPvmJfNR^aT[Fb(gE+VdcsW^ޖeagj_{_a@_o^p ;-kMze؏%'/rOp)7clv_aa_}_O` L] ۰57M bϸ vgcoƟeƒlɏL5fT_R f:à q7WsoqqCn?v{7̂?%r0]. ݊.!y!W/0 A,]| ! BHТn^0;~BnƠ ȹgM5>+ㆰ`Tr-n> lW>ܼdhl UsXō'\BMgs_{$]iԩUfkرeϦ[ 8 hLZ ~Etω? +qA`V*mr9>.m*(4uo|e*iΒFA;. N‹"Jo f g+P4,K=X:g&k?Ff,f 0С"-@̜KhqfDksI(rJ*l ~Bg)~@yñWė4S4H|'q4RYAajslZ)QFGpP>9_BMvN$R@) Jf+H%eEjSeNZyb:lT'eY^ C JVJ3WJjZlm/x } s1x[ }"tԑL#tm4H x4  hzo M " l)bW]v=09m`c)e/xXiLGYe/1^MGe]} ^ivj,' @"pb 11ҜY " Bb&"(ڑlSSO> F8~;!%:YmU1b˘bZi_zD6LbwyE>2d3fzsY6y-gt6}^,H[:㿰PDuaDsx˂S((ui0TZ-L+NDBEjD] CGy|B]D´ch)4$0m(h7uaIsK7.Ÿ*y#nu$FΓƀȂ$B\<Ȅ]hr XuH4ȓLɕTTI8#TGcw G~G1f0mXb4b@+ i fȌD+THsHHȗ|I4KL˛tKs,K#?zK4D{T9|D+(HR**1ۆ#1*4]v, ' KD+DLU3Ec̷*S*.SkMuMּ=@PȔMC̻T$E3(XJL̅D\ BMsL|Ä\M$OҬM=LDȴ4|܋O0- D^4P] |P%>P 5, P+P ohP=ѻG]QuGQP)QQє2+Q R!+RzM$QR'}R(3R(5R+җQ,(*O% SzdR--S3S5U)R)4 &]S978m.;u;1 *TBM$BMTEUCE}THGmThTI5T$>%mѹPOPP UR@T MRQ}UUYU:UTZ?mʽV`,ZkT]5ֹBVrVըYV^ sp>>2)> o\NE=#c٠.=6Wb~ v V3lM!l+5[V\fepC--.h7l|4be,/I/dC&,]G*BB8|_6`-.)v mq¶,Bbfufs0(@1xmfM>h:tCteufgi;qXd5z*ka= #ӆ:&FG[dpT3%:SvI4- ܘV+pEHhFN?E`F[cL@F^.Ũƥdr,j*lFrEs`jEvjh6hJV++`wUkCcrh٘a"man' pnEiuiB:cKlsl+ʴKQn.6]@ꨄIɖm\iBmd+İLʥdpʲijٕEFJ,`Ȱʮ4m6vlsІ5Jis omV, E(cUٗ+oX!-(~lCs>[Ah"N2]Ns p!BFJeQj$f);.8e]*&y|Uv]jXML6 FYu`^ޝ1܆Е@5`taŊUX  L $W.-2.gX1 _UfYAMQDCSpЗa*@cAa%fI/ص.qXg*ü6mԷ2dS]I3s;*7 ۅ~8йۭ9 pX#k9#9衿ʸ `ҝ:'>;wQ+;n;  ~<+<;<wj[=k=+>|RHC3A?uK0 _6aJDyO#t~n⿝ T wx VLH6 L@ ~P 7 r@pЃK^me $ p(T&-)CMO #P!(DJt|EǓ.vW<^kTVWi ӭ;lRg[Oyzȕ VU"09D# H:16mR~P!+%rG4^A= [UN% أGG"ܥyS; b@~tn?8oֽ-ACwh;i%Y=/~ m*ɏ{q2!ϧ#E7#ß6}[`8Z|GsS]0AfT;8 F8r#aЂ h$G.f,~TPhUW̱@69B2o%Kn|}B}g*"E.ǽA$Äݟ;xƳ՜aY(]i2anѡI4F:M @!U ?4R#_  @!zI jTT9,?IC}ICm9!ú Ap?x=C[Ġb"ރ!"0bs9 DaF6(Lxr}B0?ԃ6LBX@.C=B9p373C=B;[ٌ4|C40u=ԃ: 2>?:;H$ED<.RD9bpy@ v>#KC$eL֤|)B*$C=.|QdR':%M9"z S[#]Q$&J#QCP$n?=@AW"%Oz7B91\ALBj6x$Hdx=dbfF># Fj$3`c~dHW"DJf?.D6LeJNJ;8Mc`.NeQTemo4o>H} ag5'^-pNgT:N(0IZUv͹,GAonN'tmjgg}qC>~g'Tsgx>e}6u"Dzz'zR1y*$n"tNDhNv ..D7\M}ΏwƍGőI٠FN)V^)fn)v~))))VEƩ)֩)橞霾iQ))&f6>OƢN*B)^*fT*&j~*v*i*Ϩ*i*~Ϊ*i*ͬ*i*ˮ*i+Jɰ+~i>+Ȳ6+ji^+VT+vi~+:t+2j+h(ɺk$\iЫ{T+ʨ҄J a(JD JO>,|&lxZDAxDgmI TN|BetʳG8@\@,͆ F|[,OX^tߤZKDPLഈhFOVmgXgxKLx\GYӪ[d]t^IK`G`h6ۺd˾Tflv 2HYH1̇51,Gϼ <;Z-BLlC`opq`DrX%sGtut wxG}dz ԮҢ,Hn@@^FdADXpXG疯XSDE}$xx(`HapJL sBv1/CpKGLPD(Ȭ*E4nD\@] 䮻 Jֲ^ZCYH4hC0ʭIa/ Ġ0qdD|<}pf4Ld5YH5hn ֖ lƊP ĮLM\X8oa$F0K?K pddqMP05T˒1[t*ű2jܬJK C ݾm̄ 5 H0 _'V8 l CU2SfP A+;~R}3@C8DXFEwb< -n_BK sy&' rQs،V# p3op)gc. oynXǞZhhK4Ү'* C UO>JsIg@lhBm5`#<ܓ= %x[ PBo@c#8@|H%J-L54SiZ>ERMOA =6l8PeZmWZJ`X;Y=deQ}\jnq ۻp r=t]veWx块z|ݗ~ >^~%-5ޘ=EM>U^]~emuޙ}蠅袍>餕^馝~ni}묵ޚ뮽N^G~垛nF ڽoW"cok%I`.F(;/\RATpL5OW1DWauߝ%<##6!F;YyIY?A&eR(܅8i"jX ϣFA$p((@c(@ex5Q. N?lzٛlt/$H Vg8}-X۩ȃ%>+^Y>mF~ի59̐bB'A7jeo!@7B% x(]׍n^\Z#yЀ@9,\c@'H%1ɽ@>6:Xd#X@ROK9)-&E/q~5p5GSVǷh3t3x5L9 R,q]&XHx 57I0A@Iյ1A}SȰFJ{+tSRWOTZŒquүf47v3Z)5AppRHK7I%"5 =3J0Wy,hXR* kl/X±gC )(큮 +A*mT%]IaqKm߅l;1"ݽ,ߚ<0eCD6/ K}_iy`i++ |o|y^2B' RJ!eBju0X"%H(ms4^89qB)6ޑHX% x HqV ygj  ]hCщVhG?ґ)]iK_9iOԡ5iSw YڪY@#M*Ai]kQԹyk_=lC`BVlg?[%P/P޵l_]l\`@_ Jp*(ak! !<0ar;E/(^q(x Y5V VgЅ,'xC@WbS`T쓧99*Ԭ0(bBЄ*,a Kgӡ.uhB aj!X0$]|{&v_c]zK`&0A}Z PH +-mjbnj/̂ ,(Bɗ݅;IZ _0u tS_"0 dASA (d ']=o>KL@܁pJH ox=Lb{H I(xIl>k\sW&$JԎ/m/ t: \!\Lt! @ XrP׶ L zaNzJTrt \^^Ԛlbh!`A:CZ j! qPP tMdOm԰ : Z ΀ @D0 (R@מ/O P PN*Nخ*q*q $~$!oM.2`A@y+q, 1ܮNM jB fԤ`0 rzPb }-l! fOq X vM TLMr:D@rQx \` nQ"'\Lm dA hXmOR R2Rp1vPn5TZ*AO w|r(!-ox2'*+2t2M &.ʀ O" ry Zל 6p2T `*m/sr΀.30GpR T T"W5;"M- t@/w P RmOל7M-rPQs&+/.'(r(O)O  s׎mzL'q'*= ,9nǭ.N{!Lm T״` x bמ NM Jp54׌?u-p ;@ Ԫna / PN33 A@ :jpB͠5oT"_"ΰj@`ԚqL)r,IND1I'.)IQOTN$a+RIsmJ&t,ߴ-~@: 2׶ x ؜Lj; dA?'Ps.i4E#/j t6Ժ`OOMO; jOn&RqVIOGd\^a:l` Pnr2qdJL;` ߮J @; $Ax-> nʴhMOZ5]=JAt D piJ80S.?T Z@ ZAL- ZHaOfA ^:; z!eW "o p!eM 4v` YA \ D lc=VvtTp f9\! nnK .Z'(O-<O[ó*6|M>P6^SllVo. $cg!TCO r F\Ը D/Dk!J"eWr1 rƠB f Q;@XAu͠CM fq7 u tyNG dA J`jyʖ{O- JuBX R)R|w շ{wܞ؊ dW~{W{ Js ؀w{k +؂M/X7;؃?XFMGK؄OSXW[؅77LgnOjXwC8s/zDxVAi6@h@% ؁* 5E1⋧&fgʼn6d@ SX[XmXjXV8Łhvaa, !`Сa*I:aB* > %`B6@A1.97y*8ށrAāXa>9_%-5WdK–qYDّyYBAhAfSb`A.yYIٔy[-`@㡓BAB!, `V> !*@A.`a Y0`B(Z@b@ 8@%S~D,D -^Zj@k;)@M7#w/:-a(a@A` BKS-Zqx T`bU9A"Z%T B¡( + ASҡ7e%Yˆ b`>;)F[ w[oZ!80 a-|[# %oط8Q A{W"*Yv "N%B[,ʁڹ;?z X[;ÑD *ܦd3!;ۑͺA{ Cp)S\ \JŸ[_图mtAXA @(B$;C'|7ó"(B%\b |%Y['(bvJ UέI\;iD<|ʓ"U " ba{S.WV~н;R/ @%C%C(*j&1ݿo A0#1`,,$P{Ec!&' `JgBֵ|1]3)8B$)\}0bW"A.@|a%2#)34..)6ik5H`aW]JB2dCTB\DI-.6Fn$GCD%(KIzDs$;*GD՜*N~uK`q$qb LLXbUBIHVK~JJ8o)މLHA !eXm@r.>^i!+,0 H{CH #^4%jȱǏ !vhɂxTH0];~18̓8sɳϟ@ JtdRъ;' UPza֠)=.mHfƂ7&\Xjݻxh$RN~DΕHCaqv(VpZkbFxq]{(ӨQ5"W g#/I:wҥUYBtܥSq+s%r#wNaw'qx^[NXE Ph6Іi)]aA؁BgM "L1@v2Ѽ@D9`Ng)RtQ ^8wѢr $=4x 7 "p3?~*(gy? 4&q,}f* /8C@:Y=*WR6 &66Pr DNUh#]-w5.ŲD96$SI=b:xγgД#lF mLB08ѐ'MJ[Ҙδ7N{ӠGMRԨN'般հgMZָu[]Mb-c;ЎMmT#Gn{^PpN=rq3[ηop{Nߺ1wq'NqC\ ϸ7nd ȧq(OyKn0'5 8Ϲw@ЇNHosї;PzԓNCYXǹ5! XYN{&֭A[p? `}(ͣA\?w?|` +\̖v 0w\y;^l@;W/teguNϼ{s/[0F|`y ֐C -jX z譚??PuQu '}`w\l(u`_} w lV~p~֐wRxhW]'@WyV֐s74w|7w:|}0 X2 /z'~'/({ uws[v~'G u@xc}9  hzԠ2ȀNlN` eᧄǀ[zG8vwFgy֠^_ׅfȂot5؉x79|ΧlG lwWHh `ɰ40Kxs׀f7wgsmWԐX~~]xwn(Lp76xGxVyp(` 4lQhWH¸Gֈ֠<(7 F᷂~z lY~ohze(Ph SpJxwywUlWxP^hsw-x0I{x{`Ǒl5yw0)3x'ك?ll'ugs` ^lNj s]''8!H UzTu@/|Ӹl lz}`Hl_` Wlq9S8eywh{ُٗJYy79w{~W懬Yg XsvVxӗðhs9 ɖ$ƀY\Ke[m{ىv5ITi `wЙ[7lUXp㉝ɉpkw -}h^w>8Px0q[h:Gז}'ʶwvp}*v݈~{#z_xv+ +ڍ='L*|WsO:TZפX wUzg R``bJYZIWȢclf֦r:t:p݆vQ~Dr|zzJZm O@tc = mOǨ MW (uQ6O <ؖک;6z'Au*p9ʦbʩ 85'tj=5 `@`P  3(  &jz рѰ9zp ` 8WJ pl `:'麮P  @ :@Pbٱ9 +``l ]ȳ:~%ˮ 0蠯* ` llF+?׭޺m  ʆp Xl\ f̖k۶`8'l˷ʆ:lp4k  sp+t{ P p" w{lz 9gP@W]+;u:طl|[2`Ҁ^;FsΆqWtm lTs ꨾ƾѪ 6 J+l{|K ywpG09V p  7'˶H |$,M;m, Fl_# t&Ftl&2lPѐ `6gO|Qۼ6PlsʴX`p 67ئA G VZ q[msf^&~(*,.02>4^6~8:<>@B>D^F~HJL'P>NQ^=08iL'Nxupvqyjdbpwrʧ'zzְ_zp^8~ @ vxxpz'L) }ҷuLw G ~'~DfְG[^H .u4-þή}w'颎}~痎ٔ5G>>BHJM(O 1IxG!dꏘΆ˚ Q)X~(@h^X?"s SCXh. m g.wx|ՎtP(!>A> q!j =Ռ POzއ4o||䦎wYKUO7^by!i sϜpuIv |9,5pDQ02  ?)֨UaZ_]ybԬUHaG(O K1eΤYM/SOA%ZQI+:'oiZ(Ѱ6"RNٳ%. >[) O$kr=/~DP*I+RL%ӜuTRK-<L<PUL H"sIC!E;aUGoQ5׼TdTfu3hTZdv[n[p}rp=PtUw]vu]xw^z7^5w_ _x` 6`66avmxݔY ܳCo fby_qdF_Oݥ_I Go`y ҵ( 6h{$o3`'uIhb=i#묠MłS^ zt)L8x1!3Hc3B. fo{(Qy 1xyа!$& ScHv z7 `@9 ` xXj?:)cD9ascx屏d:SyB4ҡy0u\+*yKfWܐ,w6E9R!HNAptH"{f!_+'G9hy"chT"PP\D e)Ȁ|<SAo; b|8§|(_ճ+ .*hvu6Ta'Elžk)ZeWy{! ~b9A6qJⵁ{@Z :?Lp(݇rB{h᮱TJ!5o UW4А\d!.c `PY_|BFo'o}?z:ԇ~[{eX{~}4*rHQj weөP܃ %. `zȿ݆ᱧ hݝ.gS]wC6= Hm۽(|[ OMk`c#%(@ J9 :sfpz$C`xq cSA:P/|s|$5OVX@?Ћ);d4߳! }HLSC?z`~`3/3 ?KU; 0Ŀ@ н@@k1;1› 8vQWX@ us@[')l=gқ4w`b cvЅ BwɅ1,3L5TrxbX1@zp]H"%B)t9wP)@B*$9t r*sB0D ZpMRzu`\{!z؆bvɂq8(."Atq81[mERd80[NT*xl^ZB =?\:Z ]k'Et+cޥ_ u(>dDNQ'\}dMRdIdJAdMJdOq%dRO>eTNR^LeW~edeYeZe[Hme]e^M ))fIc&eJfFH~v)|Pb;SH`bC9/2z"rFY)q禔bJjfA gxvop Ifgg_fn١û~(6:#:2CQ3$D.t!{0 #/b0zgh? $v :*0,".pmc[iqAZ627R jt!R 12΢-jnni &Fj>궮"'j! n3Pl4j.u)CJ$F!BK¤iVh)2waa2yTq؂~Zᒢj>xcmg")B)RqH]v2&d&)Kumގy%>$p8^kxty'Ʋo o$Vbv)f>mf*vymjaw*ځ,Jj# I"W0g=U8G S37`) 3/[ Ͳj+p`)pv0rGw!.ܢQutqGCqg/Rj!'6B0*9լr",0z*c0O!&wkv;[+Zosۊ$1C1c1p"53C3f`pti|'x@AtdkF3?sMײeTS$Ї-5Y66%]5u3'4VW2fup[j^7 3e0uc5noFSBHuXu]taO< 2C92NDtt]jO6۶6 qt ps00;Ь,01]xS9h`9 { J _FP5`5Xx+px瀎H;_7Kv!C0<*>vgrAosykgvYfg;q_;l:(ߺuY {?sF\zg: pc}<I,dx fro$x>z P#xu}?hA6 @vYK$=ަ>KzWJo> '3TzVi,? V<}ӧW?@sAA[ l!,Bb~:Mz7=w4lV7V q ^;]6O(9pF1= mcuj5.8 c_c)%1覚g☣.)S,2('ݼ:J5*Zz)j)z):*Fz***7iz+z+ ;,A+*,,J;-,jmz-,p{.:...m//;0e + ; K<[|1R1Ǟf1!1%|2)\++21}4I+=K;4Q۴U[}5E5]{5_=6@]6ik67uۭw7o7 ~iރ~x"+8;8K>9[~9k9{9衋>:饛~:ꩫ:뭻:>;~;;; ?<<+G=}O4W/RC@Oc`(4FPf ,xA NO>(µp&$[ O®p.Z _(%o6!np>`(ĝpFYĕ%qN4Y(ōEqVT-r^"(1f<#Ө5n|#(9ұv#=~# )ȋ]c\!{vH%ȉ=>$%+9a2ada:OB.)gS+t+ 0r"i[2rDe/)a<&2؁ &I&4"B !`f~ Vɠf2YAhFhF (A %`?]ªZЊYd!?^P , (AEh3X`E8ql!E,`H ODLt gX& z15h(2 `A~: ]@bR3!E U&L[pT"#'܂Ej/b .0a&xШJxA 0s>-΀P,|drAT-8"Y(,]]`UEN; }KghA X j %&cC#3d_ZtE FP$@ AEЊv.ZQjO- 05 YZB / dQ6ʭ?Xp)pĽ jZU~ >0K&8{ f__`GxG1*l ci$=~8H1K|*^qZ28wZ 7cC&rxd$'9gG!,@ H*\ȰÇ#JHŋ3jQa CIɓ(S\ɲKcʜI͛8s)ϟ@ `9猧SJ:QÀjC>ٳhX&OFaw+xmK"܂-vEǐٺآ9*3w̷\U8:fe<lO#װcl ylPO7fkU 9{.B)3#P9sG;bǗ?}y݊.{V(}^SO(@pm#RH(&0K @x;u( G@>646BC!Xe0q9#(!#@$^waE$ZOD hd}À?L12_P5 i[< 2$p ˏD` 1$H dD G:ĊqO4):c.r s4p(:YN5 !.w c@▛)̠(2K.$+BYa@ɩi>\bM?Mg1LtoF tT2JP$JL2E,rFzdHHOխ^WVwQ 3q.PG=AW b%(AW6e,pQyy7 bY*AeZV߈3H0ժ)Q YRv qjeYcq<9av4E@Kr@J^iMz׻i,/K]j xKNy ydK3kHA Sΰ7{ GL(NW0gLq@L"H+&;PL*y.{`L 1hNf pL:vγ>o~MBІN=#ѐ'GSҘδolM{Ӡu,RԘ4WVYծgָεw^MbNf;ЎMj[ζn{MrNvMzη~NO;'N[ϸ7{ GN(OW0gN8Ϲw@ЇNHOҗ;PԧN[XϺַ{`NhOpNxϻOO;񐏼'O[ϼ7{GOқOWֻgOϽwOO;ЏO[Ͼ{OOOϿ8Xx ؀8Xx؁ "8$X&x(*,؂.0284X6x8:<؃>@B8DXFxHJL؄NPR8TXVxXZ\؅^`b8dXfxhjl؆npr8tXvxxz|؇~8Xx؈8Xx؉8XxXv h n. 2`&IxnJ@ f `V1I0.8n WИa,p N`4DpgncJ`X|!,@(0*\ȰÇ#JHŋ3jȱǏ CIBM\ɲ˗0cʜI(ɳϟ@ *3gAH*]ʴӧ JիXjE(uׯ`Ê+ٳhӪ],۷pʝ-ݻxMkw߿#+ÈKN̸'BLŖ3k|3ϠC?%EtS^ͺװc˞M۸sͻ NM+GμУKNؕ۟,?]xxˏ];dzW[Fnh`0gf-Z1]j*c"n*rs :yކ<%i` ӊwUt@C,뱀ZDt0?y㏈ޙz,`ɠ "CBa S~w4tPRl*" Fx' XcxHGfݝfj:hiꩨfaz'yP!5  މ?(^zE#Iw3t@wy i%ނ+KGjj^OڷX 7!1j+yP?# (EĨM!qfٚ?$jE0t0y'j,1y# M)Q2l#j9Dso[hwz73f~A4d6|$N3X\o$ CT|9kzG ރxN#`jcO,˓  g-?c@ ry]x~'E(w2P;^A F.d;]z4Eyo ^AC c-#f [h. 5q̵X`}3qA1 *laya6GzBWXA?%r3"5D*2xVZ:'/"󬇎 #Rl!tjS򕰌evRA.w^&k{HbL2f& !,) K[e.W-P%Ƌ% !,lF/2/\ e#H*Pv2+"!,@H*\ȰÇ#JH3jȱǏ CIɓ(S\ɲ˗ -ʜI͛8sɳϟ@ JΣ0A)҂NJ=t)A2JQYׯ`Ê2ìJM=%YVN>l6ىhBeX+È+^x#ZrВY[0RbĬ7e5'-1/|* pưc˞M g,R&>V:A`#w' @!P320@|3+7 yc5/xPR 6?_Ԝte]^)zL@Jm5|JQmf`YA?`!G0SaTYEN5Ty)AVU1Y5Z9[|EgCZNRSQZTdy hZ81(cI]BxgI~8P)u@)R9&Ӝ?4L眤I u?G~]dU>դL(uqzm@j @4:5@xRT?F8P2kVk}.@ C=gTRedT5iS3.f.ZUtiE{g W [ } o^ ȫe)V] FHP%J.)ģ `{ٱ3_7ŁU|vc{ @9$ê&RttJ/-99  ?W UٌsdUJL7pS U&o\ : ap 'Bγ>[g TN-SR 0eZvsSncU!+P0ESzr/"MtVw[TxӟBQDSԜTUYٰPo=읽e u`8rӍEVұ@ ԾT:=ե~u@Q!'H ʤ ,h RGH‚h(S 0I gHC8̡aj@ ";@"HL&:PH*ZX̢.z` ǘD! hL6pH1ulb/F)Ѡ70Z2^Gv#$'II:qRL2hw$ UX4dNqݼ`WO|6"<=Gָkecp#~Ýb8 "m#N#EmDlzTq0"rmE\NUډl[ TfN\gЅ`* OCp8cK%\]391 D˵sg|ہ䩾349Š,,yҍqL&c2 4NuPW(bAs""]Fw NMt Z;B-Qg@7Q ^7Q{c}&]^$f߹ʙ K^:쁎W4q)x3D|B_hzֻ~/};zD} xW?|"D,8ct'bxX5 ݎ{"=ݡJ];ON?8Xy!,) ==E!L(T녿K!D+BX0#xh-$;%"H\T / !,.@H*\ȰÇ#JHŋhȱǏ CIɓ(S\ɲ˗1ʜI͛8s\ϟ@ JN=gYTXzb:hKٳhb,v gv< U÷f=huE lVlÈ+^ S&[p4Gnz%y" ;ӨSuܶ c0P7zB^<`@.:tZA7Oױg v+hBM[1t3pxlvv{7B۸uW1'wo WB g_!/|v[nMe`2grV3O2 4A>j8'f1X8?W,q+S@A@ ItT4v%S Ne U93t0NHБI.)@H0M1#NB)@V]/ u*(v Pެ# 3ЌuZC<뮩H8!PL1U-3]) @t4 A;liY^S҂!(tlAUh#@0Ec.A(k 7ߐ'+缃ľl,eV겨TVD=p \ U@ѼbѤ<@|+rfH?c9؇PN#/ɡs0L3  -Q#dPK=A;V4iS@5 Ow 6R;$7Q  4A &vkBcCTX kι3MP8d|AMt[,@"@O觯cL&䐮 pp3!@g)D#26 o9F2JP !GŸ BygA4A,πMBЈNF;ѐ'MJ[Ҙδ7N{ӠGMRԨNWVհgMZָεw^MbNf;ЎMj[ζn{MrNvMzη~NO;'N[ϸ7{ GN(OW0gN8Ϲw@ЇNHOҗ;PԧN[XϺַ{`t \  $ X̅1D ). CLZ0!Dȅ_0| ܅V@RE 6?8d!,=@(0*\ȰÇ#JHŋ3jȱǏ CIBM\ɲ˗0cʜI(ɳϟ@ *3gAH*]ʴӧ JիXjE(uׯ`Ê+ٳhӪ],۷pʝ-ݻxMkw߿#+ÈKN̸'BLŖ3k|3ϠC?%EtS^ͺװc˞M۸sͻ NM+GμУKNؕ۟,?Ͻӫ_ݻvR?F2ŇN>|rP'?f 6`pkyDtUxw1_eφ"nC2%Bw`(4*{69L+U j7Of?vjPxgƇQlSVySV !2H A EJ )9gw)tz>鏐jzfYވFA֨馜vJۍ' 3FT@MWw3w" *xvE0q 3nR ]*PMV+N-.;ujE_J皞[+jp x7ˠXE}WFPX ߯ʀ0@?g1 0Ls,O=|2j/VTWm^H*Ωe$jBڤwJ]O"pfځ D"kwYB֣g Iv'd\6Nvv[-;ivB8Ёsrk4?6{]XDSw.g( SrkqG/=мj}.w}= ᄭ8Tw}xeBb;G–5"2w7G9B:YɧQ?'YyVL/KȠdd@{? YAX H/< qFO"~(HTyt(JbaL`3ufE5(8 yjĐ dޓ#,ψL"HRTρAFZdlQ (GIR !8,) N2+UED3Jz 4z!K--vM).ЊSʭ\!,L@n H*\ȰÇ#JH@-3jȱǏ CIɓ(S\ɲ˗(-ʜI͛8qɳϟ@ J#^rD2J֊U!MN/EÊKٲ] aT]LBuhwnĩ2v{ЊVCƚ׳#KL٬~1i[W0ႂT+\а(L۸9jg]K:C4]Խ8CY#nΕ@#u;ԩ/ta??.ȝ?2#1- $S?/N<ƼV25`MWuiMr8`I> sAW0 iq3C=[i$[nL6@eS.S7CE/2tY-Ҕ746=]0k;G1q4gw $=܍! D30!463 1nqcL R)7ff:@i%HJMu\_IĘFkZB>V=vn )(t%q:P! ,o和@d9UA1T?H4匟 E= pA\t,o`x#2oEAO@pSӁ$w`v`?0|rƈn3=cpsm-TWM&{@!kFS0=O@Q i? 4M-=OA Mx7 K@AӨ:zsvi襗5[yЇ͏=qG0)fW/W%8k6j7e/ 2" சg.pQ`r w&rl{q}5U^x%}w\rѷxr}g.|6n跂,hgSp 4z h`۰w9hZT0 U dWXshcrrWi,gmkf!cWxqAelpGqmi灃rgem`j(,WaPUwu(^>_'(r'_ЂPPIUo`4AXZ U` 0jGY`p-`^pKfb&s,ml@Gk xwP !nYրjm#r`qeVm8n&e،]/h`Ҩ]]1hXl)Ȍ؍[AZ8`Hv;gEt2VHo؏9ggV UؐTUY\j U) "9~v&y(Y$,ْ.0294Y96:<ٓ>@9BY+ a QM9U : SLF9HYiVH`pl\Y8V p/tY_)-Qi UMyp@ `POE de,R]iuUyɏaYV hQu/ju I钤ِ U)Up[p2 P09V|y0ؙdeTTIU<O=Vr)U `O   Ty U@ "P  3 O `c5z d O , 扞9VQUМ &J)ʞIe%@`Oş YpV0T U8)/ ưc% Uf 0Z B*ITfmդ:`OuTU  @ 5ʦ0Dڜ#ZښZ"J}ZJ 2k c@@ K)z I멨:IZ'pDVR"0U !` ZN H+@Uy PЭO JJV+0eš: JOjce TǠ y3 ͉گTp XTتZyF*й 6 rJ`Z* _ @SYJVy wV UЩ;UU0 `8Kʳ*@O T{šfŜ k]c MpKP U0{+U9U<;зim9T(=; 6mcg0mETG0 Y[ +k୙if0〺i O%j PT˹;Vb  I kŻ ; KV~SVtʱrjOP O %U+02)!a*U{ *KmzT?RU ЭP{O{V:ۿ+TeT@ \O xc6=p  ms l(𸻊VM0S @pulXlZ@Pѐ /jl+UIUnTqW` 0YcE@ &peW<\T 0Ufj|}kEe kQ Yk Ƅ<[ pʙ\{,~ zl|G+|cv;):UToOek|;ge<7|OAPerlLlkTJ@&0U=U@@ UPQue;d dT"U.@hU0 c' -SQ - U"U@-k[Vߠ )Ua f0 ӆH\?cE9 GP<-ZT% ҩP3]hњid]V苨U:cOe=- -pReee=ݙMV{VLoT,Ǡ X%ڣ-U3 !R-ˮ d5UɀۺMVp ':3PP^௠-e `Ъ=i=Uk W ڱ= mMw `0P'Zp1 Uazް-W{`ݧ-P g`\dֿ,mޮ=hͻ]V}ɽLʻd 1OUP5R۶]L;\?ē@@<̖p P\`-   j os^wNV/ c ɶ]eu,R2McanVSУhlUCk@tͩP f`OUR}Ep,dU͌xƮ>.Nlc^O.^dpnX{>UQ O R-p,Pt,U6T Kl? ѐ 9y@o)x*%FP 9Y|?4_Vvp8:8"_g#gd6AJT`PNPV!Wg;|@`bOV/cXgpFi/^ccr?f_Ugtg|ku\o{o?Xt?vp)[f&gk;oz/X/ٚ aJd|\op{duOSdp1 ʠ**0 d wʵ/km ( r ;pOkkpCT @Pr3~ի ^ NYX3Z-17Bd ΒFA ` "C)G1ҟ5rP`_55Ȇ lr0*2/EBSQNZUYRUWaŎ%[Yiծe[qb@@-w0t6,|N '!DYANpET,a`*r9ԟ mIΦ]mruoo¯ S- ~2 VgaĉD_+f!RaYZ'v2@!GP2sbC7 4TpAtA6 XU ! rX\0nj%0P`h4Lӏv!5rH"H$TrI&Ϛ%&(B0dZaW0Hgud@_b:(Njʴ漯 88%UI'rHPC*rQFlQH#tҷxe{ƁCB8vt!bFy1С]SְcW;Zbϔ<=QfՊRhvZjj[YnZpw\HݍWtUw]v[xw^z^\i٘c x` 6eava#^b3xcVcCydKdSVye=ecyfzfsygogQ6hhv陙zjXjz낱knk6l*V{mvm{nn;{oo|p 7pow\%#|r+ "5\CKkjLwoeg}tsuy(A=QWus vaBve^wß0xoo98bZ[< Pv>3/k x$(_70)21g & r<@e}(`B#靁~S3h7yL!5DAzpLazC Qy'pDAz8F BA .DNA@@5^S8Q R;E~59xy`^P7pB wCn :CFX9 &UVLjߜG *1,S@)7^a+hhFfTsKXUs+>ͪ3}+5S'hNch 7Sm 5#5g&AhY ԨiVvqAmtmoB>o<^TzVӓ 8qk1/aLaTyB yYTp4h =2Xa&I1P"}/ y@rη kp׷ oeMZc*Fh1$(ڗ~72&hZI՟#9xCG -iJCԗFuMboMxAc1kkN?Lk]=f+:qvUu@ ަ-jփ˵~-9sPsܪnkފv-yG@8Cq z:ml@7X @:V7ȅ r t0߈p(QxLwޙL &oУfH7qT +A c1*:1 8F:k3zgqh#a.s]D9q3PԩnuƆ׽}Eߓ?|_ q;DrC$ -j~}c%@AپS@c@@hzB5]&L=RpRƨS9ћSLeA!POx5T8 9H@EʀEbhVuzPCYw`OQ{8sg@?{ʬk=ofRW :wx.?PN-UiVkΐWz\Pz8SVZAɾa{ vuWx)XׂETT˄uzmCuƛ؊ؾ9YIY-N=ؑ}|~V(iեEI-WZJ[LuX؛X `eQ7l8D}C6+x{[39{)kVU'[5VvItC=x\}ȝW\}m{nHW%ۡ!R,H*\ȰÇ!, ^ ϟDB.T(`Xџƍ=@PCPpڞ}ݮf]6 "p9;L,$ pp@ڨ=4r4}דl2Gp iU`$SB180 aOc_4cA>N4۴7 ۡ |  uУ객 cFs!S`? |#̰ .1c\`!98 @ 3?CM Ʌ:q&;F14h-rы+`EQ wJ-|a xBhQ`]IH!Ҙ!bjy IJ QB1I~ @JЂMBІ:D'JъZͨF7юz HGJҒ(MJWҖ0LgJӚ8ͩNwӞ@ PJԢHMRԦ:PTJժZXͪVծz` XJֲhMZֶp\J׺xͫ^׾ `KMb:d'KZͬf7z hGKҚMjWֺlgKͭnwgK%q.}.s+m#f?Absvkݎց `:t<*-?$i"qsxCXL L ϳ@{R Z0 dr,oEAkHΓ $?'_=ySޗDҘ xL=!@AgXPaHn85d5yg891 mGS?!u֣c@О=0|lfS\eqtD _>i!`0Ga_ 6 eB7p Gs4b#DA$r$I$$0Pɰ axi'vkva)$(' 9@< '"pxG} Xp l"0'"&j"'t"(gvP"%Tҁ'7x <4b N|3;o)++07@5wp-ς5E臀'wWs9/BG9?wЉ'a"1ˆΐ. q0.WiDC-7Of#1_( 'yh`(E&P "*f(U/8)z$H[҈A`4P#a4 q)/4vEC| .0 { -0@x4ۑuCx5t-"s,20Ç؋'y 0pOyY 0_o62M8* V!P :;LHaH)%8Q Gq a37 :1 c (p#9 d 00nw7ІP- {A @=3A JѣH$ŴcFUJիXj@OnKٳh2԰oʝKݻ۶ [(޿ S/}^̸ # ˘J,̠CMӨS^ͺװc˞S۸sͻ Nȓ+_75KNسk {ËOxS_Ͼ[s#C_at݀-)!xۂ@hn8 t} D?.χ@' a-4A*TGЍ0 Ѐ='!pCV$u$rG6פ7|ۊ^ۓϖvC ?}ٟ_>ex[AޙЌRH!!-1zQi g[gAcbIxx, g~n.!'Њ(m@Έ. mا9+hj(&A:Z~ ض-F.(eK ZF⻠쪫n>8尿)fk/2{-追ݫPkjqʪ}B w*mp++G"=zg}oΈaP,@='(Kr[+9׻[Rb+2v=kn-n *jyڦOio}_ tN9l:e'6{mo*!2ͩ^{U@*.|&BL;@zB,"} »<ቻ8!*)lźi<ӝ= l|뇬~܊?O@"Yi% ZYKH'&whF@: Eb,H2 ~nyrе*Tw+q޷% \Xt#Cr%c)—RO| ݧPc0e Mq 5FQXt*Y-S+ FAUբٙt} ]MC!QMzs67$ Qao1] N(67m17ap7&\$:V1Idoh9J&mf`&VIhSӣV6SuRW|Ȉ wkzU*2ӍD}T%I \Xe.b/Xa',5)a[̚;jɂToBKTKag\M̞&%Ot{ }FǸɎStYRiǛDAkXJE5!'S)!*L5T@R )fD.=d娺)` eXI Ju2j"YT_{+ḇU~vSF4]~L8BŰ!bk29u ͬf3n69 G>K"hWKԺkg5mS pKMr:ЍtKZ-Nz xK+ՏޖCMyKͯ~ 3E-^E"7jA;Gw'L # 0Ip-(NWb{:9c0 w*;͕XB{UK'{jirT\F?ZL*[XI֥D+,f(hHRf3Ww:Z踅γigөB}&6S]QYMoٿ'M<(Oh*:jUQ-l?lA D,E   Ī\ʶ =m!ǾP' e<l|h-ؓ 0@l]Gm̞ ܝ(?]PZk 2]+8:۬>>/X5Yl0?ݑvJ[HPRuPS7RVR&g]~`vbVcwMjvlnYG"tv^`|~_/uov?$O_m ` @` 0'O^tj#Q C%o @ @^À{a?5rOGn@ _i_Ɵ`cp֒c-? .T(@BNXqC4nG!E$YI)UdˑeΤYBk aXAkL}@gBk Rᯌ59 FH(JfLTdFbr&:ҦOsȬAN5Qi+U02eѨJ.Nx4Ef?Ufkر5]6Ü:w T(BQa3 +7 A4"sk 0PWrڨŦ\qxĔ @{#n/@rp,o0i:% iJ2F7! cXe(taLu%p$X9#`0[ ,2R\Aam ǿ|(# 2[w4lrAҳ*d-( ).uL((]w|g٬Mj&]kX*+:],Aa3#FYD#`1c ǐ&HRhF0mHJ F<+ jyuD P+daK BV5Ce9I }C vC$ GD$fIHD2QS"XAQ[ (>0Qc$cxF4QkdcF8QscxG<ыcG@R$d! yHDq-!H$Θ#: ϑa!;KbRLILRLcIIR2"d*UEN8F3Fp+Q關|!dϼehVDc bȦ6ØK]p&Ac&Ld9y2i'>iF}‘mpHz0GtZmd1f:dʀ<:б1r`G.$c@1H{X!F@z> `1"F)x0CDJR 9Xb8І8q䔧,uc2u3hqtE(l@G460ƛtdLR*SU*va}XyִJeSGXRժX mcIkSꔧ+_ըE1RK2"tgzRcJWReŬZيƁEhBPfgҠpV0V$8[T\@V@b=*7!&clB=O5/z L ;  +i iED'unw[FRn#P4)Fc,7U)lap`Xe bF,Gx&<72_ !-.xJr@ng\qt4<:Aj6W(XCذ؀ m9Cu2E86w Gqc<4~INXƀbԦFT!`7Ǎ9"pyXw4cU9f-R;U8vl:Mf=mml{Snd+w7Snֆ{NpHĽt: w&ɷmЃm`ڠA; h t섗nvHJ آpxIƆ; Pu>!'VxB4ҡyڒ89qxHЇ^t28 yE2Xg}e?v]p;NGd}{H}RۏVǺYx4^_{3 O犑}U3dx-/#lI9}ȁ 㷁y)oZ|.0avoz1`0cj~p3s2ssۏ[ۼXyû@~00r#s?1R<??}C7b< @3j@~8= =̓?ӽL2p8$ˍS,K pL$4HKE;CDk6Ȟ'<$pN6Nܧ#|Nl;NtNOUbo0O6lO|OlOPO|[O9zW&܏nI%RO]PmP}PP P P P P PPP QQ-Q=QMQ]QmQ}QQQQQQQQQ R!R"-R#=R$MR%]R&mR'}R(R)R*R+R,R-R.R/R0 S1S2-S3=S4MS5]S6mS7}S8S9S:S;SS?S@ TATB-TC=TDMTE]TFmTG}THTITJTKTLTMTNTOTP UQUR-US=UTMUU]UVmUW}UXUYUZU[U\U]U^U_U` VaVb-Vc=VdMVe]VfmVg}VhViVjVkVlVmVnVoVp WqWr-Ws=WtMWu]W"w}WwVxWz!yW|WعW}W~WX~V=X%MXmXXXX% /X0XXXXXY=Y|5Y]wUY}Y(rր!, ?H DPABTpŠ b(q#AN ɌI~2aŕ-Q6LQc!,LA H*\ȰÇ#JH@-3b$ű#G CIɓ(S"ԨѣG0cʜI͆,3xϟ@npI ՉGPJJ QHp\iկ`Ê-yhRNI]˶ۂyQE+iRBy}˷ߡ9T' މ& eȒ_̹pfƝCӨ~kyM۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨ*_2Q.@x1) FtP ^VԲK%Pʂ U46 !)5P* e ,覫n oBJo+{/2/!,Lr( DP@'=-̈́DȠ{fXHJ#GEy1Ǝ$ePDBYF[1c+]A%rnRJaT.6tH"׮ %!,A ?u(Zş?fzf[)gzTjZO\v+!,L`( DpikVDRfBu"PaA9Ң=3nPDbD(HsH5ur޽{ q)æN6! ,A ?ի-.z0a?%:Hϊ\P,&u`ueb@ș3!ʊÀ!, Lg( DP@$e&T'B %x19gܑeES煋c%I*%*GZq Q0ʼdPj :9LԈTbӈT!! ,L8()\ȰÇ$aċ/do`ŃC7P0̄D(ȗ'T%̛ rc+[I@(Cfї}jȨ(?Z֭#N 6QT 9սײx \r1j9wݫ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËQOӫ_Ͼ ZkK&LSES+TX`V̐^V^-.THY,"ŁqJ!,Ls( DP@$e&T'B %x1!0vMeEre d%I*%*TƁńEw >_[, `}fqǴa΍VF_:lJ* !,A Ou` +'Ʌ$]+q#/zLb H" K0_:|U_V/DHU 1l92 !6,MP H*\ȰÇ#JHŋh܈Ǐ CIɓ(S\ɲ]ʜI͛8swҳѣH*]ʴKB:JիXt֯`ÊKv^cTђZ۷pUSܻx[.[ Lˆ+^<0ǐ#KVx˘3ϠrMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+VkfvM+k覫+kl' 7G,Wlgw ,l(0,4 s6l!@ 3AB @78#|/,  ,hQU(qpﱇBB %gO&B\̳ Nݱ ]!0,*{ET +`,!CgԢ ,P+DO@+2Ń_Ӱ7د 1K& FlDKL|<*"{2Xb!PvMA &mfKP,p\<|-& Eg?paBfX `;B.`:]`4O6 ` b`FHL _X tᾁ]" dAA (d '.;3YېKL@6JH"nvݲ=Lb{ Ieo: -b]bI|*$P C01%/gych9BW!ČaqWGZ,xAXB c F(P./a`\e@55@*A   z5DIJSN eZQZd 0/Ir̄:Pfl'\&p2tar?1xf%,9RL GOq$`F'3aԢg$*a>.nϳ"f6KHeQلQKplyl TCFЍ 0,Zq t̆E9;@^4 `j,|;| p8"jXхi!P-&\.fՁU x*cX]YS8,\0Y XJ) l3d\ [b,GiJ1A=[RYF홴lTGlnh$|f0m=xl=%؀DtE x s} P9k4 m scC7}7' -D0\ dt9Xu/ ` gX3$Z'C8wP c5!PhEX0f@<(N[I8Ya]H~h1A1H JP+ ~c~V1 e@j32(:Sd `HB81cX3Mx&ӊ؋M-0XxȘʸ،h/ Xxؘڸ؍8Xx蘎긎츎0Xx؏9x 0 i9yX "9$ُɎ8*i՘+yy)4Ɏ26I n 8 ?ْB jȑXPR9T)hjj08\)^ 8pf;3iiY9m9=9>an䈕27ɍY9J6aN)UIxLydiَoYyqَuxIYٚx) iI ٛ)Y7*)YbHih9IթXn ș*)Y>y6q>xs)iCy7I ۹u*>)I2ɟ JCٝnԘɒ79ܘgI)ٜy Py+ 8:xij zi-Djyr_ɒH :X:\iƙIUڠ*[_ aDʥjkiy`i=n ^]}IxgZ{Y>yUe:h:*p*-x;z8ݩ)Y9٪Z*I4zj ɜYh82y|i ojٍ̚ΚϪ`՚y;٧JZmɗAjX`YĊt*; جZt:)١٘ ت?iI+zd :9O؞*\NJદp(*kڠ'7KښʢY/YFթK/)?{˜JJj$ b[ )+?jph 2ګ*ˍ^ qw۵y[,Iy4 -x֨vI@+ʜ Jzڷ~KW۝Kw; [YK5N9#)-j v>ʩk)nۗRJ[v~ nR*RJ۲*[8ཱི{љ\k;՘Vtڦ:;w١p+#px'XW= 0 +18q Pϝ{RVL*"n,诫׮!,i( DP@HJ-Zn‚_ !^WDy)wΜs,`τB%+Q  -RW%*DXb! ,g( DP@HJ7('̒FA ! !-*G 0gg)3 t(g1ӵhB-FJџիV-!,HI HA@Q P %!F L@@Pc' !pH<ȳ!,0 H*\ȰÇ#JHŋ3jPǏ9Iɓ(S\ɲ˗AI͛8sɳ'F} JѣHRӧPJJu"SUjʵדWK٩aϪ]˶ےiʝK۸v)޾ /È+fhxǐ6Le/kj"?MZӨS^ͺװ_#M۸sͻ N8ٰo=g D-}wݿonhx&]?_&O|Cc 0e'܀ꁷL 6큤mJ]GpKLQCjڶ=2p/ƈbj5ިym-qaq  9 qqV3σ :l g L>cG]~X: jY -SG;H @C+㑞|)S36sGOF9G**^dyj)a'JJNB)%I =#7,4hC В*9C1鸓"l:iGAr*Hv+@ RFK.,F:/ J2s 贓:謂:*fV9Da@p+ӊGl-j8(0 L3Hc+S(#M(jSGۜ3C@}30U#4Xpx 8. Шn8 NC}T[٨uߝ=T}!`,Pƍ04Ges.Ӟ錇y=>?{7WyG5 p~C/߼z?j_:wCmnNqO re> y2OF:!gp"x;0BXEvBpk1DqsS, &Xs GMCP:0r3jg$H2*D5eɔlnLt'jiNtZ ̧Pe2ә+b[y;E=͏d$-}iLKPuQ)PzK >iG'HmhA!';%*CTU&bE)Vc R|.OYW*XU3zU$eT=JI :ƒ(XoV$"1 |@KK%iZ#tb+Ǣ \ҢT|#`jQ#[vGحc![JbJ ֺt{ @x} *빁JsZZp ŽetvޏTW&L _^f%GrAQ#PhHZ+D"6*I}A"Px?8 (1$0`{ts:ZG +0%(@ w C8F. {8 ?'CS 0,8<21}/3Ed+g~q z+,FfP sz< Ytm (0 NyЊf!C.2 3jNdeUIHm@#t2C'!dC#3|*/[zr=BDG: O`N0}`n<#=KuУ "&z4m>g9o \G-. ug@8h.&Zϸ6p8mo|16ˮ5:xYv1O(xɱL{w rǀzCAoD":Hnu{6ud:5Ҥ/w 2?;e=ؼnt~e ԭx ,\xGy `6;Hw#I'SbE Hݣ c{H9>%$|(Џ~P#@2PV|R8G?|X@_%"PD8 4>,tL2%hY$LQJO5x4 Cb1h;oZ:sF@A`9gwFXxӤJ@:WP馜J (騤6Y!,` H*\ȰÇ#JHŋ3jh1Ǐ CIɓ"9\ɲ˗0cʜ)͛8sz(@[h)!ТDGaTJ*ҫ:]׮3*&O M(ZoF%Vڸ[l-c݄Sev3>:XǐK\qOFҲWfH2 <,hYVxzc뙇h5qn6q.B^<`mL+SV4t6\Bn{v{7̂*đcǓ^;3m mFO$%3N>蠳x;Hb/ˆz7ލ>Hm>&d=E6$@\ 䴢ܡ.:PmN L cM3H#^Oj^#M({H0M1#?>@^@X44( q#MAԱ.˨J:38!+`,Uzk)'0bYRPhc4?2k=-".봛Kl_ f gxX` "@ R:NQU[@4,+=MxG 456[ cgɈ'ј1}? pM}Oݗ 8~ Tp#vg[V;!>@8NV)59ߕsyٴz^)1ڄ{fu.D4#3P,DYݓ`&jcE.O09 Ta*#} ER( S ,Bi}{_!3p0)0 Z8Fp4\ \`!G1񙇅!ĕOr(CɌ <@ `х)hA c0fQOXQLT3S#/$*<9$94 X K=X@j&f?oq˛@6I2oe?L@?%[*JU*PU U4iSȲ&u@q\j: LIjR S*W ն޵U:B3yMjXh1t4?&e++ *ALTER1r1 9q 4xF0^*]:gG=ʵׯ6KٳhӪ5BpfKݻv˷/Fk *^̸qcĐ#K cۺe:ϠCvٹ+}FZ@x6f ؽ[ o sC({97(G@“c'[MyǨӫl)ۼ=`spܐ\G`E9?y%'  W?Vxxމ(Hz,U{dq3f5(cRD!96#CHN|SgLKxr%w(A1'a.Xj\Zzǟti'J.'O0U4YJ1HCAV1lNQ<9;Pߚ7 '(t` *qѥᆥB'q۝J瞼QcMSDDӨZZq,YX`nQ[-) J܃m*b~`iZkV*Cj[4Dİ,YP?9,hgkx%:Ƒ]*o Lf$‰2^leY8:8B9Ỳ4;`O jM`ypz|`mdLj  @3ln]FhS4L6h` $#*X*F׸bkf$T- $q4YUȨ9fL P=V M/dPbjqa犓 ?.Ɠv+Eg}Jwͷ/Pmo>W_Bo# N?ɏ~K@ H Zi7I  XW0iZH8̡wЃ2H"HL!AL-b/A:1gMj5 ,1ׄ'/ 4X>.pF"؝ьQDc"wCя |c8G >R$7Lҋ4d۷F$"ڰ6`#+cyx:ڑtc1\H`7@>܁cO ޠG;̰e2ә9AVg9e6-hl`7/'Ozs7^ G= D,uc'/} Esl7)gpЁ~ȅ9摌{3 yE2vcK\򐼉4 b<:Oiz˟z"]hC`S&1Lx9jH0|`7. )BQ PxJX8ZFM BhU|:$0bX"P,c Y5{6QLx+:m #mcE cH#PƔB W~5,fg ]AVעt24Y @)( aH iwfרMnmoVksYf5y7ʰUH#<J 9 6@6`C>ue"h7uu\a}0ѝԆt 2l8,X,{rhW,#3q."؍?(..udb7TGuc:Sh0?de7r|8c̻ݬ}u; hc7mЃPm``f@1VX堇c勿< H:4 d&k\m~ugmZbWOF:!gss񈨴eTsuKe{Fvc~6ԗmg_7@f, C ڊ4lq0A4YvhnAB(QZ5 (>q ޝwgLѝ!w Ui9G!qDf? 7SA:P/_2;W~8p Ɵ٥l<_)>f c?eQĠ=};hǁ(% sxT| |x TJ~ɷ|L|ևNA8rqiiza>DB(N۠P01@ "  @SMZȅ^R7 l nxp]F!SMQ8p{ !  Рp؆ PNRa8vyaKހ 0p@@ mvIXP (  @/H8cgUxщq9zv` ig`ms?CԎ! "D8p/{)c%ւ;`cYyYH{D}@Ԓ2!i]؎ uE +#賒s.@,9/6yC:9/7ٓ.;wD9@)H"E?AI'”Ria)Py1ZF^ [dbyoTjyhٖ1p9n9!v96tt|y>zrї:Y<1)6q<ᐎY%n1"^T_A *DX">h+(` 0@ i䛼9"ٛ/YZBi ~՚%pY`Y-Py01ybIYnԝ >8)`Iͩ2` 9 jVٹ۹rI)T9P@ )ٜ A.jyP'5(4ڑ&ڡJJ1I* Y%2` ``yi J 3e` r'` &R@ p% 4}ik: e p 5 pZ6arJ Lѥf ,&٢bj*yyA꥞ʏFڠ(:MP10 X 9p]rQ X P%ʞS9i 95 W"rv4E(NƠaJ6Jbhʮy * @jKJ*yyrZ]fJS!I93Pi` (Pj @ yhzW2r:0I^z`o`_Z\ @ n$ @P9gfqi ";YL:1T@p۶I ՒluڢczZ[@Ok SpڝҝW0h`kieI1)A!z!YJCzY+K.+1[<[Š` ^PpB!`ѾںNSP*D99 yW@:_` W0LںEx ٫t5eJˬ:k aPZ[ТV ÐL\ƀMl:rekj Y`k^ƪ*r#,V@W^ +V\ XPtjl7n/ڝK k60ʥŰ[Z*b,(`E: aڦ} pQ*:!\lɒ E|·1D` Μ|? G`i$B}У 4 -DJ=F A ?d@@I=C@( Q֓1qڑ֜\?y]yf'1?}((? )B)x?a{m! *M%xp*mԽ*22l}צܨf!,b,A4f,,bR-ۿ!"5ƭMCkM=M?ߧh/Rf0R00 0ڛ@ټm51޳A2ޠޤ-+d3CS<3@c;9SF4Kl<q5l25XS5!s5[!.ɉY6+}q6i6mco7c!#tc7{78ic8 8X8"&B9Cm9n&97bX-:S:3;3;s!<q5ţ5%U<}NQQ=c1|-)4蚾lyM$N,~Ua~CqK+ßDJ9ZB]&Ti~IF^'q^*iNá)C)n؎z) In.*]ɟB2ȉ>﹚+>~N?^3nFꞚO 0.OF%oZdE"pnFF Vd-AbgX?91FOK)_xt)d8$B߹<sJUyQ>!>SSAY_rI!JQpoX_WL(M$%EPRULEE/?O1qQd T `OPQOuP{ @P Lܯ``  PPPRR+RJ?}Ŏ#X3 +Ƶ"ޕ!d r FaW`&\Hp[ H &ǖ0?cBXPH>8: ӊY'_ŋ rvA-jXFՀG䊺 b] Q0H_%K1)R[!NiÒ/ڢ%OFe̙5ogСE&]iԩUlka!VrW 2mt#ݦJY\#'#pzܦ <2uU 0xrf`x]'osA r cd &F 0O5CqDK4DR:Z`7dN*# zMzTxgMb<+ أGǗ "]8b]QTPВФ(6)Mp4GҥD`/0`GHn?(|+ޑ jњ)1i0{3!7 P4+41I<`@;ӂ^/-$0؇w@҃CflFs |m!ACÂ8sapof0 zp|AGp3z؆bP{ƖG)F; ,B.:xX01FӂahsiP6tF<S"XpI)+WB@I.T{#xP¯vj*'*c@*ɝJ+(yXbZ!*Px@0P*\J:{pWJJKz8KKKKԧD',*/f*Ibr*30ʬL˼LL$,ϴ/ d1z*T\ʰ4zTLbM*f3/ȌLL-MM NN\**RMѤMtӔ(D$>*M;L,T',OvbM.l rb%Ja<&Ug~ VT~dn:hޠMhJb%ghd聾,h%bhaJ h.sB!hNOXTii/inQ LjLOn͛Nj^i~j5N\jLϣ*jVjkQ-Pj ҷrM7MkhkkkknjΩVk&U+n-nMk^lnl~.B̧TfUŽOl>¤FuhmҖ v Fh $0zp-zml͎%RVĤ)οn)پ 閌­+0,D b(Qڦ h`Jo#6[X'$ô6KFiׯ*-`o^p">.z0*f< 6@6* n>3ȳ3:[gA+.DS4Fs4HKJKD[F+AIà#\?ey3?X(2mn6Ϡ'2B۲ _G 083y8^ 3i-ȅxo ?4m1#30- 9sC=**27st!߳,.߸Rr.Sx"4_9?à "aJ|pt;7Uu=!r# ?h'()h3qh{ Xn Sw|w iU% c8 ~' MrϠxujxH ց+8 }E/h8H8 H􆯫OWP7xmCY}}Wx g07tS7wAi ЁH{6h@ X``6{}h~ZpwP<<(kģūo)*Əzn+HKxQ f,?dX w0JfN~wi~m|6ڸ[uV-[3۵2Yɥs̼Y@gBo DGe}pA;  ڰUI * nmph(p|^x3 D=Z( @, E;@a ">, `ѐ!P. 9fa 03p͙E$GL1("& $?pĥy&|%|%@2c.6jWXbO&3͈P[x> F`'q@rfIf)X:i`e&mCasj)i> Ѧ=w n ? 4K:LPLL Ā)ˬ3!3@8c ⒋+/ NuSP@x!в>;X3$;Dܴ[+VH@WdmT;oıny!^Omz+P9 7\F  wלR˯{oo֊.  Xڰ"@ x6'7 t5n1G`˺k QD C<$ =pmA.sNVG嘳pL0;Gu{ <2E悺!:b 9S.zYWou; $o67goT:}= = nY; 3qO:t&" 0& FJ@g%8J3'y}^N $8A.u; x}# מmPt}'o#-TJ 7ؠ mȁ@r& @hq^"(1!, #dj|#(9QZc=~j C 7 pE2~$#)IR2XģMrґTPG<N<1ETjR|%,c)YҲ%.s]򒕎%0)a<&2 ,_6R|&4)iR Q+,kRiq<':yu<HC7Mia88ө}G6nn8F:lȅ^(p C0eCx4~5s#$A䠑@r@깆6!'@@+">)P*T[J"IQ"t)+^P1  4(%I `Hʑ5TZl*C+^Wb G-Ꮏøf<"#>8X)#G{Gd = yGtY4#mAI Ҷ-ns򶷾^+R%K`d*2U O<G::4ȀNԮs8Mҷ/~oK򷿾z<2,wubyGGPY< ]oCVHv$&>1Oؿ E$B:R/-] . _LVk6y0, ?Pz".=@F1m1Ӝ=.r^HBbֽ#Adqqy9a@}3HԞj0̖լM$9.p$a4EjSO#x Ӷo9]ӮGP2RӺ#NG*e3-֞XlSdء.o Yd-cn7}y߹?->8eo.k83qsY8C.p9Sk*9<6}is>9Ѓ.} wF?:ғtwwN:ԣ.6}V:ֳk^:1f?mǎRswݷMUFm';{w;ո.y;x'|_8m]܃{/E~'Uoقsʓ$ioxkX5>‹^׼9xeXCƖNzk _Pu/H_b 7>X#5X0hq5_7qe ʕ_߯A_-N1$}A Ԟ`%`4! _ i n]@?C߂(@} X150?|x! CXxDFAI5??HA "_2 A}DR~aa ~3 H ?pauY#zD5C xDn!y!%vG@zGЀ15V4!,nFaNX! CZ0`0C`bi@5iH_П&5CRw=bQb!?0"1(@1&"7 59Ac1AdXC4x"/=zKmGP8A`"E6\"-v`"Hd7`"`5(4cHX$~J 5DѡH$ Id3$5T@.5XOΣ=GP`$%=5pG~cH@%\@V l+V$ZEf.nDFP Dr 54aJ@dA6N^]ZúqD6<*R>Z^ ]Ab΀XC_X ^58@p 2^%J0%&"o^J \F̠@ddN@qF1XhDL 51I(',Xfnre #5V( j6 G_RC$CI+FN+gzf `~+z~kqk?Ԅ++kM,빒+DZ+lk,.,+,!30/,@vlޫ ɮl6ɶsʬ͚lrmUlVl7l6*@@@,B&Ь.,vעl,ٚmo ܒjtr06,(|AMB_98PG87Æ[zVFl͊ rnbln-Ѿk9, 8D+8dUpǾ6CD*W5Ϯ@h@dAբ銭,n̶Jo4TA4.jz CM/ɎHlɎHv/~@m,*Rbo/ kݶJ+0Ȁ" d9$,HծÎl̪ހ2sip?/nM9C94l2 ɎJϰzl08@ |pM0Hm36,q+64 D) @,B oB~&`(@$km @AD+/+C7n D7/b$g,(qꞮ~r3(W?rfV155O63p7 8>a3937<R=3>K+@Ks(3BKA/4C7C?4DO4EWEw~=U:ԇȬ omH4mHGƹtA>QGtIoJ4*eD,FA٘dDQoMSoDSTU DU4JI)S5})uu݇VIT/R\_55)u@HZZs:`Ip@C=TD) ::X=ąx9C<y K'boD L=h1t@HA`CAT99;@PEa@ĵ:0HA!B @c{ԻsW-D:D j6knoMfpq#DUEe|7UttOwun{GT|qp8}j6xvm߶FqwMv?6Cf?x`Cgg7b?7K7uG6|`GDPFBVmUWXS!qV-p, Fs0H@n \9H4=DMUU]IO4(H%R8W5FoC8TLRoK9Ɨyp9KUYNy9@ySGCՠRcs9Vi՟Mozy:zFй9؈1 xV=1AI8TeeG9JHHБ7|r+,CY8@,CS GHhQnFCǻ@\?,L(yvŹoE_>XJs>k(kޣ9?vK`ɻL~ Ǵ اDқ;4A0ɖt Ĩ| ~,4@ʥlGtVT?I)Ӣ@@,hV|N'4V @wDXcx+XC8 DC!2HQE9z=+ 8`A}\'6_'Xb`#͝?;,0ҦYiƭrΡ^ N;9[9@~n >@8h=@H`dJ=L4:ϱ~;$<U.j l:R (rJȬt~ԒmԡuzerqV xesn *ֱ S&fi oꪫ=ƚW1&<+^b~垛ޛ ?_\q%-5ߜ=3M?1\]emuG|}o?Sʟ&襟ꭿߞ?_域ߟ X@ T@>)XA ^9A~p*P@XB)T YB1 iX6φ9ԡ Aa T!R T"xD &щQ" #4(/Z( Q{C`JPb&&BA5Pd- JAB&/"` IIv ,0A&p .!+t1)*,eiYKVUxYq aQ0A Pl UXyd.+T!=BB% aIpu@ LıӬB'%0/.pG &h*ԠVg P00 ^[H@*u Ҁա>9A/̂ ,(BNxObTTJ+ ,.)+B&x1tSE+`H Fea Hq|j,1)!:q{U0Ie5Y'aqx$Š լh (vC% 1U#zV5lczUz5hTYVLO\#h1E,`R "g)8[ c t"`q,E v[R+$^@M`-lԪ%i%؊2" e;\.,^ف"+ @r@V1MMB΄XՀ2%!01CS d,S41O5Wӆp6g6}4k7'5Kh7s786889}99:S:::|!,c@I (*\ȰÇ#JHŋ3jȱǏ CIBM\ɲ˗0cʜI(ɳϟ@ *3gAH*]ʴӧ JիXjE(uׯ`Ê+ٳhӪ],۷pʝ-ݻxMkw߿#+ÈKN̸'BLŖ3k|3ϠC? !,r H*\ȰÇ#JHŋhܨǏ CIɓ(S\#G0cʜI͛8kܘϟ@ JtFwXӧPJy4R-SjʵWU2JٳhIj5FiʝKNu]} LXˆ+^.ǐ#=,S)c̹3N͞C1ӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjTA uP@<ʪ۪\@\{ `B%* 쪴+-Q(kvC/3H pڞC-QT +C/2ċmq9KDD/1`D,g\Y-wj%D-PK j\V2H F+"E+g_@P/0ZE-@ K530Ddmlp-tmx|߀.n7G.Sxgw砳-騧ꬷN n+/|'7wWo}w}ٓo>G o?߯ +\@!z,P H(\ȰÇ#Jlŋ3jܘpǏ CJHɓ(S\ɲ˗0cnIfD8sSdΟ@ JQЭ㇡g[B}x%T˴0uaך޾8Tc-Y;]˶۷C(TWMP *c,Pv 9sN< 3,d1 ǣ MmO=P{ɝzjBRSm5b(d?=߀ NB~WҼQ:-?*D=Z 4Xk5玭7zvC.?"?3Of?&lxrTijp*>~'& @, zɱ>/yX^By,dxS t,w Y"%+,WxPqp`3@!"h.ψNty#dчV'M[ `#MN{ڮi?RvC|jRհcZۚZεSn^gUޝ`;$N6?".A"Ѓ1}l*ɖrA])#pKrdDED;ўv!K[q0o}2&v|";|;v2;oz G.V@AE.= |GqM+ݙ^o")ʯmȻpqO;2xs{u'KgL+Rh|փBjpSٗw" 6 ~=T>Ӻxt]!׽ug7O?@[.?|~1֧UVmoѻ;ĭ.['%uzb'f;8O*X'SZd-~׮_~O8O~c?~eְ]>ANU~6nV`؀% ZhaxxEV~X`v uFցK's Am q`)f.ܶ{$Z/xE!s+Xم@ '{h&>A/Qh vFxRPS'izOŅdHU"ZdeNwsj!xH"ac~rmgyNIM?gb'0I1]s 1^>Gkuַe)Iܙ~By{ !Y)W!Dw p 8fDj2:z؎P6Z*) 1 }gd6pnɘo|Zʖzjmcit؎czl/ygǣrDIk:yDpϦ>ɧhv-)mC8:v`g gxhoJr蘏X0Tמzr}ۈyWocnȏg!eWtW`qgrG'ɫn蒾YrВ wy*q"pJ(m6moˊA=ǣEn y !*t.qm%'>9t2 Qt'Y;ɺʼnA'wrr ٢rYaAW8yWfy+iw~RzF [ygx{A[xg&e[wŻi;;Ka˹Xש/7kWij_h`矆 {Ȩ;mm;zo; bSocjjoAHطn NX836!$V-,ۢm4|5Amk&| euAlREHJ LR>Ռ   fP͵ @  QP͍RA>~%n(,0Px]-pPPp n?DnGJLP.Tn/3^^`<z Aγ]۷|P~P=C C0]&p% `).ln= )*P **p- nnB@ ,Qp]P3 ѳ.&@0 >/  Rp /$_ʠ 3@ دPf^1=o'?G DPP͹ - G׼ 6.}ikܐ /p4`@ !*.O s` ,@)^M lO\ ɠ͵ ^}?4H:0+&A$'3hA#/^C…Ez%0W"Qh@\➈iqȱVY fF`-ftPlVuOj/_-#K2mI >a"Na ^wpkJg"kZ.aʤ&ܝ=/ jpO6 )̙1CD&]葊uC:X$H A rТ,#g0\s Çt۳=d]{v!:l0 LB왤B`I(Ν<Lg/|TCJpPpA:Ȉ'Fu摆):'qA `);('AEtyA hX $,PCE$q?Gz9dfq22 uٱₛt"rY|p0*dEe'4H} =y9Lq:$I(*5L3P3TSMSu4T;@\M6l3( 5ESXX Rh8V̻Dλ*6;z΀;Ψ]x챟 [ÄT ni2};-4̘f馃@ʪѨ:qVEL2D3 pvb~cLḋ eXx;;.찐G.aESy/ZU{mV]UAYbo_g{ENEMػmKaDRxun략T<Ȼ6(QAKz\\STm;P$2}wD9.J B f'fHGh;ƘDʨ^e!#( |n'`+$Ky杧s0zfIZ"d06.x;:{@ A2)8:{~{&=ٯ^@ ڈA!R{H8 #^|G<?+.6RMrZn9n-V@Cl+0N`I`sVz ;m 1d=Xv9pm XG=ܑ4p@΀oH;aQo$q%,eɤ_谢A~w\%#G7`oXNU %>Լe*I` &SL}l`JЎ10h 3UkN.{Mp [F!i@c%l)QOi& n~s 0R' Lb5ySt48D؊C%A"YT0Rz*>USHTƍ,$ժTEM,,3C|)W٦Ҙ4~oVD=V\ !S ^;WկkaO5ؽj2*bkԠwт8>BS,B4h%f2C$~Џg*lδD:䑎"B=CZ]moܮEUY{βens\FWLrfWnwx{^Weo{^W֥o}{_WoW&p |`'Xp`GX0i|a gXp+aX#&1Z;_X+fq[bX31gbX;{kc Yq|d$xKfr$GYSv|e,g9Urb-Ycv|f4+kfsmf8Yus\9YysF{&|hDЋfth.ғt-}iKӛt==i& @ԣ&l'Zfu!Q:IJ/lk(Z׻I]X:ͰGcld^vW5lb7;]vlŎ6kcZ6m-(ImjfAY{86ljwv[n~&{(x;fxÓlnb;x­m\{;޷)VY;G {HEp\41\H`ò_ NjV[TP+!Bζ׳!,`X ]Yue[n9-^1a;?~>ꉓߢA-1X@4|槭j l@YUwvw=YǾ vWXM.P+pK3xG3V> 0C4<㗝nZ @,d ~Hxmk[Ծv֋@4ko0ޓ[7Q_ @R>|@1* Kb?7*e 0A[?s7};z= ?8;d6>2 (hZ+xx'PB.B0?=ؿ=[6ڽ%C?$KK8CX6= PNhC/\t\H̿ۆ[t 5ǫsr`w #aQrx zcQ ؅uxaQey\Ib(a{XQ6.4[OS ˏ[x;L0;9<99#hOC@ >Ю$6J TP%@AHI}d9@$(+x5[ȷG<3p@ *X b{ \R8.@Xrhje @{QceX@iPPSH9 f5#؇ G @lK?ج,B`C45he<0T5)} DHhY!6$X>`qYmٝxO`G0Z^XS@fw;?zUY(k@m`pt( wWPa{eѳW- dP "#Qp( 6EX8UeXYz|ӜH~{?$PTQ<898K}HLYU } ݖ 5ҍP^CVz^-Ah#'MV\h;A}<_mu_8:t8 uQ{hHugXl"*]PK 9s0s3[(z֏x{'` QkQ `,SBXG<|xh; P^? XС$HZuZa}G1* uG1/|G5)dW(o{'-_l}<% xG03HmdpꕀCNE`,@LYa2a8DLt䒆) >hY 5i^]$΍R!UQs@!!Ciy|wfiؿۅq0"5[ڎD(<:8D?^ĶE_e_%)}t1i0{cH(wrfQfʍ˅a]XHdMhddOE}O&eΪp #"b~?(P'^ޔ Q>aOEPDQP6CJCI̔Ks_uzP<Ѕ|&FcDtFufcQ]10nx`q-m(`5 ȅo:31@zp]jxj#6mꩮ8zF\hg*85ɖ|X#s^Ui띌ll ҢJ홄H4%XI^Kf|h`;h& Cnnut^.o>oNooMVo~oooVڬoooopNtX~VJh`wK^<nP!U  M\E Py2aknz)6՜!G$M<Ì`#"(#p PE@`Fe-We_1 Qq""x2()5Y>u`db&=y|[!d_>b^UmW]\|bdTHwaUP(w:Cv◴+X0{QKumwVkgzׁi{|~ume{B7?X58ȃX0c\ `AM <8)Y7? .@%@1Kܛ59a@ E.?P""l"? 7A@ >NPDU HA bABkT'ŒeXmBsT8dGwf:f6SFd@E*fP A:yaӘ=L@gF3}L5 NqӜ -\f3)zό~sP?Ot=h:{a!8ۄا"DMbr:F P `(0}9Z0܁?QA-|@C!j@ɇ@Y%DYX B|"@>Īq?bU`,"WDYG71ul恋$yL!(ځP`j2o@=HaZ D 3!Ҡ6 8ҕY.Qsv p;BB"~CS86Az#ErNץ 1]Sc@Gou|7"Q/{o..ե/v]T!q7 !=.kN~ػJzkbַ4 @Op3*%3N~rQ"1\;2^2]Af>HϬ5{%͓12= ԟ,h&gO|&@Z.e}R0 Թf1 e%4-=OZԦu7-( l@ΤVXo(Jfh${%..Ң],.܂5ʸ޵Y-I2֖:0mmseͭ>Hm[&gw-p#ʻ?8{' S83s8C.o|&?9S򕳼BӼ69sq>9ЃBSu3NУ.S*?27 B|Bqi뙯C=>By .@.}Wዸ _?oN6Ŭ%.`F&V M f`Y 6~ _ᅠ   ޹ v R] `ϭ_ ` 5!>1VV>fa\!v!izZa!r2aa!"!-a!Ρ!!a"!`۽2hA%":CZ"2B#FZl""%]&E9!!!H\<9(\@C.['"])>EC>.,5 Q"\@#\"-ܳi 6&_**""\,*ą-ZH#QDH,4b+C53r:Z#Ͻ,ąqc7f7ƅC=0P@m2 *܃Aآ=V;䱣?4CFZl-D:DEuu A^90@Z$DJ?%Q%R>RC[> T:\3$=\P  Ĝ.?T[|Ot i&C.?CY=kV[EsERj[F-2&2~gKB}}r=_ 츖aᖒDSM?ؔE8S9*dBHTk1T12ٟZГ?0,N+naMAD-l4iv>iS2eAE,b=%/[,c,mLjZЬ**J,GU,UUa_7ڄ)W͘?ZX@vmZ?tc2ڂSp*l`ΗtͩVğA? ZV:CŌۢ[73 W--"Wf?p_g- "QՉY82Y-_\H/\_ / &a?Zޱ ]/v]跁U// B0'60/.0G+Wޚ=8wpn-)z[ACSX ,:2;C.̋eI>A8+1 TF9Jrx1 mdG~dX<%GPm"]z ;_e%%'Mq[ˠJ#b^1SLQgTL܃ EGXfP9EAk-2ؼL̠)/LŢE PD 04̅}AWAZ; HoB5 rrTi[ 2:YQSP25?T%b/6c7c?6dGvueKe_q ew@MB%` f#^hvQ6,BdUvYAk[k#Yԣʩ6m/pEqYPl qo6g&0[6Q AluZhq#gsKvIkB/؂Z<y@|wuZP*q A-,@~ZẐ.B x@-[.̂6t;sT[/[|xY8kx  0"B- w/[ A]Y4l( wxǷ;Ct?S7AtX%!HBvSa99yx yع (. A 9\-[@AE `-ţG-<*A 8x ,d#Zx,0 Aw@s9Ђح B ::: l6 [@9ByʮE+8uA7m7ٸf?xp37A%H`;wHB%ֽ%O t!`»wpS${jUgg Acz(7s9gww<л!ܻOyWy\(J ß6 S6+TB xӏxA{%[E-Bi 7>\AkZ}\:&þ?H,B,4?lA,-,Hi{ʹ)U ]\hd&iJyP3 =&'*¤ 귃>DL(s dKʱbG%# 듕+j*ՙ h O f %^6y&(2f,h^Ɉ^֜ib K(ȉXB8]Ë١ ;୆XH=2^Ŗog:Vb&, [*V16 wܟ*\e s"c c&/nhoza*ȘIgR% y1Ο(=?NÆ&WW(."IXR=R}\b_ yl#IAʟ|B/1<cHQXp ' ]4T ~r&2hAitB5o[3Yquݰ kB^<>aW@AGQ`ZB , H睡8P )![3 UD>To(u[H_VVZ &)H^M>$g~O-1E D|B =/%x4a$#>JheBN~b]j&M.1Df} V@3X,-t` X$M'P0ASPA`;)O / xaT\qI-eߔ&%$Fl" gtB+H BAS uC' v"-P <Ӆfߘ c +b!X.]\j䗽Ք lJ{Yӊpy gZv"'B{ZU@!,P H?*\ȰÇ#JHŋ3jȱǏ Iɓ(S\ɲ˗0cI͛8sɳgB@ Jѣ{"cҦ虪0S|` xx!Sk 3E N^XF6=+T-Wװcǖxtڴ8 jᲉ#D;oOK\#艊wn#kRG֮V÷3DIߜ|x֩gr/So[}ygT=To8GX5`gn[Ia\Ru(@Չ0~nՂƐ8И`Y\bB>Ψi% IgyI.C!ƐTI*ٔgZ"TA8pd=p=SqVUjP-(蠄zA~RUuKiGQeV'fBP椕 ]G)vPQ!UVfp4GO+)A끿6Jٜ J,Fiؗ2 .hC:zUv^\k[ղ&X\,0ۘ7et;ԭE簹~-zra~ѻZdꀄWؤ 8 cNW;r(\cdu%dniti$S1saR]%oc[JӾFU15*fr}?bdG߀ͬVD&sy'*rt.Rl9#SȧYUh*sN%Ţ] hl .v9t7D|31WQWLKe<ꔘ[o觏Pϒ/w HL:'H Z̠7z GH(L W0 gH8̡w@ HLH&:|%ZX̢;B BXAo4A:4"{#/ -4D #ZB"]\l$ #92")&,1=(` iv(=!4 dCNP>!TEL| qIS%C ȃ BędHQDB%1M~D(2# E@y G$wBzPCh>uf292@> BHF|XF9C`BjQ@8D"${+B,uc ю|(i`D!V@6f2ֲ;e0 @!r-a@lu+\K[׻ laXlE&B&p#($Ӡ6WrVnhKڹvek͋*,f5{_֬ 8EQU?! a|E>@ ?A(aDB;#@p"!A\~RH>@5iOӠN5%@pTH"BT##{&%jS\̆p WX@o" HX l#P4Xi conB {񋄨sqG<υ>t hAZpV0> `ҠK{җ}s,Y~ !gq*C(! @縧!+ϞvϽwG)[{(p_} >GH ON7 ?Vo `)(N #bEWq#EA_` !<%6GtKdB`JsהXup75 cHZ\U{e @fyS.yFU@\Gl5  G@KR 4h8:ȃYXGKX dX^Py8Gx(D\|Q`(8e'| a P ?pV~ ~fR2Rr"w"U'q)br d0Xs{psJ sM'Pt@ B0CwJwtIdtWb@[ط"&pZpIp]\'wp` IOXYx (Hx^(ȅx.`g o 1ⴆq  dPlthV@  Y5v^!;ip[\|C#k%u @a@Ax~(|0b@Fb 51F2 QCC`F0?rfDdE&8JdMƀp aee]v`fs P)d=W q0 fVp Q@SYh°F`@(Wǐ %P10pkDdpNgٛ)gw6S0 1ɕ Iw@\p uxy%Sp&)rhƜ9p癞В p Zؠ0I詞9L9 p 7#?0 EqPQ~R| RE:r(b-嗎$MxSUU@;Nge:T?8'SUC7NT{pn1d<fb` :]z X P.g S۠Pyz_  8@ z*fڑ| N<ױFp2JI+K۴Q[AL]R>& װ>AApp|$B1Y% KMT`p\з~+z/w{kQ>{VDOtA;[{A=۹۹D4?{빥>ۺ;,ۻ.⻷+{AۼH;2K؛)a 图{ۼ ;{K{A2SR\DqL \,i<2!I?1)ӈ.>X>Ie!8 p+s@+MLf*'ZZѓP[ ܿGQ@`:<,?tSBpn\ܦg1m*K`Ҧ{X B"jjG<†L4P c`qȉ3܋6G?wW=]5?&5AS]%+rpTA$ ?L}V˻ %MTp̈y -ǻd1 A4λsHW  O`(9<#=v 7 1ڼT=tIǮp + ]<3|]LA@p0tHMB`iso-;M ::  -ic|= do֙-1, +؃ ,́B3Ϋs)V&42Qdں}@}=;]E |RqSxQe4ޒ,],=== uL~/|;)!GϨ¢B G*Q[ +9&!+]o"A5 $AP Y}AK`K-=qJaL, &'umߪ}^:E^,XH2,@-`[PN4O O~d>b3/P)Q F%!ߎu r1t0m]eA~PQEQ%Q7F(G5'R+/%!TNZ> / t&a_  RG_'\\%%Z:WAXXq^e`9 yy!a)a )}py tsp=; Qn->cb"$fb(bb/EJc F)7;P@fF+α*LKHPV o$㍌Ȋ~fjs1lƆllmzgG\ohiFio6d$e0e`?_&0T>6!ߖ>y`ZR`q$'qgqWr} !'cEZr )+rs+t7{WuX'g>O8 p@ t `zzzbgYG@wE0p3n^OmOڷ&1 ovu0;psayp0 ?H/d7k"#/","!D@`Q AmCc *X{,!A -dΔPҥ?KA% feA*]y(K|TVLk?tGrJq1KOS'D+]E;gܹxXU^1 I0LxA~8xO  P%e1hԩUfkرeϦ]yg (8a!Fv2yrb\r58 x&`P1Bi.!\F#)Z-&¸Ъd€s41*qE& Tɓl tdf)4lK0sL2t*:<7;.%fÂQ$>{("c飢Y$"p!OM@Dbx?O,RUu.eNjX9N,j\ZsX% c A*Jg{L`)iY{dJ( ,Pb:(VlJb*_<] [4LVxaaka#xb+b3xc;cCydK6dSVye[vbyfkfsyg{gzh6hVzivi~8jjzkk:l'lw.A$ ߎ;Vd饈Ȣm}޻p Xh-JC(!·"sJh{`W@2Zg/UtaEfΨEXy-feəeŌXg Wte ͨbx\fWǏfC e Zxak1u@Of  4)g+f@ ԦI.h ]Є` K8ڈ 2# ƀb=;-L03($ \ `1s"3B 5xfւVw1/V"Qf^XP&L DmZ(PhLn# z! ttZ`ZElxÜf&ތ>+Ȃ8LBf*DHm: L3,$*9_As*8$hiLЗ,%z3 a-c&crtk/Mnz؃Jh3pVB*D+a_ӞLH3.!fD ~6t@z+8H3ZDf+@gFQެ gAD`' 3cA/4f/ Z;1ӂff^Lai͞ҙ/@ E+ ' j += 3H!f!`Ԧ>5fM`-lyU3#X*,2S*S*3)XWAY0,$ځ-Ă,HQlH'\+R0KA xفPbD4m BJt{ Y2D(al)-{~T8) Z: .,c[m-! W%eѬ\ 2,^2mfН @ެ+bYP`P" m-C d+oģ<3Vtf.gY0ToNM@,4;,J \` Z-pBB0(!ax8bo.͖ 9)gbȜeB-0V^s̤&!.8M20B,h`V<{D=Fs鍥p9Wh.!Lc…3[n )K`vS4.Sf5,g3Wnjk]k_cbF+B J]:/>;% X3'92B.,3p8A/pVH `gv-3{/F_׭GBsoB[̼@4A8cU@ x.d@3% 63',aX x3Z\@Jj󞌜˹:b^Ű4$;\DN@!:d$USn6XGI!ӥ4Ri!-m'G٢MՁ&Д&qHLHyv'}ZN-$1\p+X[38?W0"s@=(iLF\Yȅ3йߓTˤO\1 ڱX\ȅ5P˩Z@3ek2,v 0O ʴl;YYj%0! UQYhQ%U\XPZqƟAYPKQ!qY";3GOB3 1EiSɟ\ӦSɂ."WSӮARSFu*H-(JU&T)D]T H$UqT*lATmUWuVW͙Z8?QU]U^U_U]}aVb-Vc=VdMVe]VfmVg=hiVjVkVlVmVnVoVp WqWr-Ws=WtMWuhWw}WxWyWzW{XW}W~WW XX-jWMX]vWؑusew`Wpp/V0 tX~ijXVbX֕Օ-|ؘ}Y kX=XYeXݑ_qwӘ5WeZ5~( ~j؍Xmm0ْٓ凢V(Zh~}[n=Zoi[pٶV-p[EZ[֛V֠}\ȥסU[m]{{؂ u5W\30o @yiZpXq-[\ WV\n]pl5%^kUV}mȭ^ܵu )zh3\0^-W3i-ȅxZ/ z` cX bX؅uxa j\0yH ޭ E[]ӈim  Eu(g th|@tXnhXjuZ8tpc\abVavk _sr`w h@"^_&~(F `&` ` ~6baq0 p a!fF "ٚu1^^,Na-F Pxo`8Vc<a?⨠b+^@ V&vb(b>?3`-2^ǵ^]Z5 $+Xh؀z(1+0W Pf/h8.@Xrh q _(0i P_VoXiӸY v n h{`i`0V0cPY݆sq'8gohh`^j5fdui@{ Piphh^.i 8>t^g}zln6F p (df.YgpjjNniZXi*iFcF geeV^v `(iEmȞlX_ @rk6WX ob~#vzgcI{eVh1hxYu`hZ`gP|hqnjӰ Ƞ plm`t@m0h~f%FmfFݮm~lv)prX_f` ~@xd؋(5~omj@V&wpE 2Vgj̭xmYAGEhOr"_  ޥgD^GHw s m3w)o!7 pr>7- ]0 V?kuC"8z_~\tg A'u6Y./w$GOwu hV91of>zfӈix i1l*'1s̛;;9lcѷ83ZiagQx}7PXg]\'ĥtgk2wJn't3&9XO xoNaTU&I*$Vh9Y@s_-ND :+j%a9&cJIeiY)Ro%]y=rZ`J3_crULzO>) ((J:)裐bjuo r*TtꚈW:*z+%kvs9J:i7L7*h!,J;-Z{-5mZz-;.Yks YZ&3-I?+Rd{˗az ߛ_ 0,ZkpP ԁ@sܳM ^l}"ˣGb{w׻!qx;^( @$|axu{%$C~Y̹k'GB7ND6F1,p`q(@2vaE0 TiF㑑#!)IJr X9泍pT@P! 4N4(%p:z1)cb$L1CV%1I6[MnzsRΈ0,r/ɍ2dqa+e9IzR $1IP" ! HcAƺsjH)Ozҕ%3IZd%`Biu}bSr/ :̎ d&/Ɋ@`1AFrZ?ff#l #:$89wfk? zy W ׍8XjVˀI50[Sd"dʙK3Y@:ɨڶ⮀I*âSk!jUNΰ@*&՞8B-&~=n:>$p+]:եB TEg3 Ej⵫ k_EO{k\2׺6U._"WamoX~7칖j9>ɩH, &XEt b[Hxux}3pa!(5ݚ2"09FD"p>e:zB4ҡy=2z0?']G$-G7LpD{vTb -x3ih[@ 62uXc{Pq\"3d*1)<op]ɵ!Yt'lߘqD1]Xb0氩sjA󐋜DOr9xP|b(V7tQ^5G,bnW$"G3[^}ko£baa2F\`(< fAگ#6c-}$b!MHry;l  Yq%YU-oTWT@AB;wPsF/TnE>#{`V.*cS-6g[#!8^`b-[}L(-y( hXHᦧ$!lCd[ vS<)Ki2/H"`G?|"3FƗ7lf_? [d;qEN.Ϳ3INx#E)LqJA*/\Lyq "M}D$ qD_O[2`㥘EE_T\iyeWS`F  ]]@@E@4̃=t] ȋG%Ȃ,mn`fduE\x|Gc8dH@ϔAqrrH|-0Ȁ ! @yY;L@Na@ ҈h_ EhؠjF@mGUaxa b"~h_@ %RݼbHva6;E!n Y - ɠLK b"0jɟ .B 1Ĩ K3:#6bQdcJT㭀\#7 4ʯ3c9 ;rcM;;+#>\9:K#@@$AA$B&B.$C6C>$DFD TE^$FDn$GvG~$HHd$dCdJ$KH$LƤL$M($UdKOfdMP%QeP*CO$S6%%TFTN%UC>E IYl줬8Z%T[%\eM2REIQ-*1RY/u( [aj\&b.&c*1Y%YHe̕*b%Ja~Z6h&i&Z eYH?XAsE5Rz&hOn&oMjC^X&Z&n.gKs>'t$jFV^GPXYYԦr2xbdty'HngGx'g.x֧'~~&$l t@E/΢ا~&.(G( N(*h:*F&Iv~脊P#((*rʊÿ`Y$T(Z\hYG'GQ i@ƌˑZʋfY@ Z &B D1"*c ͓li@ ̠@A$@ 4?LJwr`#E`D_S,)6i)> Z)bi0ppx)Z4*4@ @MQ&ͩ^?KY8w&AzVY%YQ-|lVQeY?LP *~CYdyCVV~AREWOEŷ~ +>Y ?iYTl+Pm~KQm )NT> 4 0DM88 O & l4hZt Yl^&ld xq`f?l&YLmYHUj??s\Es2o:YYV9qC۾??xEh?PfM-ԍoíY &/r5p=/oxm$C>ݶW~ZGI| |e*<|Ehl$'FFrή_YCQ@G0"1fZћr?DYpjNXқ9ey??CYDYDGY&CJiZ 0kkvDqq2ˈI1Y?lN??Gqk!ޚYDK'/ )wWX%@Er0, jgz*o&oA?j2 e1)nqw&CYxYDYۺ~e֪?[YCZ`ev1si@ gG3";<á(oLlsYU16e.4->OЦ 3s\[ @1`Չ[{uZ?Kɪ s1'2[,2LPO@v~^A 4+_~3%/ts3Y؞Y]8k&DG$6z-RR"Q>%Tx&DP^'5Hs:HvjsU1DQYȟX\ӭ?Y,_A RN0,IfZsuT^l|BN)g1,)!#SEE]EsŏXf!t~!ix[gZju xb+Hx?rw[(zgYԈ2+&kP(b72,{wᄋ&w=lC10':$ &Zpn J111SιجOC2⬈@4ˍ"kK(qx*qڷYx]i,XJ=ʋd'+ $䚳虛Eo:d n19j;A  Tʘhsː~d2cƣCyP::Y>>ɢ+iFƺE׺K纮7ɭh6%=T:'/{c* n44@BN%o;wDDNC-/C!C9PAU~;ǻ;nkvu3GNC?,?klG::j|뿒ź&W'<ȇ<.K't,Cj .v :P뷾3K3_IpKJL+7t8(y>Y@@_?shG>8yElCIK#cDdz @D 4xaB 6tbD)VxcCtdH ׹y.NdL7 i̜ =FfHgDZȥ޹35BSQJ=)5R|o-z4k1Zpsֵ{o^{.N/;G $.278d\qB *gOȁ'c̹r\GlrϿYvvlKovnќ\LA}7~B =J4OD}H/TMI-Lcz:PEMRM-UTU]BNwtXeHa\uunɍw`nbA%Pk]}YL"6Dcm≸iYp\Ue35kCVv{bp=jzuܚD`pq'uvAU:as}#:#+Й'َ6aޒM3_Q"#?c`qZt_+0Yo֓^͔(hPi~ډǹ`jN߭ZfL~d(;9[*qnoa{fuƍLy|n֚"h(#M]1eau|X)sv)f^tL7uuGץ^Kp_orqxeO!bLFzQsYqD9 pZa]*`:>. &[Փ VXlשNA)T YB1<قhXCO6̓>A$D#6DD(qN'E:QY\E/~Q \ɈE1ƩitF7PXG;yG? YHCT"HG>$)YIK^*PINb(IYJST! qXUF2E ]r Pd`B2 ^P/yYGbtlA/0G)b!+dqjұ Jc8=q!.I|R%V@'&hX\$dY I쁓BP$ :ӑ,,_Ѣ`:VA%`\%=]p #zQӚjtɤdnjԥф4y=n 2j-4 KBԨNU*0̹vvg=)JS! ! Jӏp$A'J` 5DX٩U|+tQ^? NZ,DP)PR2XP HkCtlE3z% W{$B/r; b;DiG9r8y܊ }*P#Q(b>;a%(D_Ё0+mo Ć{򦷛O g9 G,`s%\)P:u$l9aJ&+X\Q` b*T'MGx{%aO} \n%`PS?rQb'\q8 \Pv zR:ގX-Hxo,Ёs'c8r0'^,j^Ϸ[?G#=Sc+v^TN1oG3T ]1thv~_ե65~LTו%hkT͝.AR M Ύ˾Ə:` PxA: fK4oTjVBp; X!nNMQz!ͶȎ dAn nb\`A mOoR/PPaL)xᛦ `D @ߚ@(:@(a ]OJ.vJ- ( !IntAxr{x(p;/X bjH  ,NJ@ p @qߒdq Q-`:( J j- d Ԋ܊ [-k-n ŒҖKiIS#AJ "% 00ZM̑*nxBxx' 1Z 8B`)l` Li(Ch vdw0΀bl`! GJ B@|Rhv_-q'+f p茎fnU)髞 0K*&+L 0s^i"j81( ^S4k%=J J Ա)i!.S: ZX p`]H lpaӦsL+o\ `,}ʀ l ^A-0.;@r+/H h?t9s= B@>CI*VsDW4` rLEYthG @fAvT`B3TTHԏX 2XaVnIJԑ,*a4d5LdMB:MNtHTNt5TO45MP]NQ.QmP :Q]&R#U[ TC"f @ OqTUXLD&5M5#UU?V%W+TqU75VTs2U;1FcOM ^;3B^<`n ] V_H3k bL\V͛u2xn6q.`\ p]C ֽfA~4$@.:t :u,;-ln1'pP \t~ k=M.̓ nyz=W,) @N+SӰ"4LoMč04qq%,P8.+ãTZCH"@<E{Q&38!EU@F> ,ђM*hDi^C7渣`4@d"n)'DF3D8?VjlY?%#@$A&2Ɯ@S#?S8!P]O1C&>˸Z{:f:PIaOD+im\ahT#jUK> MsEDʬEѤ<C$O.‚ 3lUc-SZ!@U#A-c9ăgHDδ"׀+ в7` vj\!BI({(CF#ASm5ZCDuŪo"@ܣ@2. o k%) 9Q@VXp+ ?&\DE<A+hg绯SDYfnO.@ ?D<5~CNx ppovOX6+Ӳxģ C8 0t^R. _kLerx@>(Abx3#C DEd4 ϫO􃆇)il}WEHˆBa42Ҹ :Q xF0?F1Q:@7߈3 uУ?ƀzC .1chg~ǀG;t1Ґa =$` > +kxB8"G=2l.yHG i\pQF^ c&9IP@380Āa.\2H83Ft"D.(ě4B !Cx(a )9} JHDPt`U7 *" :Wxb@ P`T`(JW!,P H*\ȰÇ#JHŋ3jȱǏ Iɓ(S\ɲ˗0cI͛8sɳgB@ Jѣ}*]ʴӧP5"JիXFʵׯ`fKYaӪ]˶ųpʝҭݻxB˷߸z L8È-̸#K˘RysϠCL_ѨSh볫c˞UiBy ^,+_nУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfa_u7'|h(R$"FR0vzaP%9ǎzТ(itd$-beDVilב#(y@A1Qc

vydzD)z806v)&W*hhYމ$#.(e" P@G`y)iOA@y柀ꪌ墯)@XcnOA$?j*@ezèH"FW"Zڨꦤ'zcL:ƫuer-GvPvcvx +N]w$ _0:\g:L2)klsN%<3ꨯX&\IHbjkn4,Ywƴ%`mC/q<ip}jg_˭w]9>冈'3gykWuo.5nN8膒KxNQo|K(D^ro}COxbx?=9O3,ׯO<ğhH@ŨW`Xā&qU$0AY֘$3舃AAxb. ATpGluCh0tҎfEj&BE)O T'hZY6U[241p`DŽ%XFxܔcjI=J_ t-Ni""g(IiE dJy#VE@5" "aDC-Bp'd%Bvj_) P=_f/nej d"Z̰r"b ?T*mGcKphL TGD'$_)7%D]TiAplqzކ̧tX: @ςVH,PYXaP* uZA"%i$SHЄ c}ZaJKL2r䷴ײ%-#RN%2%I x02E%JP:uc*Ie+Ҫv0GҘrQ I"PR5 +Xo*Ts4%G&S95&"VKԧER*LƈF>YBSBs]SƳ6ImKinŬL=ĩAZMIeI]Z-ʐfCC iD^ZvS ,(#"Qy)欳lQr'3 k۵&Fd8P1`L G:]k0׳OJ1(bޯζ &H9H"j!՚H:27c_Uׂ3׈Hsz4*%MbYZScZbb+9a!cѫ[< YXQt1_֪T,3`+^Ix('ֳ:5W3g{81Ϫʴ4+5Cү[J*1Ħ堗)K1T]*Awm?& 7- f0Syʙ#`!e(Yh])%dr_C-1~53‘hlJ/,ӑIܔWaJ~FVhYTX Z_@GN޵_k0D>-ߓTfE`*v$5i&UG+iƨ\ 'ѪJ"bGQ⊪yg"ocb+@nRw?6`\*_L׹noj6J\i'+ͮYx:tXFjk2*bz!$O"Hد ؽ6_?\]o! P~"~[˝1'S-q~|?Wֻ~rgO}bl|*'fnDiOE{EOO/ٗgOp>?3o7~7gx~wySdW| 8z/G=z9h88|H(|s,C 00ׂB V@gp |nG kEH؄ c`qXt Z \8c:\SR '=腿uaRfA8.q,R~%$ 0 {<%jPVHHL(-@W 30@ O0{'aA䉉x T@,a Xs`[agAuH ,bna &礇 ÇmHOpUHWH$rDepQVV&xp3؊ S 0h((d l/QeITq  Б|(C$)p}0Ԩ60 @bB0"{ `AרLٔ@ .A9E))pAY@ @`) V&m3"R f&"^@p   F/ #) `0 p40" a0@a{ٗ)  i  ș ɚ.2uy( a0" P  @ G0" ~ Y%9PI&pF@10 К̹%i者 YٙIvI\0%ʘ؆{(`)C0!A|,)2A)4j 00| &P XM9 @ 0 U)@_YYpH U) 0wj@nILDHpW!xI,H+@  @2 0Iĩzh  0"9"_@v: %z $ 〫 JZo%z:p׹ )ٝ P p"` ਐ*j *qɺq0 M@: %: $%(*ъ:F- & ć.% )L:wwأ`05{?~|ТUjG\T%H)`*`: @eY xF`VL W;Ve?a|۷P v @%2 `Ӹ2$E0"G0\BP~3c#r0 $*E."PٚP E 0++ۻ!/8 , ##h h+2 ZX0{0jp+IUZ7 +)1YA*?½S{h`*HI^˔`{)@$@0`]z@ I, N,  viH-Zl 0%O  $[@3S@@@p`9P "c\grL`ǷjP #bP *e0`P˛ ;  p(.0 p]f @s\Xz~\I<"ˋ#bi̭#R,ɞ<"D $,Kx\)(JfpREL)ѳ# BK0y> PSk]%5;lq@ B$g{jl#mjb@0KL="@"pZ`̼Kp,3kX`I@{ $;Լ*$p% 5 d$Uʰ,˴Y@ m> YMԔ d]PѸ,$ [Ձp̬<~pY(+KX%:"'j)Z8A|0-.T9Ej7-9A0FMˢ#ۨ_q;"tp mpJsF P) ss 'j W&W@" ˼* `"4 Ny PX03(dS % >ɝ+ 13PP^0#^ꬰ>;ּp @v˳l0` >3 =A>@ c.%ɀ @>a2H. Ep,,j,}~(i#<0.`06IDK $_L< @%\ꔼ~kyS\-X2\7S@pR` b` jʈP ϲNp'0 S"@$5$!p c #rN?֎K/ @ly}@ `. .." 9لOAC O%? -ʰͲ჈`EqcA zVh1Yi?X#bԄ@R" eL(|Ahr`F2JP_ɰOR2T$#a)/& P}A0e8 ߾A _bf/|{28$XA C.XE5nG!E$YI)UҡYM9uϕ.]ƜQI.eԩΆ1e GtSYnׄQRYiծeۖ`DB˺[]y=F;\&\8k߲fcjd̙5o&jUf9&]dCfkZ]meSֻ'^xc.q&gsѥO^uٵowC'_yկgϧ_}CWOp3@TpAzh?(@ +м=A;CCLͻ(ؑp6 H=.tq(@*Y,PDsqGгA*D,|o [J,Jca'  WgttV&P )C)b `P!OBxPO`z=6Ho\I4TTS ,Kc`qZ144Q5jqǛ1,_E V\P)CuS R@`(bt  XE/FCVYq&=U^|QKlj#Rt`!^I))-E@PI+ UUqFldo_we[VQ~dh xU(*Y b!btQ /4LXiN3ѨcBy2u&ryv|p+''nhma)޶i5c@< EOr3_VW0HaA5V`(r =ZM+ɥOtmtLh:Zvoc 5~z1]&hoy5vll˖Xl|Q[1;C<4$uȢА^aYP&p#&z:oJBt fې,y0l`ע8D" CI`}]2s?,"r(^f[Cu_HG䙁?h0^\AY__2 a Kz ̢kPm JGhw +VT @(A` ^EQY @̢R2c >G7|+H2/6y8?zI[$an_hL9dn-F|?xp*?,h2?0WBue24bE`:@3)`@CtCr"0as爇,`r¡ve{O8{n@DJ,89ݦ]+#΃vc|!8?(:fV>0w>>0p=\z ] SJwK|[޲JsomK]W!SX-A{hAoVb? ٥+ djaA W\o 6e1IfP>ސx-9X(|% i`E>.@ >3{.RpJ 3C@CkQ/Ah x&x'y(V+)>99)}t>0{PcH((( 1m!_Ѕ  |ʿ}ҡ!C("*A(t)dX_$iHƈ(ą + W4*)0œxPFxh`@j |I{ 2+ؑ&ut J,KKLK|T7:9DYKKKKt8c {Jl,LEP@3"AD4 D2IAP# D:4\n xL|$({|Oq*' &(4lN& xˢE4(,,7ʃ;h(+;8#Q44R?X)1 -=)Wt7ǫ>ʩ(0Hl X)D((P)ң)B؂nhFI!0[0=ECx`THe D݃)zh3 +ꪯ BW4,aa7N `جSNXjU1!hNJ,7abO QU>+c+dUf}eJ,2im $VS5TuEWV}hVȢ%lm,VQ}QE+9%ׂ-·{: (9:F@EȚ(PCP>vz@:z< N?8撳C>!/CP1>8AC*骮5 <.`M !T&1CxG E1T, 0S30(K1\[Os1 _/V\\%0ɅϩT1ϕ =U> 8ǽeխ\mKn h˅ṋ^s]Y]a{0\ ӄ]u*7r$+<8,RC:{tXƋJUL> z0=m0)?  Nӆpr2;һ4(tw@/efLEWL8J8xb(R-_S`S5Ⱦcb@6P6fg3_]~h!%ebc #jWn U`Vn>hccQ &AbJBpnn&ī(6Ze booǗFo Q.Hre Y @" 9X"|xr *8RD!4ٟ@`@f 35AlSx4p&gd]D/xHrHzK|U=H!<2@1+X (wg-B\1$C/ih23L5pQ 5CB.ot~0]W7CLo@*BDE/1tXAC5LܘH@mzbBQWt_w^]u\ou9tՑus@7tCV̓mDF҆Y"R`wM;E(/냑RStJ,.r#_AU(WtrM4.0EDr SRxV,Ȃpnu!Km#K TJ` Pu ZmoW5G`rGyG8QUayyql?zw5mjoGDof/FFwUٞ uzǧžz 2roL J)ʬ d_۟˥'H/~%$ITJJ%@~4~W˜~~LLL @TIsJKz1ʴ,h „ .:0 'R$Ċ7r<#Ȑ"Gh$ʔ*Wl%̘2gҬiM:wj'РB-j(ҤJ2m)ԨRRjhϫZr+ذbǒ-YͲm-ܸrҽ.(Y..l0HwEKq-Ȓ'Slb\x3ТGNcސFMx,gӮm6`řAµid.n8Ku$¯ʧSnbXu~;3KWgVgo=)P*ԡF=*Rԥ2N}*T*թRV*VխrYF*ֲHR&Q 4*mL T| S\ Y!CNd2d}-,_:&B*ee Xh_!(A C({Dg"Rb9kZ:U)o _{ [| dPVu(^,H ]#j X@MrhapE B"Z!$OXR|߆$Tp.f1 !/, WH(Ezc 8"2bxN**E -B#b1E cCZFELP1%S@VA*P>Z0!$qZJ(3~1[ "Lj] @ AM\` H(*# p.$0xn7Y;*P,-h,7uadEff QFi Fs+l^Ԁ#+M^,EXx1C+EUuPdEaQf \j@ĀD:@!~`b?κJH&o!XجYx6D}0Ia~ڵ V8nCA;^-+!q`ƕ0Inr>U-®^+ !`-^4- cXtя*!"ZEB$lAy§R. %^t3BB,xAX!i,UݹX? Xpa 6SR0KA ݁Pb 8&h\ݝiKރ!?P%M}i1UBI¸1`%L} A`Mi9Z$͟ x B.Ђh̀ `dNZ5~sYA-BW0HB^@}藘} Lӽ+tNHaH,,|Zz PA=[[,,BAt @A OeA.LA t@tqA $Y jÂ%"6"_@ "aH{)"#V/~%A.ȗ9ۙɂ A,Ѐ B|_ V$%s [[V@$X _` Ʌ &AB֣O!ྑXg[B"OQ1GdBed@ 8.4P@.Pؕ-tbۢ9. @:OPD  B)F!tYvIDN B^ --+H?4AZGt%t-.@P)#xmAe v Bf$A+t OGA. )GLx[fiZ?k~Dp? .&ITOlUN@uup™-jgQ |u&d;@(%CaB%8(v›"PM?śx[gmqiYO՟9Wwc׉PqPb~@ijQ~ @ h hJ~o:}+g\٥?T~b G$,؂-P[vD(:[a@Bhz))H(-lA@-BĒD)U\6*=Z&YZVvHԀ#vA.f 4diZmʣ$Ч$i}'<*=2dG>y.ܸ)벎.Z *}G$.dIv"F' ܂-*~D|?Td6|hteD̂xtvl.ɥ?O~DB_ B+r$gq۰ҪA#' %`vkiM$RY?H ۵ڷUu-Q;?Fz 4G-eݙ`ґOUY-@ 8@ *"2/H!@ U-GDN^eHHjH, @~"ޟva\I)/vV+B DCxO=+kG5:Y@ I0+Bx%z?B~dyI s00/dBvLZoz*™B VLZ1#&XA._f?*\i* UCfYuS +AO&{ţ&Y\q+{1Y鹦s.+XA(~@H!I-j> @(,`A_2)rHpA.8?P,O@~D85'')B-DB B.M~bW0eU-Fi B+A DO-F0@]wA`78 0w 1I,B/̂,273f=sV,v7;?|2{,‹R#'hI0Ȃ O)Hk)*U*h[嶕t"I2ŬѲֺֽ"6"A8tSo/;!4"s"Wd'2bdE5Fg+05YCH.9ThHurTOSD h4,mSFbtR@X6c"vtzvS6yx h[Ej# B+LV[F-"Qui[X6\o6pp7qq7r'ro<7tGt )TuW4@ @ROh7w@ty7zu_7iDHA$|7G@ }xCA7HwPxw\L< ĂJ䃫4tKt{hD# B0z[a w? @{x| w}ĊCAGt@CH 8I8GxQ/CGCI4xhēDIH9Cd@\yS8B9XF9#w8)C|A}뷎{7|ŋ O˸@zQ ;z ܃m ĤWzG9Ed\zBxzz#0:Hйx}8~ y8}w~{8} @?~7D灉8Hxx 8~׺w$9}' 7;; $9H@1D8+1| $4 A#4Ѓ6 NDͿ@0h88=p1; @TdM+ LȟQ<ۼM>@@>B ||? s_|ī}x}Ňc'BxC.<$" }Ƿy|3@뷭 8 G7wwx}O; Į c ׻?@L=QL-݃ ĥo@ `fHdwQVYj&!o`~$Ø цYҾO4cUE lmTp5W8h-#NWu4Vis{U^-Gͻa?7v9d-|rאk=¥容\k}WI:#\pu v">)ŀF*8D Xizca $WF1mDy.]0 _R&ctō!E.ѧH\|=Ą~z)C2{$}Ɵ@y 2C% Ͳrw*ܠFqijD瀪9yi\E`.SqJE08M(0^8^| bT û_F n Ḙ;*s|s@! wL2Wr& q$$'s#%(@ >"m^C8 _#9pŋȐddXD@)JRfVXАKfE3(@MessF/˄s;ffNyJ!e)79yl+c9K( 4iM/ /1texG3T:joFƐť9F)1q&(ÈD۲bWj0xضf66̀kuH֮3o&{` X7. 螌Y&g#b#W<`ËOfՍ'`޺~X[`ժYzm<6mVAЁ ݲ B-? tuUBߑRވ$H?YUA؟UO6# "m`YhY?fBUkm [=@P[ #A%df5?`$V#lh)YUW9ꈕ=!? dd&ystX<-ѠA:9 YY[!뮼2kuՓ[I%Ak2*ۙ4;ƛ@ ըf05fW)πn"yi,YھqjIړ8ȸzY0i^]aB Q\\aY{N LÊ~ @N+Yy 2@-` &YIhـe͋Cm!}VyJ$ l5@OΗg=ݘY-e\_N1 伔['W*̵DO}6y^X?ks0S<|65K1<O0(,N}i ϞV.kTkl4V8Z=vTZAax]r@}l "䷜O>x-ZӸF-,&iI\` X&UTBՓm!(HFZu'D#t*5w8!b(7ikOĸ},kX5mկz`~ uIm>v8*n\_ᗘ(E)j"D9 `yX/YqIhE4Vb= %dpvc  `P QB X@#~ wtHRB%AWܰ0c4c/ \ C,5Sx $a^X4/{cXBĴ&l1)6ՄFz aրűp 纴2ڏ ZdE:@.2tb"pGFG rICC!@'3\ҔhKœsI:ԜC=f$EQk"dkς(h4SǓt[ JP5T)i61 k5d e.),Dz'%bBR+*P~ XzC ²\b075Fؐ汐A)~F\adTbKڌJ=t6"},g1V9x#miwU.=Ρ"W!twgZD]!n,yP H*\ȰÇ#JHŋ3jȱǏI ɓ(S\ɲ˗0c7̛8sɳϟ@ѣH*]ʔPRMJJիbʵׯ`Yj KٳhM˶۷p[Kݻx ˷߿ULa+^̸1Ď#K2˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ$*무j뭸뮼+찰T,CǾH6F+j%+-\-Hrr +B^4+/ZD> :,ٮ+@Ox+GOp /<C԰FvCL#AWs;c?eD ?Qd= 1 i!Lo(ApOqO= l="0,3?|rHM\ƫ/G!<*4]j1:4иfW@, چ-3RAʰX b!=,dlc5PG+!< `4x@-zC' bPis7=0@ Nx[' [/}{ 3؊zcdHC~=f@ɁO|{9| LsZ" ?.xֽ.v"k/qYBuW Fd$@G46HNaV_0 HJx4:gnMl cDOj@6 x,Ach|U+\ce&IkuҒH+%oУf09AV 9`P@ 418 bJ}U@ AӘr%h?n$r_cDq 0D ~\AF.%} Б6 nhG *i"VAFsOYU3i@Z.:hB(^ȧ8#cUث u#Ue6U䫈u V$9aVf_4q<4l8*i"mcaUWFֱ0&{Up*!zᕋ?f! (@ Х3X`ڀiX@Х*m\-+@ [Q@\@V@ ]І/ 0cH#PF*i%\a >Oi|8BAA{< ?d=4 pAyvS@,#)/+EaK-0߂Xô?߂-| Լ n"1}cu /`կd"qڽwr830[8 ؒ\:^ILPi||UezX+Vnwa52d¯xP@q0YVE1W):[6-GG26yѶta3SH"Ep\腺Xސ9\NҊFa bȪ @06Z@'7ʅ]>[xů!_(u209*KJU ǎO}-quGC}тs?\L4xa}gbc$7soC0+%w76rW+7}recҀT.}};gE@|V'- h7- aSUЂ|pS?+9fxnvW2{w{v{g&Uހc@ xGhwcKFtiixxrW,]*`X.`0z31rxB9@kՄ=-zzGY ( 4 h&@-gm+O P_RP 0p?"Y_-Q[Ti53S%UHZH6sb a0EHES+cS&2TbFa5芰s *|Z%+T P(GTS<+p;F+ 츄o"(Xxv AH17axfL!o# `Y x"S 1)YjS0 19+ޠ% 41yFYVpPzw\pU s PXP+ V,d@>7&SP-? ?@藋C4&0 .ˠ d aT`x0PSS+;C?T Ȑ0t2Zpt8sYP}f9D a$P9ڹ Ȝ+L@]0_ٜIvvPؕ_5Wh0n/ PjNY ۠P` #J 0Z0ylbZ1@ &}^ Pp 0 ڠj|-Ak #+G"; pJ,۲.02;4[6w{8:<۳>@Bk^ny!JkNX 1TRY+ɵc_ L۵[qc jQK. =ZtvXѷ~{;irony-mode-1.2.0/server/000077500000000000000000000000001317254426700151155ustar00rootroot00000000000000irony-mode-1.2.0/server/.clang-format000066400000000000000000000004541317254426700174730ustar00rootroot00000000000000BasedOnStyle: LLVM AllowShortFunctionsOnASingleLine: false AlwaysBreakTemplateDeclarations: true BinPackParameters: false BreakConstructorInitializersBeforeComma: true ConstructorInitializerAllOnOneLineOrOnePerLine: true ConstructorInitializerIndentWidth: 2 IndentFunctionDeclarationAfterType: false irony-mode-1.2.0/server/.clang-tidy000066400000000000000000000016111317254426700171500ustar00rootroot00000000000000Checks: '-*,readability-identifier-naming,misc-assert-side-effect,readability-container-size-empty' CheckOptions: - key: readability-identifier-naming.TypedefCase value: CamelCase - key: readability-identifier-naming.StructCase value: CamelCase - key: readability-identifier-naming.ClassCase value: CamelCase - key: readability-identifier-naming.VariableCase value: camelBack - key: readability-identifier-naming.ParameterCase value: camelBack - key: readability-identifier-naming.FunctionCase value: camelBack - key: readability-identifier-naming.NamespaceCase value: lower_case - key: readability-identifier-naming.GlobalConstantCase value: UPPER_CASE - key: readability-identifier-naming.PrivateMemberSuffix value: _ - key: misc-assert-side-effect.CheckFunctionCalls value: 1 - key: misc-assert-side-effect.AssertMacros value: 'assert' irony-mode-1.2.0/server/CMakeLists.txt000066400000000000000000000044401317254426700176570ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.8.3) # CMAKE_CURRENT_LIST_DIR project(IronyMode) set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake ${PROJECT_SOURCE_DIR}/cmake/modules ${CMAKE_MODULE_PATH}) include(utils) include(CTest) check_for_in_source_build() release_as_default_build_type() # Disable exception, we aren't using them and right now clang-cl needs them # disabled to parse Windows headers. # # Logic stolen from LLVM if (CMAKE_COMPILER_IS_GNUCXX) set(IRONY_COMPILER_IS_GCC_COMPATIBLE ON) elseif (MSVC) set(IRONY_COMPILER_IS_GCC_COMPATIBLE OFF) elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang") set(IRONY_COMPILER_IS_GCC_COMPATIBLE ON) endif() if(IRONY_COMPILER_IS_GCC_COMPATIBLE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions") elseif(MSVC) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHs-c- /D_HAS_EXCEPTIONS=0") # irony-server uses some code that breaks when iterator debugging is enabled # # The culprit is CommandLineArgumentParser who initialize its member # 'Position', of type 'std::string::const_iterator', to 'Input.begin() - 1'. # With checked iterator the begin() - 1 breaks in debug build. set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /D_ITERATOR_DEBUG_LEVEL=0") endif() enable_colored_diagnotics() check_cxx11_options() if (CXX11_COMPILE_OPTIONS) add_compile_options_(${CXX11_COMPILE_OPTIONS}) endif() foreach (link_option ${CXX11_LINK_OPTIONS}) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${link_option}") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${link_option}") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${link_option}") endforeach() # Turn this on for local development or continuous integration option(IRONY_WARNINGS_AS_ERRORS "Whether or not to treat warnings as errors" OFF) if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") add_compile_options_(-Wall -Wextra) if (IRONY_WARNINGS_AS_ERRORS) add_compile_options_(-Werror) endif() endif() option(GENERATE_DOXYGEN "Whether or not to build the Doxygen documentation" OFF) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) add_subdirectory(src) add_subdirectory(docs) if (BUILD_TESTING) add_subdirectory(test) endif() irony-mode-1.2.0/server/build-aux/000077500000000000000000000000001317254426700170075ustar00rootroot00000000000000irony-mode-1.2.0/server/build-aux/run-clang-tidy/000077500000000000000000000000001317254426700216445ustar00rootroot00000000000000irony-mode-1.2.0/server/build-aux/run-clang-tidy/LICENSE.TXT000066400000000000000000000036201317254426700233300ustar00rootroot00000000000000============================================================================== LLVM Release License ============================================================================== University of Illinois/NCSA Open Source License Copyright (c) 2007-2016 University of Illinois at Urbana-Champaign. All rights reserved. Developed by: LLVM Team University of Illinois at Urbana-Champaign http://llvm.org Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal with the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimers. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimers in the documentation and/or other materials provided with the distribution. * Neither the names of the LLVM Team, University of Illinois at Urbana-Champaign, nor the names of its contributors may be used to endorse or promote products derived from this Software without specific prior written permission. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. irony-mode-1.2.0/server/build-aux/run-clang-tidy/README000066400000000000000000000011151317254426700225220ustar00rootroot00000000000000Origin: - http://llvm.org/svn/llvm-project/clang-tools-extra/trunk/clang-tidy/tool/run-clang-tidy.py?p=294607 - https://github.com/llvm-mirror/clang-tools-extra/blob/c2e903ec98385b82e35bdb303e411854a2e8c032/clang-tidy/tool/run-clang-tidy.py Modifications: - the python version has been frozen to python2, as the script is not python3-compatible - added -warnings-as-errors option - the run-clang-tidy.py script has been modified to return a sensible exit code when running on Travis CI, i.e. it honors exit code of the underlying processes causing build failures on pull requests irony-mode-1.2.0/server/build-aux/run-clang-tidy/run-clang-tidy.py000077500000000000000000000202501317254426700250550ustar00rootroot00000000000000#!/usr/bin/env python2 # #===- run-clang-tidy.py - Parallel clang-tidy runner ---------*- python -*--===# # # The LLVM Compiler Infrastructure # # This file is distributed under the University of Illinois Open Source # License. See LICENSE.TXT for details. # #===------------------------------------------------------------------------===# # FIXME: Integrate with clang-tidy-diff.py """ Parallel clang-tidy runner ========================== Runs clang-tidy over all files in a compilation database. Requires clang-tidy and clang-apply-replacements in $PATH. Example invocations. - Run clang-tidy on all files in the current working directory with a default set of checks and show warnings in the cpp files and all project headers. run-clang-tidy.py $PWD - Fix all header guards. run-clang-tidy.py -fix -checks=-*,llvm-header-guard - Fix all header guards included from clang-tidy and header guards for clang-tidy headers. run-clang-tidy.py -fix -checks=-*,llvm-header-guard extra/clang-tidy \ -header-filter=extra/clang-tidy Compilation database setup: http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html """ import argparse import json import multiprocessing import os import Queue import re import shutil import subprocess import sys import tempfile import threading def find_compilation_database(path): """Adjusts the directory until a compilation database is found.""" result = './' while not os.path.isfile(os.path.join(result, path)): if os.path.realpath(result) == '/': print 'Error: could not find compilation database.' sys.exit(1) result += '../' return os.path.realpath(result) def get_tidy_invocation(f, clang_tidy_binary, checks, tmpdir, build_path, header_filter, extra_arg, extra_arg_before, quiet, warnings_as_errors): """Gets a command line for clang-tidy.""" start = [clang_tidy_binary] if header_filter is not None: start.append('-header-filter=' + header_filter) else: # Show warnings in all in-project headers by default. start.append('-header-filter=^' + build_path + '/.*') if checks: start.append('-checks=' + checks) if tmpdir is not None: start.append('-export-fixes') # Get a temporary file. We immediately close the handle so clang-tidy can # overwrite it. (handle, name) = tempfile.mkstemp(suffix='.yaml', dir=tmpdir) os.close(handle) start.append(name) for arg in extra_arg: start.append('-extra-arg=%s' % arg) for arg in extra_arg_before: start.append('-extra-arg-before=%s' % arg) start.append('-p=' + build_path) if quiet: start.append('-quiet') if warnings_as_errors is not None: start.append('-warnings-as-errors=%s' % warnings_as_errors) start.append(f) return start def apply_fixes(args, tmpdir): """Calls clang-apply-fixes on a given directory. Deletes the dir when done.""" invocation = [args.clang_apply_replacements_binary] if args.format: invocation.append('-format') invocation.append(tmpdir) ret = subprocess.call(invocation) shutil.rmtree(tmpdir) return ret def run_tidy(args, tmpdir, build_path, queue, results, i): """Takes filenames out of queue and runs clang-tidy on them.""" results[i] = 0 while True: name = queue.get() invocation = get_tidy_invocation(name, args.clang_tidy_binary, args.checks, tmpdir, build_path, args.header_filter, args.extra_arg, args.extra_arg_before, args.quiet, args.warnings_as_errors) sys.stdout.write(' '.join(invocation) + '\n') if subprocess.call(invocation) != 0: results[i] = 1 queue.task_done() def main(): parser = argparse.ArgumentParser(description='Runs clang-tidy over all files ' 'in a compilation database. Requires ' 'clang-tidy and clang-apply-replacements in ' '$PATH.') parser.add_argument('-clang-tidy-binary', metavar='PATH', default='clang-tidy', help='path to clang-tidy binary') parser.add_argument('-clang-apply-replacements-binary', metavar='PATH', default='clang-apply-replacements', help='path to clang-apply-replacements binary') parser.add_argument('-checks', default=None, help='checks filter, when not specified, use clang-tidy ' 'default') parser.add_argument('-header-filter', default=None, help='regular expression matching the names of the ' 'headers to output diagnostics from. Diagnostics from ' 'the main file of each translation unit are always ' 'displayed.') parser.add_argument('-warnings-as-errors', default=None, metavar='STRING', help='Upgrades warnings to errors. ' 'Same format as "-checks".' "This option's value is appended to the value of " "the 'WarningsAsErrors' option in .clang-tidy " "file, if any.'") parser.add_argument('-j', type=int, default=0, help='number of tidy instances to be run in parallel.') parser.add_argument('files', nargs='*', default=['.*'], help='files to be processed (regex on path)') parser.add_argument('-fix', action='store_true', help='apply fix-its') parser.add_argument('-format', action='store_true', help='Reformat code ' 'after applying fixes') parser.add_argument('-p', dest='build_path', help='Path used to read a compile command database.') parser.add_argument('-extra-arg', dest='extra_arg', action='append', default=[], help='Additional argument to append to the compiler ' 'command line.') parser.add_argument('-extra-arg-before', dest='extra_arg_before', action='append', default=[], help='Additional argument to prepend to the compiler ' 'command line.') parser.add_argument('-quiet', action='store_true', help='Run clang-tidy in quiet mode') args = parser.parse_args() db_path = 'compile_commands.json' if args.build_path is not None: build_path = args.build_path else: # Find our database build_path = find_compilation_database(db_path) try: invocation = [args.clang_tidy_binary, '-list-checks'] invocation.append('-p=' + build_path) if args.checks: invocation.append('-checks=' + args.checks) invocation.append('-') print subprocess.check_output(invocation) except: print >>sys.stderr, "Unable to run clang-tidy." sys.exit(1) # Load the database and extract all files. database = json.load(open(os.path.join(build_path, db_path))) files = [entry['file'] for entry in database] max_task = args.j if max_task == 0: max_task = multiprocessing.cpu_count() tmpdir = None if args.fix: tmpdir = tempfile.mkdtemp() # Build up a big regexy filter from all command line arguments. file_name_re = re.compile('(' + ')|('.join(args.files) + ')') try: # Spin up a bunch of tidy-launching threads. results = [None] * max_task queue = Queue.Queue(max_task) for i in range(max_task): t = threading.Thread(target=run_tidy, args=(args, tmpdir, build_path, queue, results, i)) t.daemon = True t.start() # Fill the queue with files. for name in files: if file_name_re.search(name): queue.put(name) # Wait for all threads to be done. queue.join() except KeyboardInterrupt: # This is a sad hack. Unfortunately subprocess goes # bonkers with ctrl-c and we start forking merrily. print '\nCtrl-C detected, goodbye.' if args.fix: shutil.rmtree(tmpdir) os.kill(0, 9) for ret in results: if ret != 0: sys.exit(ret) if args.fix: print 'Applying fixes ...' ret = apply_fixes(args, tmpdir) if ret != 0: sys.exit(ret) if __name__ == '__main__': main() irony-mode-1.2.0/server/cmake/000077500000000000000000000000001317254426700161755ustar00rootroot00000000000000irony-mode-1.2.0/server/cmake/CheckClangResourceDir.cmake000066400000000000000000000056521317254426700233400ustar00rootroot00000000000000# # Get the Clang resource directory. # # If found the following variable will be set: # - CLANG_RESOURCE_DIR # set(CHECK_CLANG_RESOURCE_DIR_CHECKER_CODE_IN ${CMAKE_CURRENT_LIST_DIR}/LibClangDiagnosticsChecker.cpp) function(check_clang_resource_dir) if (CLANG_RESOURCE_DIR) return() # already in cache endif() message(STATUS "Detecting Clang resource directory") find_package (LibClang REQUIRED) set(checker_code ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/LibClangDiagnosticsChecker.cpp) set(checked_file "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/check-libclang-stddef.cpp") configure_file(${CHECK_CLANG_RESOURCE_DIR_CHECKER_CODE_IN} ${checker_code} COPYONLY) file(WRITE "${checked_file}" "#include \n") # Paths stolen from Rip-Rip/clang_complete#getBuiltinHeaderPath() find_path(CHECK_CLANG_RESOURCE_DIR include/stddef.h NO_DEFAULT_PATH # the default path, favor this one over the other, in case a specific # libclang has been chosen. HINTS "${LIBCLANG_LIBRARY_DIR}/../lib/clang" # other, distribution specific, paths PATHS "${LIBCLANG_LIBRARY_DIR}/../clang" # Gentoo "${LIBCLANG_LIBRARY_DIR}/clang" # openSUSE, Windows "${LIBCLANG_LIBRARY_DIR}/" # Google "/usr/lib64/clang" # x86_64 (openSUSE, Fedora) "/usr/lib/clang" PATH_SUFFIXES ${LIBCLANG_KNOWN_LLVM_VERSIONS}) if (CHECK_CLANG_RESOURCE_DIR) # On Windows the paths weren't escaped correctly, similar to: # http://public.kitware.com/pipermail/cmake/2006-February/008473.html list(APPEND run_args -resource-dir \"${CHECK_CLANG_RESOURCE_DIR}\") endif() list(APPEND run_args ${checked_file}) try_run( CHECK_CLANG_RESOURCE_DIR_NUM_DIAGNOSTICS CHECK_CLANG_RESOURCE_DIR_COMPILE_RESULT ${CMAKE_BINARY_DIR} ${checker_code} CMAKE_FLAGS "-DINCLUDE_DIRECTORIES:STRING=${LIBCLANG_INCLUDE_DIRS}" "-DLINK_LIBRARIES:STRING=${LIBCLANG_LIBRARIES}" COMPILE_OUTPUT_VARIABLE compile_output RUN_OUTPUT_VARIABLE run_output ARGS ${run_args} ) if (NOT CHECK_CLANG_RESOURCE_DIR_COMPILE_RESULT) set(CHECK_CLANG_RESOURCE_DIR_NUM_DIAGNOSTICS 1) endif() if (CHECK_CLANG_RESOURCE_DIR_NUM_DIAGNOSTICS EQUAL 0) message(STATUS "Detecting libclang builtin headers directory -- success") if (CHECK_CLANG_RESOURCE_DIR) set(CLANG_RESOURCE_DIR "${CHECK_CLANG_RESOURCE_DIR}" CACHE INTERNAL "Clang resource directory.") endif() else() message(STATUS "Detecting Clang resource directory -- fail") if (NOT CHECK_CLANG_RESOURCE_DIR_COMPILE_RESULT) message(WARNING "CheckClangResourceDir: failed to compile checker, please report. Compile output: ${compile_output} ") else() message(WARNING "CheckClangResourceDir: unsupported configuration, please report. Check with args: ${run_args} Check output: ${run_output} ") endif() endif() endfunction() irony-mode-1.2.0/server/cmake/LibClangDiagnosticsChecker.cpp000066400000000000000000000023071317254426700240330ustar00rootroot00000000000000/* This program takes some forward its command line arguments to libclang and returns the number of diagnostics that occured during the parsing. It is used during CMake generation to adjust the default parameters to libclang. */ #include #include int main(int argc, const char *argv[]) { for (int i = 1; i < argc; ++i) { fprintf(stdout, "argv[%d]: %s\n", i, argv[i]); } CXIndex Idx = clang_createIndex(0, 0); CXTranslationUnit TU = clang_parseTranslationUnit( Idx, NULL, &argv[1], argc - 1, 0, 0, CXTranslationUnit_None); int NumDiagnostics; if (TU == NULL) { NumDiagnostics = 1; fprintf(stderr, "failed to create translation unit!\n"); } else { int i; NumDiagnostics = clang_getNumDiagnostics(TU); for (i = 0; i < NumDiagnostics; ++i) { CXDiagnostic Diag = clang_getDiagnostic(TU, i); CXString DiagStr = clang_formatDiagnostic(Diag, clang_defaultDiagnosticDisplayOptions()); fprintf(stderr, "%s\n", clang_getCString(DiagStr)); clang_disposeString(DiagStr); clang_disposeDiagnostic(Diag); } clang_disposeTranslationUnit(TU); } clang_disposeIndex(Idx); return NumDiagnostics; } irony-mode-1.2.0/server/cmake/modules/000077500000000000000000000000001317254426700176455ustar00rootroot00000000000000irony-mode-1.2.0/server/cmake/modules/FindLibClang.cmake000066400000000000000000000060641317254426700231310ustar00rootroot00000000000000# # Try to find libclang # # Once done this will define: # - LIBCLANG_FOUND # System has libclang. # - LIBCLANG_INCLUDE_DIRS # The libclang include directories. # - LIBCLANG_LIBRARIES # The libraries needed to use libclang. # - LIBCLANG_LIBRARY_DIR # The path to the directory containing libclang. # - LIBCLANG_KNOWN_LLVM_VERSIONS # Known LLVM release numbers. # most recent versions come first # http://llvm.org/apt/ set(LIBCLANG_KNOWN_LLVM_VERSIONS 6.0.0 6.0 6 5.0.0 5.0 5 4.0.1 4.0.0 4.0 4 3.9.1 3.9.0 3.9 3.8.1 3.8.0 3.8 3.7.1 3.7.0 3.7 3.6.2 3.6.1 3.6.0 3.6 3.5.2 3.5.1 3.5.0 3.5 3.4.2 3.4.1 3.4 3.3 3.2 3.1) set(libclang_llvm_header_search_paths) set(libclang_llvm_lib_search_paths # LLVM Fedora /usr/lib/llvm ) foreach (version ${LIBCLANG_KNOWN_LLVM_VERSIONS}) string(REPLACE "." "" undotted_version "${version}") list(APPEND libclang_llvm_header_search_paths # LLVM Debian/Ubuntu nightly packages: http://llvm.org/apt/ "/usr/lib/llvm-${version}/include/" # LLVM MacPorts "/opt/local/libexec/llvm-${version}/include" # LLVM Homebrew "/usr/local/Cellar/llvm/${version}/include" # LLVM Homebrew/versions "/usr/local/lib/llvm-${version}/include" # FreeBSD ports versions "/usr/local/llvm${undotted_version}/include" # Gentoo clang-4 "/usr/lib/llvm/${version}/include" ) list(APPEND libclang_llvm_lib_search_paths # LLVM Debian/Ubuntu nightly packages: http://llvm.org/apt/ "/usr/lib/llvm-${version}/lib/" # LLVM MacPorts "/opt/local/libexec/llvm-${version}/lib" # LLVM Homebrew "/usr/local/Cellar/llvm/${version}/lib" # LLVM Homebrew/versions "/usr/local/lib/llvm-${version}/lib" # FreeBSD ports versions "/usr/local/llvm${undotted_version}/lib" # Gentoo clang-4 "/usr/lib/llvm/${version}/lib" ) endforeach() find_path(LIBCLANG_INCLUDE_DIR clang-c/Index.h PATHS ${libclang_llvm_header_search_paths} PATH_SUFFIXES LLVM/include #Windows package from http://llvm.org/releases/ DOC "The path to the directory that contains clang-c/Index.h") # On Windows with MSVC, the import library uses the ".imp" file extension # instead of the comon ".lib" if (MSVC) find_file(LIBCLANG_LIBRARY libclang.imp PATH_SUFFIXES LLVM/lib DOC "The file that corresponds to the libclang library.") endif() find_library(LIBCLANG_LIBRARY NAMES libclang.imp libclang clang PATHS ${libclang_llvm_lib_search_paths} PATH_SUFFIXES LLVM/lib #Windows package from http://llvm.org/releases/ DOC "The file that corresponds to the libclang library.") get_filename_component(LIBCLANG_LIBRARY_DIR ${LIBCLANG_LIBRARY} PATH) set(LIBCLANG_LIBRARIES ${LIBCLANG_LIBRARY}) set(LIBCLANG_INCLUDE_DIRS ${LIBCLANG_INCLUDE_DIR}) include(FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set LIBCLANG_FOUND to TRUE if # all listed variables are TRUE find_package_handle_standard_args(LibClang DEFAULT_MSG LIBCLANG_LIBRARY LIBCLANG_INCLUDE_DIR) mark_as_advanced(LIBCLANG_INCLUDE_DIR LIBCLANG_LIBRARY) irony-mode-1.2.0/server/cmake/utils.cmake000066400000000000000000000103771317254426700203470ustar00rootroot00000000000000include(CheckCXXCompilerFlag) include(CheckCXXSourceCompiles) # # check_for_in_source_build() # function(check_for_in_source_build) if (CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR AND NOT MSVC_IDE) message(FATAL_ERROR "In-source builds are not allowed. Please create a build/ directory and run cmake from there, passing the path to this source directory as the last argument. This process created the file `CMakeCache.txt' and the directory `CMakeFiles'. Please delete them. ") endif() endfunction() # # release_as_default_build_type() # function(release_as_default_build_type) # Set a default build type if none was specified for build systems with a # unique configuration type (i.e: Make/Ninja builds) if (NOT CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE) message(STATUS "Setting build type to 'Release' as none was specified") set (CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE) set_property (CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") endif() endfunction() # # add_compile_options_() # # Adds options to the compiler command line for sources in the current directory # and below. # # note: add_compile_options() backport, which first appeared in CMake 2.8.12 function(add_compile_options_) # possible to check with: # if (${CMAKE_VERSION} VERSION_LESS "2.8.12") if (COMMAND add_compile_options) add_compile_options(${ARGN}) else() add_definitions(${ARGN}) endif() endfunction() # # enable_colored_diagnotics() # # Setup the flag to enable colored diagnostics if any. # # For now this option is enforced only for Ninja builds, where compiler output # is redirected to pipes. # # Clang has '-fcolor-diagnostics' for a long time now. Since GCC 4.9, a similar # flag has been added '-fdiagnostics-color' (somehow they managed to use another # syntax than Clang's one...). Recent version of Clang will support both as they # added support for GCC's -fdiagnostics-color. # function(enable_colored_diagnotics) if (${CMAKE_GENERATOR} MATCHES "Ninja") # Clang check_cxx_compiler_flag("-fcolor-diagnostics" HAS_FCOLOR_DIAGNOSTICS_FLAG) if (HAS_FCOLOR_DIAGNOSTICS_FLAG) add_compile_options_(-fcolor-diagnostics) else() # GCC (and Clang for compatibility with GCC) check_cxx_compiler_flag("-fdiagnostics-color" HAS_DIAGNOSTICS_FCOLOR_FLAG) if (HAS_DIAGNOSTICS_FCOLOR_FLAG) add_compile_options_(-fdiagnostics-color) endif() endif() endif() endfunction() # # check_cxx11_options() # # Throws a FATAL_ERROR if C++11 isn't available otherwise sets: # - CXX11_COMPILE_OPTIONS # - CXX11_LINK_OPTIONS # function(check_cxx11_options) if (CXX11_COMPILE_OPTIONS) return() # already in cache endif() if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") check_cxx_compiler_flag("-std=c++11" HAS_STDCXX11) if (HAS_STDCXX11) set(compile_options -std=c++11) else() check_cxx_compiler_flag("-std=c++0x" HAS_STDCXX0X) if (HAS_STDCXX0X) set(compile_options -std=c++0x) endif() endif() # Check whether or not the system library provides proper C++11 support, if # not we try to link specifically against libc++. # # This seems useful for Mac OS X builds, see: # http://cplusplusmusings.wordpress.com/2012/07/05/clang-and-standard-libraries-on-mac-os-x/ set(CMAKE_REQUIRED_FLAGS ${compile_options}) check_cxx_source_compiles("#include int main() { std::random_device rd; std::default_random_engine e(rd()); std::uniform_int_distribution dist(0, 15); return dist(e); } " HAS_CXX11_STDLIB) if (NOT HAS_CXX11_STDLIB) check_cxx_compiler_flag("-stdlib=libc++" HAS_LIBCXX) if (HAS_LIBCXX) list(APPEND compile_options -stdlib=libc++) list(APPEND link_options -stdlib=libc++) else() message(FATAL_ERROR "Standard library doesn't support C++11!") endif() endif() endif() if (compile_options) message(STATUS "C++11 compiler option(s): ${compile_options}") endif() set(CXX11_COMPILE_OPTIONS ${compile_options} CACHE INTERNAL "C++11 compile options, if any") set(CXX11_LINK_OPTIONS ${link_options} CACHE INTERNAL "C++11 link options, if any") endfunction() irony-mode-1.2.0/server/docs/000077500000000000000000000000001317254426700160455ustar00rootroot00000000000000irony-mode-1.2.0/server/docs/CMakeLists.txt000066400000000000000000000006631317254426700206120ustar00rootroot00000000000000if (GENERATE_DOXYGEN) find_package (Doxygen REQUIRED) set (HAS_DOT_VALUE "NO") if (DOXYGEN_DOT_FOUND) set (HAS_DOT_VALUE "YES") endif() configure_file(irony-server.cfg.in irony-server.cfg @ONLY) add_custom_target(doxygen ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/irony-server.cfg WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMENT "Generating API documentation with Doxygen" VERBATIM) endif() irony-mode-1.2.0/server/docs/irony-server.cfg.in000066400000000000000000000240261317254426700216030ustar00rootroot00000000000000# Doxyfile 1.8.2 #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = irony-server PROJECT_NUMBER = PROJECT_BRIEF = "The bridge between libclang and Emacs." PROJECT_LOGO = OUTPUT_DIRECTORY = CREATE_SUBDIRS = NO OUTPUT_LANGUAGE = English BRIEF_MEMBER_DESC = YES REPEAT_BRIEF = YES ABBREVIATE_BRIEF = ALWAYS_DETAILED_SEC = NO INLINE_INHERITED_MEMB = NO FULL_PATH_NAMES = NO STRIP_FROM_PATH = STRIP_FROM_INC_PATH = SHORT_NAMES = NO JAVADOC_AUTOBRIEF = YES QT_AUTOBRIEF = NO MULTILINE_CPP_IS_BRIEF = NO INHERIT_DOCS = YES SEPARATE_MEMBER_PAGES = NO TAB_SIZE = 2 ALIASES = TCL_SUBST = OPTIMIZE_OUTPUT_FOR_C = NO OPTIMIZE_OUTPUT_JAVA = NO OPTIMIZE_FOR_FORTRAN = NO OPTIMIZE_OUTPUT_VHDL = NO EXTENSION_MAPPING = MARKDOWN_SUPPORT = YES AUTOLINK_SUPPORT = YES BUILTIN_STL_SUPPORT = YES CPP_CLI_SUPPORT = NO SIP_SUPPORT = NO IDL_PROPERTY_SUPPORT = YES DISTRIBUTE_GROUP_DOC = NO SUBGROUPING = YES INLINE_GROUPED_CLASSES = NO INLINE_SIMPLE_STRUCTS = NO TYPEDEF_HIDES_STRUCT = NO SYMBOL_CACHE_SIZE = 0 LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- EXTRACT_ALL = YES EXTRACT_PRIVATE = YES EXTRACT_PACKAGE = YES EXTRACT_STATIC = YES EXTRACT_LOCAL_CLASSES = YES EXTRACT_LOCAL_METHODS = NO EXTRACT_ANON_NSPACES = NO HIDE_UNDOC_MEMBERS = NO HIDE_UNDOC_CLASSES = NO HIDE_FRIEND_COMPOUNDS = NO HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO CASE_SENSE_NAMES = YES HIDE_SCOPE_NAMES = NO SHOW_INCLUDE_FILES = YES FORCE_LOCAL_INCLUDES = NO INLINE_INFO = YES SORT_MEMBER_DOCS = YES SORT_BRIEF_DOCS = NO SORT_MEMBERS_CTORS_1ST = NO SORT_GROUP_NAMES = NO SORT_BY_SCOPE_NAME = NO STRICT_PROTO_MATCHING = NO GENERATE_TODOLIST = YES GENERATE_TESTLIST = YES GENERATE_BUGLIST = YES GENERATE_DEPRECATEDLIST= YES ENABLED_SECTIONS = MAX_INITIALIZER_LINES = 30 SHOW_USED_FILES = YES SHOW_FILES = YES SHOW_NAMESPACES = YES FILE_VERSION_FILTER = LAYOUT_FILE = CITE_BIB_FILES = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- QUIET = NO WARNINGS = YES WARN_IF_UNDOCUMENTED = YES WARN_IF_DOC_ERROR = YES WARN_NO_PARAMDOC = NO WARN_FORMAT = "$file:$line: $text" WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- INPUT = @CMAKE_CURRENT_SOURCE_DIR@/../src INPUT_ENCODING = UTF-8 FILE_PATTERNS = *.cpp *.h *.def RECURSIVE = YES EXCLUDE = EXCLUDE_SYMLINKS = NO EXCLUDE_PATTERNS = EXCLUDE_SYMBOLS = EXAMPLE_PATH = EXAMPLE_PATTERNS = EXAMPLE_RECURSIVE = NO IMAGE_PATH = INPUT_FILTER = FILTER_PATTERNS = FILTER_SOURCE_FILES = NO FILTER_SOURCE_PATTERNS = #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- SOURCE_BROWSER = YES INLINE_SOURCES = NO STRIP_CODE_COMMENTS = NO REFERENCED_BY_RELATION = YES REFERENCES_RELATION = YES REFERENCES_LINK_SOURCE = YES USE_HTAGS = NO VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- ALPHABETICAL_INDEX = YES COLS_IN_ALPHA_INDEX = 5 IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- GENERATE_HTML = YES HTML_OUTPUT = html HTML_FILE_EXTENSION = .html HTML_HEADER = HTML_FOOTER = HTML_STYLESHEET = HTML_EXTRA_STYLESHEET = HTML_EXTRA_FILES = HTML_COLORSTYLE_HUE = 220 HTML_COLORSTYLE_SAT = 100 HTML_COLORSTYLE_GAMMA = 80 HTML_TIMESTAMP = YES HTML_DYNAMIC_SECTIONS = NO HTML_INDEX_NUM_ENTRIES = 100 GENERATE_DOCSET = NO DOCSET_FEEDNAME = "Doxygen generated docs" DOCSET_BUNDLE_ID = org.doxygen.Project DOCSET_PUBLISHER_ID = org.doxygen.Publisher DOCSET_PUBLISHER_NAME = Publisher GENERATE_HTMLHELP = NO CHM_FILE = HHC_LOCATION = GENERATE_CHI = NO CHM_INDEX_ENCODING = BINARY_TOC = NO TOC_EXPAND = NO GENERATE_QHP = NO QCH_FILE = QHP_NAMESPACE = org.doxygen.Project QHP_VIRTUAL_FOLDER = doc QHP_CUST_FILTER_NAME = QHP_CUST_FILTER_ATTRS = QHP_SECT_FILTER_ATTRS = QHG_LOCATION = GENERATE_ECLIPSEHELP = NO ECLIPSE_DOC_ID = org.doxygen.Project DISABLE_INDEX = NO GENERATE_TREEVIEW = NO ENUM_VALUES_PER_LINE = 4 TREEVIEW_WIDTH = 250 EXT_LINKS_IN_WINDOW = NO FORMULA_FONTSIZE = 10 FORMULA_TRANSPARENT = YES USE_MATHJAX = NO MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest MATHJAX_EXTENSIONS = SEARCHENGINE = YES SERVER_BASED_SEARCH = NO #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- GENERATE_LATEX = NO LATEX_OUTPUT = latex LATEX_CMD_NAME = latex MAKEINDEX_CMD_NAME = makeindex COMPACT_LATEX = NO PAPER_TYPE = a4 EXTRA_PACKAGES = LATEX_HEADER = LATEX_FOOTER = PDF_HYPERLINKS = YES USE_PDFLATEX = YES LATEX_BATCHMODE = NO LATEX_HIDE_INDICES = NO LATEX_SOURCE_CODE = NO LATEX_BIB_STYLE = plain #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- GENERATE_RTF = NO RTF_OUTPUT = rtf COMPACT_RTF = NO RTF_HYPERLINKS = NO RTF_STYLESHEET_FILE = RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- GENERATE_MAN = NO MAN_OUTPUT = man MAN_EXTENSION = .3 MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- GENERATE_XML = NO XML_OUTPUT = xml XML_SCHEMA = XML_DTD = XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- GENERATE_PERLMOD = NO PERLMOD_LATEX = NO PERLMOD_PRETTY = YES PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- ENABLE_PREPROCESSING = YES MACRO_EXPANSION = NO EXPAND_ONLY_PREDEF = NO SEARCH_INCLUDES = YES INCLUDE_PATH = INCLUDE_FILE_PATTERNS = PREDEFINED = EXPAND_AS_DEFINED = SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- TAGFILES = GENERATE_TAGFILE = ALLEXTERNALS = NO EXTERNAL_GROUPS = YES PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- CLASS_DIAGRAMS = YES MSCGEN_PATH = HIDE_UNDOC_RELATIONS = YES HAVE_DOT = @HAS_DOT_VALUE@ DOT_NUM_THREADS = 0 DOT_FONTNAME = Helvetica DOT_FONTSIZE = 10 DOT_FONTPATH = CLASS_GRAPH = YES COLLABORATION_GRAPH = YES GROUP_GRAPHS = YES UML_LOOK = NO UML_LIMIT_NUM_FIELDS = 10 TEMPLATE_RELATIONS = NO INCLUDE_GRAPH = YES INCLUDED_BY_GRAPH = YES CALL_GRAPH = NO CALLER_GRAPH = NO GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = YES DOT_IMAGE_FORMAT = png INTERACTIVE_SVG = YES DOT_PATH = @DOXYGEN_DOT_PATH@ DOTFILE_DIRS = MSCFILE_DIRS = DOT_GRAPH_MAX_NODES = 50 MAX_DOT_GRAPH_DEPTH = 0 DOT_TRANSPARENT = NO DOT_MULTI_TARGETS = YES GENERATE_LEGEND = YES DOT_CLEANUP = YES irony-mode-1.2.0/server/src/000077500000000000000000000000001317254426700157045ustar00rootroot00000000000000irony-mode-1.2.0/server/src/CMakeLists.txt000066400000000000000000000037311317254426700204500ustar00rootroot00000000000000include(CheckClangResourceDir) find_package(LibClang REQUIRED) include_directories(${LIBCLANG_INCLUDE_DIRS}) include_directories(${CMAKE_CURRENT_SOURCE_DIR}) check_clang_resource_dir() if (CLANG_RESOURCE_DIR) # look for CLANG_BUILTIN_HEADERS_DIR usage in the code for an explanation add_definitions(-DCLANG_RESOURCE_DIR=\"${CLANG_RESOURCE_DIR}\") endif() # not to be taken as a module-definition file to link on Windows set_source_files_properties(Commands.def PROPERTIES HEADER_FILE_ONLY TRUE) add_executable(irony-server support/CommandLineParser.cpp support/CommandLineParser.h support/iomanip_quoted.h support/NonCopyable.h support/CIndex.h support/TemporaryFile.cpp support/TemporaryFile.h Command.cpp Commands.def Command.h Irony.cpp Irony.h TUManager.cpp TUManager.h main.cpp) # retrieve the package version from irony.el function(irony_find_package_version OUTPUT_VAR) # this is a hack that force CMake to reconfigure, it is necessary to see if # the version in irony.el has changed, this is not possible to add the # definitions at build time configure_file(${PROJECT_SOURCE_DIR}/../irony.el ${CMAKE_CURRENT_BINARY_DIR}/irony.el COPYONLY) set(version_header "\;\; Version: ") file(STRINGS ${CMAKE_CURRENT_BINARY_DIR}/irony.el version LIMIT_COUNT 1 REGEX "^${version_header}*") if (NOT version) message (FATAL_ERROR "couldn't find irony.el's version header!") endif() string(LENGTH ${version_header} version_header_length) string(SUBSTRING ${version} ${version_header_length} -1 package_version) set(${OUTPUT_VAR} ${package_version} PARENT_SCOPE) endfunction() irony_find_package_version(IRONY_PACKAGE_VERSION) message(STATUS "Irony package version is '${IRONY_PACKAGE_VERSION}'") set_source_files_properties(main.cpp PROPERTIES COMPILE_DEFINITIONS IRONY_PACKAGE_VERSION=\"${IRONY_PACKAGE_VERSION}\") target_link_libraries(irony-server ${LIBCLANG_LIBRARIES}) install(TARGETS irony-server DESTINATION bin) irony-mode-1.2.0/server/src/Command.cpp000066400000000000000000000161111317254426700177660ustar00rootroot00000000000000/** * \file * \author Guillaume Papin * * \brief Command parser definitions. * * This file is distributed under the GNU General Public License. See * COPYING for details. */ #include "Command.h" #include "support/CommandLineParser.h" #include #include #include #include #include namespace { struct StringConverter { StringConverter(std::string *dest) : dest_(dest) { } bool operator()(const std::string &str) { *dest_ = str; return true; } private: std::string *dest_; }; struct UnsignedIntConverter { UnsignedIntConverter(unsigned *dest) : dest_(dest) { } bool operator()(const std::string &str) { char *end; long num = std::strtol(str.c_str(), &end, 10); if (end != (str.c_str() + str.size())) return false; if (errno == ERANGE) return false; if (num < 0) return false; unsigned long unum = static_cast(num); if (unum > std::numeric_limits::max()) return false; *dest_ = unum; return true; } private: unsigned *dest_; }; /// Convert "on" and "off" to a boolean struct OptionConverter { OptionConverter(bool *dest) : dest_(dest) { } bool operator()(const std::string &str) { if (str == "on") { *dest_ = true; } else if (str == "off") { *dest_ = false; } else { return false; } return true; } private: bool *dest_; }; const std::map PREFIX_MATCH_STYLE_MAP = { { "exact", PrefixMatchStyle::Exact }, { "case-insensitive", PrefixMatchStyle::CaseInsensitive }, { "smart-case", PrefixMatchStyle::SmartCase}, }; /// Convert style to a PrefixMatchStyle struct PrefixMatchStyleConverter { PrefixMatchStyleConverter(PrefixMatchStyle *dest) : dest_(dest) { } bool operator()(const std::string &str) { auto res = PREFIX_MATCH_STYLE_MAP.find(str); if (res == PREFIX_MATCH_STYLE_MAP.cend()) { return false; } *dest_ = res->second; return true; } private: PrefixMatchStyle *dest_; }; std::ostream &operator<<(std::ostream &os, PrefixMatchStyle style) { for (auto it : PREFIX_MATCH_STYLE_MAP) { if (it.second == style) { os << it.first; return os; } } os << "UnknownStyle"; return os; } } // unnamed namespace std::ostream &operator<<(std::ostream &os, const Command::Action &action) { os << "Command::"; switch (action) { #define X(sym, str, help) \ case Command::sym: \ os << #sym; \ break; #include "Commands.def" } return os; } std::ostream &operator<<(std::ostream &os, const Command &command) { os << "Command{action=" << command.action << ", " << "file='" << command.file << "', " << "unsavedFile='" << command.unsavedFile << "', " << "dir='" << command.dir << "', " << "line=" << command.line << ", " << "column=" << command.column << ", " << "prefix='" << command.prefix << "', " << "caseStyle='" << command.style << "', " << "flags=["; bool first = true; for (const std::string &flag : command.flags) { if (!first) os << ", "; os << "'" << flag << "'"; first = false; } os << "], " << "opt=" << (command.opt ? "on" : "off"); return os << "}"; } static Command::Action actionFromString(const std::string &actionStr) { #define X(sym, str, help) \ if (actionStr == str) \ return Command::sym; #include "Commands.def" return Command::Unknown; } CommandParser::CommandParser() : tempFile_("irony-server") { } Command *CommandParser::parse(const std::vector &argv) { command_.clear(); if (argv.begin() == argv.end()) { std::clog << "error: no command specified.\n" "See 'irony-server help' to list available commands\n"; return 0; } const std::string &actionStr = argv[0]; command_.action = actionFromString(actionStr); bool readCompileOptions = false; std::vector> positionalArgs; switch (command_.action) { case Command::SetDebug: positionalArgs.push_back(OptionConverter(&command_.opt)); break; case Command::Parse: positionalArgs.push_back(StringConverter(&command_.file)); readCompileOptions = true; break; case Command::Complete: positionalArgs.push_back(StringConverter(&command_.file)); positionalArgs.push_back(UnsignedIntConverter(&command_.line)); positionalArgs.push_back(UnsignedIntConverter(&command_.column)); readCompileOptions = true; break; case Command::GetType: positionalArgs.push_back(UnsignedIntConverter(&command_.line)); positionalArgs.push_back(UnsignedIntConverter(&command_.column)); break; case Command::SetUnsaved: positionalArgs.push_back(StringConverter(&command_.file)); positionalArgs.push_back(StringConverter(&command_.unsavedFile)); break; case Command::ResetUnsaved: positionalArgs.push_back(StringConverter(&command_.file)); break; case Command::Candidates: positionalArgs.push_back(StringConverter(&command_.prefix)); positionalArgs.push_back(PrefixMatchStyleConverter(&command_.style)); break; case Command::CompletionDiagnostics: case Command::Diagnostics: case Command::Help: case Command::Exit: // no-arguments commands break; case Command::GetCompileOptions: positionalArgs.push_back(StringConverter(&command_.dir)); positionalArgs.push_back(StringConverter(&command_.file)); break; case Command::Unknown: std::clog << "error: invalid command specified: " << actionStr << "\n"; return 0; } auto argsBegin = argv.begin() + 1; const auto argsEnd = std::find(argsBegin, argv.end(), "--"); const int argCount = std::distance(argsBegin, argsEnd); // compile options are provided after '--' if (readCompileOptions && argsEnd != argv.end()) { command_.flags.assign(std::next(argsEnd), argv.end()); } if (argCount != static_cast(positionalArgs.size())) { std::clog << "error: invalid number of arguments for '" << actionStr << "' (requires " << positionalArgs.size() << " got " << argCount << ")\n"; return 0; } for (auto fn : positionalArgs) { if (!fn(*argsBegin)) { std::clog << "error: parsing command '" << actionStr << "': invalid argument '" << *argsBegin << "'\n"; return 0; } ++argsBegin; } // '-' is used as a special file to inform that the buffer hasn't been saved // on disk and only the buffer content is available. libclang needs a file, so // this is treated as a special value for irony-server to create a temporary // file for this. note that libclang will gladly accept '-' as a filename but // we don't want to let this happen since irony already reads stdin. if (command_.file == "-") { command_.file = tempFile_.getPath(); } return &command_; } irony-mode-1.2.0/server/src/Command.h000066400000000000000000000025601317254426700174360ustar00rootroot00000000000000/**-*-C++-*- * \file * \author Guillaume Papin * * \brief Command parser declarations. * * This file is distributed under the GNU General Public License. See * COPYING for details. */ #ifndef IRONY_MODE_SERVER_COMMAND_H_ #define IRONY_MODE_SERVER_COMMAND_H_ #include "support/CIndex.h" #include "support/TemporaryFile.h" #include "Style.h" #include #include #include class TemporaryFile; // TODO: a tagged union? struct Command { Command() { clear(); } void clear() { action = Unknown; flags.clear(); file.clear(); unsavedFile.clear(); dir.clear(); prefix.clear(); style = PrefixMatchStyle::Exact; line = 0; column = 0; opt = false; } #define X(sym, str, desc) sym, enum Action { #include "Commands.def" } action; std::vector flags; std::string file; std::string unsavedFile; std::string dir; std::string prefix; PrefixMatchStyle style; unsigned line; unsigned column; bool opt; }; std::ostream &operator<<(std::ostream &os, const Command::Action &action); std::ostream &operator<<(std::ostream &os, const Command &command); class CommandParser { public: CommandParser(); Command *parse(const std::vector &argv); private: Command command_; TemporaryFile tempFile_; }; #endif // IRONY_MODE_SERVER_COMMAND_H_ irony-mode-1.2.0/server/src/Commands.def000066400000000000000000000030011317254426700201170ustar00rootroot00000000000000/**-*-C++-*- * \file * \author Guillaume Papin * * \brief Command list. * * This file is distributed under the GNU General Public License. See * COPYING for details. */ #ifndef X #error Please define the 'X(id, command, description)' macro before inclusion! #endif X(Candidates, "candidates", "PREFIX STYLE - print completion candidates (require previous complete). " "STYLE is \"exact\", \"case-insensitive\" or \"smart-case\"") X(Complete, "complete", "FILE LINE COL [-- [COMPILE_OPTIONS...]] - perform code completion at a given location") X(CompletionDiagnostics, "completion-diagnostics", "print the diagnostics generated during complete") X(Diagnostics, "diagnostics", "print the diagnostics of the last parse") X(Exit, "exit", "exit interactive mode, print nothing") X(GetCompileOptions, "get-compile-options", "BUILD_DIR FILE - " "get compile options for FILE from JSON database in BUILD_DIR") X(GetType, "get-type", "LINE COL - get type of symbol at a given location") X(Help, "help", "show this message") X(Parse, "parse", "FILE [-- [COMPILE_OPTIONS...]] - parse the given file") X(ResetUnsaved, "reset-unsaved", "FILE - reset FILE, its content is up to date") X(SetDebug, "set-debug", "ON|OFF - enable or disable verbose logging") X(SetUnsaved, "set-unsaved", "FILE UNSAVED-CONTENT-FILE - tell irony-server that " "UNSAVED-CONTENT-FILE contains the effective content of FILE") // sentinel value, should be the last one X(Unknown, "", "") #undef X irony-mode-1.2.0/server/src/Irony.cpp000066400000000000000000000432351317254426700175170ustar00rootroot00000000000000/** * \file * \author Guillaume Papin * * \brief irony-server "API" definitions. * * \sa Irony.h for more information. * * This file is distributed under the GNU General Public License. See * COPYING for details. */ #include "Irony.h" #include "support/iomanip_quoted.h" #include #include #include #include #include namespace { std::string cxStringToStd(CXString cxString) { std::string stdStr; if (const char *cstr = clang_getCString(cxString)) { stdStr = cstr; } clang_disposeString(cxString); return stdStr; } const char *diagnosticSeverity(const CXDiagnostic &diagnostic) { switch (clang_getDiagnosticSeverity(diagnostic)) { case CXDiagnostic_Ignored: return "ignored"; case CXDiagnostic_Note: return "note"; case CXDiagnostic_Warning: return "warning"; case CXDiagnostic_Error: return "error"; case CXDiagnostic_Fatal: return "fatal"; } return "unknown"; } void dumpDiagnostic(const CXDiagnostic &diagnostic) { std::string file; unsigned line = 0, column = 0, offset = 0; CXSourceLocation location = clang_getDiagnosticLocation(diagnostic); if (!clang_equalLocations(location, clang_getNullLocation())) { CXFile cxFile; // clang_getInstantiationLocation() has been marked deprecated and // is aimed to be replaced by clang_getExpansionLocation(). #if CINDEX_VERSION >= 6 clang_getExpansionLocation(location, &cxFile, &line, &column, &offset); #else clang_getInstantiationLocation(location, &cxFile, &line, &column, &offset); #endif file = cxStringToStd(clang_getFileName(cxFile)); } const char *severity = diagnosticSeverity(diagnostic); std::string message = cxStringToStd(clang_getDiagnosticSpelling(diagnostic)); std::cout << '(' << support::quoted(file) // << ' ' << line // << ' ' << column // << ' ' << offset // << ' ' << severity // << ' ' << support::quoted(message) // << ")\n"; } bool readFileContent(const std::string &filename, Irony::UnsavedBuffer &outBuf) { std::ifstream ifs(filename.c_str(), std::ios::in | std::ios::binary | std::ios::ate); if (!ifs.is_open()) { return false; } // FIXME: it's possible that this method of reading the file is 100% reliable, // I can't confirm that tellg() is guaranteed to return a byte count. // std::streamoff does not mention 'byte'. // In practice it seems to work but this may be just luck. // See also this discussion: // - http://stackoverflow.com/questions/22984956/tellg-function-give-wrong-size-of-file/22986486#22986486 auto nbytes = ifs.tellg(); if (nbytes == std::ifstream::pos_type(-1)) { return false; } outBuf.resize(nbytes); ifs.seekg(0, std::ios::beg); ifs.read(&outBuf[0], outBuf.size()); if (!ifs){ outBuf.clear(); return false; } return true; } } // unnamed namespace Irony::Irony() : activeTu_(nullptr), activeCompletionResults_(nullptr), debug_(false) { } void Irony::parse(const std::string &file, const std::vector &flags) { resetCache(); activeTu_ = tuManager_.parse(file, flags, cxUnsavedFiles_); file_ = file; if (activeTu_ == nullptr) { std::cout << "(error . (" << "parse-error" << " \"failed to parse file\"" << " " << support::quoted(file) << "))\n"; return; } std::cout << "(success . t)\n"; } void Irony::diagnostics() const { unsigned diagnosticCount; if (activeTu_ == nullptr) { diagnosticCount = 0; } else { diagnosticCount = clang_getNumDiagnostics(activeTu_); } std::cout << "(\n"; for (unsigned i = 0; i < diagnosticCount; ++i) { CXDiagnostic diagnostic = clang_getDiagnostic(activeTu_, i); dumpDiagnostic(diagnostic); clang_disposeDiagnostic(diagnostic); } std::cout << ")\n"; } void Irony::resetCache() { activeTu_ = nullptr; if (activeCompletionResults_ != nullptr) { clang_disposeCodeCompleteResults(activeCompletionResults_); activeCompletionResults_ = nullptr; } } void Irony::getType(unsigned line, unsigned col) const { if (activeTu_ == nullptr) { std::clog << "W: get-type - parse wasn't called\n"; std::cout << "nil\n"; return; } CXFile cxFile = clang_getFile(activeTu_, file_.c_str()); CXSourceLocation sourceLoc = clang_getLocation(activeTu_, cxFile, line, col); CXCursor cursor = clang_getCursor(activeTu_, sourceLoc); if (clang_Cursor_isNull(cursor)) { // TODO: "error: no type at point"? std::cout << "nil"; return; } CXType cxTypes[2]; cxTypes[0] = clang_getCursorType(cursor); cxTypes[1] = clang_getCanonicalType(cxTypes[0]); std::cout << "("; for (const CXType &cxType : cxTypes) { CXString typeDescr = clang_getTypeSpelling(cxType); std::string typeStr = clang_getCString(typeDescr); clang_disposeString(typeDescr); if (typeStr.empty()) break; std::cout << support::quoted(typeStr) << " "; } std::cout << ")\n"; } namespace { class CompletionChunk { public: explicit CompletionChunk(CXCompletionString completionString) : completionString_(completionString) , numChunks_(clang_getNumCompletionChunks(completionString_)) , chunkIdx_(0) { } bool hasNext() const { return chunkIdx_ < numChunks_; } void next() { if (!hasNext()) { assert(0 && "out of range completion chunk"); abort(); } ++chunkIdx_; } CXCompletionChunkKind kind() const { return clang_getCompletionChunkKind(completionString_, chunkIdx_); } // TODO: operator>> so that one can re-use string allocated buffer std::string text() const { return cxStringToStd( clang_getCompletionChunkText(completionString_, chunkIdx_)); } private: CXCompletionString completionString_; unsigned int numChunks_; unsigned chunkIdx_; }; } // unnamed namespace void Irony::complete(const std::string &file, unsigned line, unsigned col, const std::vector &flags) { resetCache(); if (CXTranslationUnit tu = tuManager_.getOrCreateTU(file, flags, cxUnsavedFiles_)) { activeCompletionResults_ = clang_codeCompleteAt(tu, file.c_str(), line, col, const_cast(cxUnsavedFiles_.data()), cxUnsavedFiles_.size(), (clang_defaultCodeCompleteOptions() & ~CXCodeComplete_IncludeCodePatterns) #if HAS_BRIEF_COMMENTS_IN_COMPLETION | CXCodeComplete_IncludeBriefComments #endif ); } if (activeCompletionResults_ == nullptr) { std::cout << "(error . (" << "complete-error" << " \"failed to perform code completion\"" << " " << support::quoted(file) << " " << line << " " << col << "))\n"; return; } clang_sortCodeCompletionResults(activeCompletionResults_->Results, activeCompletionResults_->NumResults); std::cout << "(success . t)\n"; } namespace { bool hasUppercase(const std::string &prefix) { for (char c : prefix) { if (std::isupper(c)) { return true; } } return false; } bool isEqual(const bool insensitive, const char a, const char b) { if (insensitive) { return std::tolower(a) == std::tolower(b); } else { return a == b; } } bool startsWith(const std::string& str, const std::string &prefix, bool caseInsensitive) { if (str.length() < prefix.length()) { return false; } const auto charCmp = [&](const char a, const char b) { return isEqual(caseInsensitive, a, b); }; auto res = std::mismatch(prefix.begin(), prefix.end(), str.begin(), charCmp); return res.first == prefix.end(); } bool isStyleCaseInsensitive(const std::string &prefix, PrefixMatchStyle style) { if (style == PrefixMatchStyle::SmartCase) { // For SmartCase style, do case insensitive matching only there isn't upper // case letter. if (!hasUppercase(prefix)) { style = PrefixMatchStyle::CaseInsensitive; } } return style == PrefixMatchStyle::CaseInsensitive; } } // unnamed namespace void Irony::completionDiagnostics() const { unsigned diagnosticCount; if (activeCompletionResults_ == nullptr) { diagnosticCount = 0; } else { diagnosticCount = clang_codeCompleteGetNumDiagnostics(activeCompletionResults_); } std::cout << "(\n"; for (unsigned i = 0; i < diagnosticCount; ++i) { CXDiagnostic diagnostic = clang_codeCompleteGetDiagnostic(activeCompletionResults_, i); dumpDiagnostic(diagnostic); clang_disposeDiagnostic(diagnostic); } std::cout << ")\n"; } void Irony::candidates(const std::string &prefix, PrefixMatchStyle style) const { if (activeCompletionResults_ == nullptr) { std::cout << "nil\n"; return; } bool caseInsensitive = isStyleCaseInsensitive(prefix, style); CXCodeCompleteResults *completions = activeCompletionResults_; std::cout << "(\n"; // re-use the same buffers to avoid unnecessary allocations std::string typedtext, brief, resultType, prototype, postCompCar, available; std::vector postCompCdr; for (unsigned i = 0; i < completions->NumResults; ++i) { CXCompletionResult candidate = completions->Results[i]; CXAvailabilityKind availability = clang_getCompletionAvailability(candidate.CompletionString); unsigned priority = clang_getCompletionPriority(candidate.CompletionString); unsigned annotationStart = 0; bool typedTextSet = false; bool hasPrefix = true; typedtext.clear(); brief.clear(); resultType.clear(); prototype.clear(); postCompCar.clear(); postCompCdr.clear(); available.clear(); switch (availability) { case CXAvailability_NotAvailable: // No benefits to expose this to elisp for now continue; case CXAvailability_Available: available = "available"; break; case CXAvailability_Deprecated: available = "deprecated"; break; case CXAvailability_NotAccessible: available = "not-accessible"; break; } for (CompletionChunk chunk(candidate.CompletionString); chunk.hasNext(); chunk.next()) { char ch = 0; auto chunkKind = chunk.kind(); switch (chunkKind) { case CXCompletionChunk_ResultType: resultType = chunk.text(); break; case CXCompletionChunk_TypedText: case CXCompletionChunk_Text: case CXCompletionChunk_Placeholder: case CXCompletionChunk_Informative: case CXCompletionChunk_CurrentParameter: prototype += chunk.text(); break; case CXCompletionChunk_LeftParen: ch = '('; break; case CXCompletionChunk_RightParen: ch = ')'; break; case CXCompletionChunk_LeftBracket: ch = '['; break; case CXCompletionChunk_RightBracket: ch = ']'; break; case CXCompletionChunk_LeftBrace: ch = '{'; break; case CXCompletionChunk_RightBrace: ch = '}'; break; case CXCompletionChunk_LeftAngle: ch = '<'; break; case CXCompletionChunk_RightAngle: ch = '>'; break; case CXCompletionChunk_Comma: ch = ','; break; case CXCompletionChunk_Colon: ch = ':'; break; case CXCompletionChunk_SemiColon: ch = ';'; break; case CXCompletionChunk_Equal: ch = '='; break; case CXCompletionChunk_HorizontalSpace: ch = ' '; break; case CXCompletionChunk_VerticalSpace: ch = '\n'; break; case CXCompletionChunk_Optional: // ignored for now break; } if (ch != 0) { prototype += ch; // commas look better followed by a space if (ch == ',') { prototype += ' '; } } if (typedTextSet) { if (ch != 0) { postCompCar += ch; if (ch == ',') { postCompCar += ' '; } } else if (chunkKind == CXCompletionChunk_Text || chunkKind == CXCompletionChunk_TypedText) { postCompCar += chunk.text(); } else if (chunkKind == CXCompletionChunk_Placeholder || chunkKind == CXCompletionChunk_CurrentParameter) { postCompCdr.push_back(postCompCar.size()); postCompCar += chunk.text(); postCompCdr.push_back(postCompCar.size()); } } // Consider only the first typed text. The CXCompletionChunk_TypedText // doc suggests that exactly one typed text will be given but at least // in Objective-C it seems that more than one can appear, see: // https://github.com/Sarcasm/irony-mode/pull/78#issuecomment-37115538 if (chunkKind == CXCompletionChunk_TypedText && !typedTextSet) { typedtext = chunk.text(); if (!startsWith(typedtext, prefix, caseInsensitive)) { hasPrefix = false; break; } // annotation is what comes after the typedtext annotationStart = prototype.size(); typedTextSet = true; } } if (!hasPrefix) { continue; } if (!typedTextSet) { // clang may generate candidates without any typedText, and we may // generate some output like: // ("" 1 "bool" "" "hasUppercase(const std::string &prefix)" // 0 ("") available) // That will cause infinite completion in irony.el continue; } #if HAS_BRIEF_COMMENTS_IN_COMPLETION brief = cxStringToStd( clang_getCompletionBriefComment(candidate.CompletionString)); #endif // see irony-completion.el#irony-completion-candidates std::cout << '(' << support::quoted(typedtext) << ' ' << priority << ' ' << support::quoted(resultType) << ' ' << support::quoted(brief) << ' ' << support::quoted(prototype) << ' ' << annotationStart << " (" << support::quoted(postCompCar); for (unsigned index : postCompCdr) std::cout << ' ' << index; std::cout << ")" << ' ' << available << ")\n"; } std::cout << ")\n"; } void Irony::computeCxUnsaved() { cxUnsavedFiles_.clear(); for (const auto &p : filenameToContent_) { CXUnsavedFile cxUnsavedFile; cxUnsavedFile.Filename = p.first.c_str(); cxUnsavedFile.Contents = p.second.data(); cxUnsavedFile.Length = p.second.size(); cxUnsavedFiles_.push_back(cxUnsavedFile); } } void Irony::setUnsaved(const std::string &file, const std::string &unsavedContentFile) { resetCache(); UnsavedBuffer content; if (!readFileContent(unsavedContentFile, content)) { filenameToContent_.erase(file); std::cout << "(error . (" << "file-read-error" << " \"failed to read unsaved buffer\"" << " " << support::quoted(file) << " " << support::quoted(unsavedContentFile) << ")\n"; } else { filenameToContent_[file] = content; std::cout << "(success . t)\n"; } computeCxUnsaved(); } void Irony::resetUnsaved(const std::string &file) { resetCache(); const auto erasedCount = filenameToContent_.erase(file); if (erasedCount == 0) { std::cout << "(error . (" << "no-such-entry" << " \"failed reset unsaved buffer\"" << " " << support::quoted(file) << ")\n"; } else { std::cout << "(success . t)\n"; } computeCxUnsaved(); } void Irony::getCompileOptions(const std::string &buildDir, const std::string &file) const { #if !(HAS_COMPILATION_DATABASE) (void)buildDir; (void)file; CXString cxVersionString = clang_getClangVersion(); std::cout << "(error . (" << "unsupported" << " \"compilation database requires Clang >= 3.2\"" << " " << support::quoted(clang_getCString(cxVersionString)) << "))\n"; clang_disposeString(cxVersionString); return; #else CXCompilationDatabase_Error error; CXCompilationDatabase db = clang_CompilationDatabase_fromDirectory(buildDir.c_str(), &error); switch (error) { case CXCompilationDatabase_CanNotLoadDatabase: std::cout << "(error . (" << "cannot-load-database" << " \"failed to load compilation database from directory\"" << " " << support::quoted(buildDir) << "))\n"; return; case CXCompilationDatabase_NoError: break; } CXCompileCommands compileCommands = clang_CompilationDatabase_getCompileCommands(db, file.c_str()); std::cout << "(success . (\n"; for (unsigned i = 0, numCompileCommands = clang_CompileCommands_getSize(compileCommands); i < numCompileCommands; ++i) { CXCompileCommand compileCommand = clang_CompileCommands_getCommand(compileCommands, i); std::cout << "(" << "("; for (unsigned j = 0, numArgs = clang_CompileCommand_getNumArgs(compileCommand); j < numArgs; ++j) { CXString arg = clang_CompileCommand_getArg(compileCommand, j); std::cout << support::quoted(clang_getCString(arg)) << " "; clang_disposeString(arg); } std::cout << ")" << " . "; CXString directory = clang_CompileCommand_getDirectory(compileCommand); std::cout << support::quoted(clang_getCString(directory)); clang_disposeString(directory); std::cout << ")\n"; } std::cout << "))\n"; clang_CompileCommands_dispose(compileCommands); clang_CompilationDatabase_dispose(db); #endif } irony-mode-1.2.0/server/src/Irony.h000066400000000000000000000071551317254426700171650ustar00rootroot00000000000000/**-*-C++-*- * \file * \author Guillaume Papin * * \brief irony-server "API" declarations. * * Contains the commands that the Emacs package relies on. These commands are * mostly wrappers around a subset of the features provided by libclang. Command * results are printed to \c std::cout as s-expr, in order to make it easy for * Emacs to consume. * * This file is distributed under the GNU General Public License. See * COPYING for details. */ #ifndef IRONY_MODE_SERVER_IRONY_H_ #define IRONY_MODE_SERVER_IRONY_H_ #include "TUManager.h" #include "Style.h" #include #include class Irony { public: // use std::string over std::vector because I have some doubts // that libclang expect unsaved buffers to be a null terminated C strings typedef std::string UnsavedBuffer; public: Irony(); bool isDebugEnabled() const { return debug_; } /// \name Command /// \{ /// \brief Set or unset debugging of commands. void setDebug(bool enable) { debug_ = enable; } /// Parse or reparse the given file and compile options. /// /// If the compile options have changed, the translation unit is re-created to /// take this into account. /// /// Output \c nil or \c t, whether or not parsing the translation unit /// succeeded. /// /// \sa diagnostics(), getType() void parse(const std::string &file, const std::vector &flags); /// Parse the given file for code completion. /// /// Shares the same semantics and output as \c parse(). /// /// \sa candidates(), completionDiagnostics() void complete(const std::string &file, unsigned line, unsigned col, const std::vector &flags); void setUnsaved(const std::string &file, const std::string &unsavedContentFile); void resetUnsaved(const std::string &file); /// \} /// \name Queries /// \{ /// \brief Retrieve the last parse diagnostics for the given file. /// /// \pre parse() was called. void diagnostics() const; /// \brief Get types of symbol at a given location. /// /// Example: /// /// \code /// typedef int MyType; /// MyType a; /// \endcode /// /// Type of cursor location for 'a' is: /// /// \code{.el} /// ("MyType" "int") /// \endcode /// /// TODO: test with CXString(), seems to be twice the same string /// void getType(unsigned line, unsigned col) const; /// Get all the completion candidates. /// /// \pre complete() was called. void candidates(const std::string &prefix, PrefixMatchStyle style) const; /// Get the diagnostics produced by the last \c complete(). /// /// \pre complete() was called. void completionDiagnostics() const; /// \brief Get compile options from JSON database. /// /// \param buildDir Directory containing compile_commands.json /// \param file File to obtain compile commands for. /// /// Example output: /// /// \code{.el} /// ( /// (("-Wfoo" "-DBAR" "-Iqux") . "/path/to/working/directory") /// (("-Wfoo-alt" "-DBAR_ALT" "-Iqux/alt") . "/alt/working/directory") /// ) /// \endcode /// void getCompileOptions(const std::string &buildDir, const std::string &file) const; /// \} private: void resetCache(); void computeCxUnsaved(); private: TUManager tuManager_; std::map filenameToContent_; CXTranslationUnit activeTu_; std::string file_; std::vector cxUnsavedFiles_; CXCodeCompleteResults *activeCompletionResults_; bool debug_; }; #endif // IRONY_MODE_SERVER_IRONY_H_ irony-mode-1.2.0/server/src/Style.h000066400000000000000000000005151317254426700171560ustar00rootroot00000000000000/**-*-C++-*- * \file * * \brief Style definition. * * This file is distributed under the GNU General Public License. See * COPYING for details. */ #ifndef IRONY_MODE_SERVER_STYLE_H_ #define IRONY_MODE_SERVER_STYLE_H_ enum class PrefixMatchStyle { Exact, CaseInsensitive, SmartCase, }; #endif //IRONY_MODE_SERVER_STYLE_H_ irony-mode-1.2.0/server/src/TUManager.cpp000066400000000000000000000135461317254426700202440ustar00rootroot00000000000000/** * \file * \author Guillaume Papin * * \brief See TUManager.hh * * This file is distributed under the GNU General Public License. See * COPYING for details. * */ #include "TUManager.h" #include TUManager::TUManager() : index_(clang_createIndex(0, 0)) , translationUnits_() , parseTUOptions_(clang_defaultEditingTranslationUnitOptions()) { // this seems necessary to trigger "correct" reparse (/codeCompleteAt) // clang_reparseTranslationUnit documentation states: // // > * \param TU The translation unit whose contents will be re-parsed. The // > * translation unit must originally have been built with // > * \c clang_createTranslationUnitFromSourceFile(). // // clang_createTranslationUnitFromSourceFile() is just a call to // clang_parseTranslationUnit() with // CXTranslationUnit_DetailedPreprocessingRecord enabled but because we want // some other flags to be set we can't just call // clang_createTranslationUnitFromSourceFile() parseTUOptions_ |= CXTranslationUnit_DetailedPreprocessingRecord; #if HAS_BRIEF_COMMENTS_IN_COMPLETION parseTUOptions_ |= CXTranslationUnit_IncludeBriefCommentsInCodeCompletion; #endif // XXX: A bug in old version of Clang (at least '3.1-8') caused the completion // to fail on the standard library types when // CXTranslationUnit_PrecompiledPreamble is used. We disable this option for // old versions of libclang. As a result the completion will work but // significantly slower. // // -- https://github.com/Sarcasm/irony-mode/issues/4 if (CINDEX_VERSION < 6) { parseTUOptions_ &= ~CXTranslationUnit_PrecompiledPreamble; } #if CINDEX_VERSION >= 34 // Keep going even after fatal errors, or syntax checking will stop after the // first error. For instance unused variables will not be reported until the // error has been fixed. parseTUOptions_ |= CXTranslationUnit_KeepGoing; #endif } TUManager::~TUManager() { clang_disposeIndex(index_); } CXTranslationUnit &TUManager::tuRef(const std::string &filename, const std::vector &flags) { CXTranslationUnit &tu = translationUnits_[filename]; // if the flags changed since the last time, invalidate the translation unit auto &flagsCache = flagsPerFileCache_[filename]; if (flagsCache.size() != flags.size() || !std::equal(flagsCache.begin(), flagsCache.end(), flags.begin())) { if (tu) { clang_disposeTranslationUnit(tu); tu = nullptr; } // remember the flags for the next parse flagsCache = flags; } return tu; } CXTranslationUnit TUManager::parse(const std::string &filename, const std::vector &flags, const std::vector &unsavedFiles) { CXTranslationUnit &tu = tuRef(filename, flags); if (tu == nullptr) { std::vector argv; #ifdef CLANG_RESOURCE_DIR // Make sure libclang find its builtin headers, this is a known issue with // libclang, see: // - http://lists.cs.uiuc.edu/pipermail/cfe-dev/2012-July/022893.html // // > Make sure that Clang is using its own . It will be in a directory // > ending in clang/3.2/include/ where 3.2 is the version of clang that you // > are using. You may need to explicitly add it to your header search. // > Usually clang finds this directory relative to the executable with // > CompilerInvocation::GetResourcesPath(Argv0, MainAddr), but using just // > the libraries, it can't automatically find it. argv.push_back("-resource-dir"); argv.push_back(CLANG_RESOURCE_DIR); #endif for (auto &flag : flags) { argv.push_back(flag.c_str()); } tu = clang_parseTranslationUnit( index_, filename.c_str(), argv.data(), static_cast(argv.size()), const_cast(unsavedFiles.data()), unsavedFiles.size(), parseTUOptions_); } if (tu == nullptr) { std::clog << "error: libclang couldn't parse '" << filename << "'\n"; return nullptr; } // Reparsing is necessary to enable optimizations. // // From the clang mailing list (cfe-dev): // From: Douglas Gregor // Subject: Re: Clang indexing library performance // ... // You want to use the "default editing options" when parsing the translation // unit // clang_defaultEditingTranslationUnitOptions() // and then reparse at least once. That will enable the various // code-completion optimizations that should bring this time down // significantly. if (clang_reparseTranslationUnit( tu, unsavedFiles.size(), const_cast(unsavedFiles.data()), clang_defaultReparseOptions(tu))) { // a 'fatal' error occured (even a diagnostic is impossible) clang_disposeTranslationUnit(tu); std::clog << "error: libclang couldn't reparse '" << filename << "'\n"; tu = 0; return nullptr; } return tu; } CXTranslationUnit TUManager::getOrCreateTU(const std::string &filename, const std::vector &flags, const std::vector &unsavedFiles) { if (auto tu = tuRef(filename, flags)) return tu; return parse(filename, flags, unsavedFiles); } void TUManager::invalidateCachedTU(const std::string &filename) { TranslationUnitsMap::iterator it = translationUnits_.find(filename); if (it != translationUnits_.end()) { if (CXTranslationUnit &tu = it->second) clang_disposeTranslationUnit(tu); translationUnits_.erase(it); } } void TUManager::invalidateAllCachedTUs() { TranslationUnitsMap::iterator it = translationUnits_.begin(); while (it != translationUnits_.end()) { if (CXTranslationUnit &tu = it->second) { clang_disposeTranslationUnit(tu); translationUnits_.erase(it++); // post-increment keeps the iterator valid } else { ++it; } } } irony-mode-1.2.0/server/src/TUManager.h000066400000000000000000000062121317254426700177010ustar00rootroot00000000000000/**-*-C++-*- * \file * \author Guillaume Papin * * \brief Translation Unit manager. * * Keeps a cache of translation units, reparsing or recreating them as * necessary. * * This file is distributed under the GNU General Public License. See * COPYING for details. */ #ifndef IRONY_MODE_SERVER_TUMANAGER_H_ #define IRONY_MODE_SERVER_TUMANAGER_H_ #include "support/CIndex.h" #include "support/NonCopyable.h" #include #include #include class TUManager : public util::NonCopyable { public: TUManager(); ~TUManager(); /** * \brief Parse \p filename with flag \p flags. * * The first time call \c clang_parseTranslationUnit() and save the TU in the * member \c translationUnits_, The next call with the same \p filename will * call \c clang_reparseTranslationUnit(). * * usage: * \code * std::vector flags; * flags.push_back("-I../utils"); * CXTranslationUnit tu = tuManager.parse("file.cpp", flags); * * if (! tu) * std::cerr << "parsing translation unit failed\n"; * \endcode * * \return The translation unit, if the parsing failed the translation unit * will be \c NULL. */ CXTranslationUnit parse(const std::string &filename, const std::vector &flags, const std::vector &unsavedFiles); /** * \brief Retrieve, creating it if necessary the TU associated to filename. * * \return The translation unit. Will be NULL if the translation unit couldn't * be created. */ CXTranslationUnit getOrCreateTU(const std::string &filename, const std::vector &flags, const std::vector &unsavedFiles); /** * \brief Invalidate a given cached TU, the next use of a TU will require * reparsing. * * This can be useful for example: when the flags used to compile a file have * changed. * * \param filename The filename for which the associated * translation unit flags need to be invalidated. * * \sa invalidateAllCachedTUs() */ void invalidateCachedTU(const std::string &filename); /** * \brief Invalidate all cached TU, the next use of a TU will require * reparsing. * * \sa invalidateCachedTU() */ void invalidateAllCachedTUs(); private: /** * \brief Get a reference to the translation unit that matches \p filename * with the given set of flags. * * The TU will be null if it has never been parsed or if the flags have * changed. * * \todo Find a proper name. */ CXTranslationUnit &tuRef(const std::string &filename, const std::vector &flags); private: typedef std::map TranslationUnitsMap; typedef std::map> FilenameFlagsMap; private: CXIndex index_; TranslationUnitsMap translationUnits_; // cache variable FilenameFlagsMap flagsPerFileCache_; unsigned parseTUOptions_; }; #endif /* !IRONY_MODE_SERVER_TUMANAGER_H_ */ irony-mode-1.2.0/server/src/main.cpp000066400000000000000000000127141317254426700173410ustar00rootroot00000000000000/** * \file * \author Guillaume Papin * * This file is distributed under the GNU General Public License. See * COPYING for details. */ #include "Irony.h" #include "Command.h" #include "support/CIndex.h" #include "support/CommandLineParser.h" #include #include #include #include #include #include #include #include static void printHelp() { std::cout << "usage: irony-server [OPTIONS...] [COMMAND] [ARGS...]\n" "\n" "Options:\n" " -v, --version\n" " -h, --help\n" " -i, --interactive\n" " -d, --debug\n" " --log-file PATH\n" "\n" "Commands:\n"; #define X(sym, str, desc) \ if (Command::sym != Command::Unknown) \ std::cout << std::left << std::setw(25) << " " str << desc << "\n"; #include "Commands.def" } static void printVersion() { // do not change the format for the first line, external programs should be // able to rely on it std::cout << "irony-server version " IRONY_PACKAGE_VERSION "\n"; CXString cxVersionString = clang_getClangVersion(); std::cout << clang_getCString(cxVersionString) << "\n"; clang_disposeString(cxVersionString); } struct CommandProviderInterface { virtual ~CommandProviderInterface() { } virtual std::vector nextCommand() = 0; }; struct CommandLineCommandProvider : CommandProviderInterface { CommandLineCommandProvider(const std::vector &argv) : argv_(argv), firstCall_(true) { } std::vector nextCommand() { if (firstCall_) { firstCall_ = false; return argv_; } return std::vector(1, "exit"); } private: std::vector argv_; bool firstCall_; }; struct InteractiveCommandProvider : CommandProviderInterface { std::vector nextCommand() { std::string line; if (std::getline(std::cin, line)) { return unescapeCommandLine(line); } return std::vector(1, "exit"); } }; struct RestoreClogOnExit { RestoreClogOnExit() : rdbuf_(std::clog.rdbuf()) { } ~RestoreClogOnExit() { std::clog.rdbuf(rdbuf_); } private: RestoreClogOnExit(const RestoreClogOnExit &); RestoreClogOnExit &operator=(const RestoreClogOnExit &); private: std::streambuf *rdbuf_; }; int main(int ac, const char *av[]) { std::vector argv(&av[1], &av[ac]); // stick to STL streams, no mix of C and C++ for IO operations std::ios_base::sync_with_stdio(false); bool interactiveMode = false; Irony irony; if (ac == 1) { printHelp(); return 1; } std::ofstream logFile; // When logging to a specific file, std::clog.rdbuf() is replaced by the log // file's one. When we return from the main, this buffer is deleted (at the // same time as logFile) but std::clog is still active, and will try to // release the rdbuf() which has already been released in logFile's // destructor. To avoid this we restore std::clog()'s original rdbuf on exit. RestoreClogOnExit clogBufferRestorer; unsigned optCount = 0; while (optCount < argv.size()) { const std::string &opt = argv[optCount]; if (opt.c_str()[0] != '-') break; if (opt == "--help" || opt == "-h") { printHelp(); return 0; } if (opt == "--version" || opt == "-v") { printVersion(); return 0; } if (opt == "--interactive" || opt == "-i") { interactiveMode = true; } else if (opt == "--debug" || opt == "-d") { irony.setDebug(true); } else if (opt == "--log-file" && (optCount + 1) < argv.size()) { ++optCount; logFile.open(argv[optCount]); std::clog.rdbuf(logFile.rdbuf()); } else { std::cerr << "error: invalid option '" << opt << "'\n"; return 1; } ++optCount; } argv.erase(argv.begin(), argv.begin() + optCount); CommandParser commandParser; std::unique_ptr commandProvider; if (interactiveMode) { commandProvider.reset(new InteractiveCommandProvider()); } else { commandProvider.reset(new CommandLineCommandProvider(argv)); } while (Command *c = commandParser.parse(commandProvider->nextCommand())) { if (c->action != Command::Exit) { std::clog << "execute: " << *c << std::endl; } switch (c->action) { case Command::Help: printHelp(); break; case Command::Candidates: irony.candidates(c->prefix, c->style); break; case Command::CompletionDiagnostics: irony.completionDiagnostics(); break; case Command::Complete: irony.complete(c->file, c->line, c->column, c->flags); break; case Command::Diagnostics: irony.diagnostics(); break; case Command::Exit: return 0; case Command::GetCompileOptions: irony.getCompileOptions(c->dir, c->file); break; case Command::GetType: irony.getType(c->line, c->column); break; case Command::Parse: irony.parse(c->file, c->flags); break; case Command::SetDebug: irony.setDebug(c->opt); break; case Command::SetUnsaved: irony.setUnsaved(c->file, c->unsavedFile); break; case Command::ResetUnsaved: irony.resetUnsaved(c->file); break; case Command::Unknown: assert(0 && "unreacheable code...reached!"); break; } std::cout << "\n;;EOT\n" << std::flush; } return 1; } irony-mode-1.2.0/server/src/support/000077500000000000000000000000001317254426700174205ustar00rootroot00000000000000irony-mode-1.2.0/server/src/support/CIndex.h000066400000000000000000000015231317254426700207440ustar00rootroot00000000000000/** * \file * \brief Wrapper around Clang Indexing Public C Interface header. * * This file is distributed under the GNU General Public License. See * COPYING for details. */ #ifndef IRONY_MODE_SERVER_SUPPORT_CINDEXVERSION_H_ #define IRONY_MODE_SERVER_SUPPORT_CINDEXVERSION_H_ #include /// Use \#if CINDEX_VERSION_VERSION > 10047 to test for /// CINDEX_VERSION_MAJOR = 1 and CINDEX_VERSION_MINOR = 47. #ifndef CINDEX_VERSION #define CINDEX_VERSION 0 // pre-clang 3.2 support #endif #if CINDEX_VERSION >= 6 #define HAS_BRIEF_COMMENTS_IN_COMPLETION 1 #else #define HAS_BRIEF_COMMENTS_IN_COMPLETION 0 #endif #if CINDEX_VERSION >= 6 #define HAS_COMPILATION_DATABASE 1 #include #else #define HAS_COMPILATION_DATABASE 0 #endif #endif /* !IRONY_MODE_SERVER_SUPPORT_CINDEXVERSION_H_ */ irony-mode-1.2.0/server/src/support/CommandLineParser.cpp000066400000000000000000000053241317254426700234730ustar00rootroot00000000000000/** * \file * \author Guillaume Papin * * This file is distributed under the GNU General Public License. See * COPYING for details. */ #include "CommandLineParser.h" namespace { /// \brief A parser for escaped strings of command line arguments. /// /// Assumes \-escaping for quoted arguments (see the documentation of /// unescapeCommandLine(...)). class CommandLineArgumentParser { public: CommandLineArgumentParser(const std::string &commandLine) : input_(commandLine), position_(input_.begin() - 1) { } std::vector parse() { bool hasMoreInput = true; while (hasMoreInput && nextNonWhitespace()) { std::string argument; hasMoreInput = parseStringInto(argument); commandLine_.push_back(argument); } return commandLine_; } private: // All private methods return true if there is more input available. bool parseStringInto(std::string &string) { do { if (*position_ == '"') { if (!parseDoubleQuotedStringInto(string)) return false; } else if (*position_ == '\'') { if (!parseSingleQuotedStringInto(string)) return false; } else { if (!parseFreeStringInto(string)) return false; } } while (*position_ != ' '); return true; } bool parseDoubleQuotedStringInto(std::string &string) { if (!next()) return false; while (*position_ != '"') { if (!skipEscapeCharacter()) return false; string.push_back(*position_); if (!next()) return false; } return next(); } bool parseSingleQuotedStringInto(std::string &string) { if (!next()) return false; while (*position_ != '\'') { string.push_back(*position_); if (!next()) return false; } return next(); } bool parseFreeStringInto(std::string &string) { do { if (!skipEscapeCharacter()) return false; string.push_back(*position_); if (!next()) return false; } while (*position_ != ' ' && *position_ != '"' && *position_ != '\''); return true; } bool skipEscapeCharacter() { if (*position_ == '\\') { return next(); } return true; } bool nextNonWhitespace() { do { if (!next()) return false; } while (*position_ == ' '); return true; } bool next() { ++position_; return position_ != input_.end(); } private: const std::string input_; std::string::const_iterator position_; std::vector commandLine_; }; } // unnamed namespace std::vector unescapeCommandLine(const std::string &escapedCommandLine) { CommandLineArgumentParser parser(escapedCommandLine); return parser.parse(); } irony-mode-1.2.0/server/src/support/CommandLineParser.h000066400000000000000000000011071317254426700231330ustar00rootroot00000000000000/** * \file * \brief Facility to parse a command line into a string array. * * \note Please note that the code borrowed from the Clang, * lib/Tooling/JSONCompilationDatabase.cpp. * * This file is distributed under the GNU General Public License. See * COPYING for details. */ #ifndef IRONY_MODE_SERVER_SUPPORT_COMMAND_LINE_PARSER_H_ #define IRONY_MODE_SERVER_SUPPORT_COMMAND_LINE_PARSER_H_ #include #include std::vector unescapeCommandLine(const std::string &escapedCommandLine); #endif // IRONY_MODE_SERVER_SUPPORT_COMMAND_LINE_PARSER_H_ irony-mode-1.2.0/server/src/support/NonCopyable.h000066400000000000000000000013101317254426700217750ustar00rootroot00000000000000/**-*-C++-*- * \file * \author Guillaume Papin * * \brief NonCopyable class like in Boost. * * \see http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Non-copyable_Mixin * * This file is distributed under the GNU General Public License. See * COPYING for details. */ #ifndef IRONY_MODE_SERVER_SUPPORT_NONCOPYABLE_H_ #define IRONY_MODE_SERVER_SUPPORT_NONCOPYABLE_H_ namespace util { class NonCopyable { protected: NonCopyable() { } // Protected non-virtual destructor ~NonCopyable() { } private: NonCopyable(const NonCopyable &); NonCopyable &operator=(const NonCopyable &); }; } // ! namespace util #endif /* !IRONY_MODE_SERVER_SUPPORT_NONCOPYABLE_H_ */ irony-mode-1.2.0/server/src/support/TemporaryFile.cpp000066400000000000000000000037251317254426700227150ustar00rootroot00000000000000/** * \file * \author Guillaume Papin * * This file is distributed under the GNU General Public License. See * COPYING for details. */ #include "TemporaryFile.h" #include #include #include #include #include #include static std::string getTemporaryFileDirectory() { const char *temporaryDirEnvVars[] = {"TMPDIR", "TMP", "TEMP", "TEMPDIR"}; for (const char *envVar : temporaryDirEnvVars) { if (const char *dir = std::getenv(envVar)) return dir; } return "/tmp"; } TemporaryFile::TemporaryFile(const std::string &prefix, const std::string &suffix) : pathOrPattern_(prefix + "-%%%%%%" + suffix) { } TemporaryFile::~TemporaryFile() { if (openedFile_) { openedFile_.reset(); std::remove(pathOrPattern_.c_str()); } } const std::string &TemporaryFile::getPath() { if (!openedFile_) { openedFile_.reset(new std::fstream); std::random_device rd; std::default_random_engine e(rd()); std::uniform_int_distribution dist(0, 15); std::string pattern = pathOrPattern_; std::string tmpDir = getTemporaryFileDirectory() + "/"; int i = 0; do { // exiting is better than infinite loop if (++i > TemporaryFile::MAX_ATTEMPS) { std::cerr << "error: couldn't create temporary file, please check your " "temporary file directory (" << tmpDir << ")\n"; exit(EXIT_FAILURE); } // make the filename based on the pattern std::transform(pattern.begin(), pattern.end(), pathOrPattern_.begin(), [&e, &dist](char ch) { return ch == '%' ? "0123456789abcdef"[dist(e)] : ch; }); // create the file openedFile_->open(tmpDir + pathOrPattern_, std::ios_base::out); } while (!openedFile_->is_open()); pathOrPattern_ = tmpDir + pathOrPattern_; } return pathOrPattern_; } irony-mode-1.2.0/server/src/support/TemporaryFile.h000066400000000000000000000014731317254426700223600ustar00rootroot00000000000000/**-*-C++-*- * \file * \author Guillaume Papin * * \brief Not the best piece of code out there. * * This file is distributed under the GNU General Public License. See * COPYING for details. */ #ifndef IRONY_MODE_SERVER_SUPPORT_TEMPORARY_FILE_H_ #define IRONY_MODE_SERVER_SUPPORT_TEMPORARY_FILE_H_ #include #include #include class TemporaryFile { enum { // if we can't create the temp file, exits. MAX_ATTEMPS = 25 }; public: TemporaryFile(const std::string &prefix, const std::string &suffix = ""); ~TemporaryFile(); /// Returns the path of this temporary filename const std::string &getPath(); private: std::string pathOrPattern_; std::unique_ptr openedFile_; }; #endif // IRONY_MODE_SERVER_SUPPORT_TEMPORARY_FILE_H_ irony-mode-1.2.0/server/src/support/iomanip_quoted.h000066400000000000000000000020031317254426700226010ustar00rootroot00000000000000/**-*-C++-*- * \file * \brief Dumb implementation of something that might look like C++14 * std::quoted. * * This file is distributed under the GNU General Public License. See * COPYING for details. */ #ifndef IRONY_MODE_SERVER_SUPPORT_IOMANIP_QUOTED_H_ #define IRONY_MODE_SERVER_SUPPORT_IOMANIP_QUOTED_H_ #include #include namespace support { namespace detail { struct QuotedStringProxy { QuotedStringProxy(const std::string &s) : s(s) { } std::string s; }; std::ostream &operator<<(std::ostream &os, const QuotedStringProxy &q) { const std::string &s = q.s; os << '"'; if (s.find_first_of("\"\\") == std::string::npos) { os << s; } else { for (auto ch : s) { if (ch == '\\' || ch == '"') os << '\\'; os << ch; } } os << '"'; return os; } } // namespace detail detail::QuotedStringProxy quoted(const std::string &s) { return detail::QuotedStringProxy(s); } } // namespace support #endif // IRONY_MODE_SERVER_SUPPORT_IOMANIP_QUOTED_H_ irony-mode-1.2.0/server/test/000077500000000000000000000000001317254426700160745ustar00rootroot00000000000000irony-mode-1.2.0/server/test/CMakeLists.txt000066400000000000000000000001461317254426700206350ustar00rootroot00000000000000add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure) add_subdirectory (elisp) irony-mode-1.2.0/server/test/elisp/000077500000000000000000000000001317254426700172105ustar00rootroot00000000000000irony-mode-1.2.0/server/test/elisp/CMakeLists.txt000066400000000000000000000027141317254426700217540ustar00rootroot00000000000000# On MS-Windows, emacs_dir is a special environment variable, which # indicates the full path of the directory in which Emacs is # installed. # # There is no standard location that I know of for Emacs on Windows so # using this special environment variable will at least help people # who build the server from inside Emacs. if(DEFINED ENV{emacs_dir}) list(APPEND EMACS_EXECUTABLE_HINTS $ENV{emacs_dir}/bin) endif() find_program(EMACS_EXECUTABLE emacs HINTS ${EMACS_EXECUTABLE_HINTS}) if (EMACS_EXECUTABLE) message(STATUS "Found emacs: ${EMACS_EXECUTABLE}") else() message(WARNING "emacs not found: elisp tests will be skipped!") return() endif() # # add_ert_test() # # Create a test which run the given Elisp script using the Emacs ERT testing # framework. # # The target is deduced from the ``FileName`` argument, e.g: the file foo.el # will be the target 'check-foo-el'. # # FIXME: assumes MELPA is configured... function(add_ert_test test_file) get_filename_component(name ${test_file} NAME_WE) add_test(check-${name}-el ${EMACS_EXECUTABLE} -Q --batch -l package --eval "(package-initialize)" --eval "(unless (require 'cl-lib nil t) (package-refresh-contents) (package-install 'cl-lib))" -l ${CMAKE_CURRENT_SOURCE_DIR}/${test_file} -f ert-run-tests-batch-and-exit) set_tests_properties(check-${name}-el PROPERTIES TIMEOUT 15) endfunction() add_ert_test(irony.el) add_ert_test(irony-iotask.el) add_ert_test(irony-cdb-json.el) irony-mode-1.2.0/server/test/elisp/irony-cdb-json.el000066400000000000000000000067141317254426700223770ustar00rootroot00000000000000;; -*-no-byte-compile: t; -*- (load (concat (file-name-directory (or load-file-name buffer-file-name)) "test-config")) (require 'irony-cdb-json) (require 'cl-lib) (defconst irony-cdb/compile-command '((file . "../src/file.cc") (directory . "/home/user/project/build") (command . "/usr/bin/clang++ -DSOMEDEF=1 -c -o file.o /home/user/project/src/file.cc"))) (ert-deftest cdb/parse/simple/path-is-absolute () (should (equal "/home/user/project/src/file.cc" (nth 0 (irony-cdb-json--transform-compile-command irony-cdb/compile-command))))) (ert-deftest cdb/parse/simple/compile-options () (should (equal '("-DSOMEDEF=1") (nth 1 (irony-cdb-json--transform-compile-command irony-cdb/compile-command))))) (ert-deftest cdb/parse/simple/invocation-directory () (should (equal "/home/user/project/build" (nth 2 (irony-cdb-json--transform-compile-command irony-cdb/compile-command))))) (ert-deftest cdb/choose-closest-path/chooses-closest () (should (equal "/tmp/a/cdb" (irony-cdb--choose-closest-path "/tmp/a/1" '("/tmp/a/cdb" "/tmp/cdb"))))) (ert-deftest cdb/choose-closest-path/chooses-closest2 () (should (equal "/tmp/a/cdb" (irony-cdb--choose-closest-path "/tmp/a/1" '("/tmp/cdb" "/tmp/a/cdb"))))) (ert-deftest cdb/choose-closest-path/prefers-deeper () (should (equal "/tmp/a/build/cdb" (irony-cdb--choose-closest-path "/tmp/a/1" '("/tmp/a/build/cdb" "/tmp/cdb"))))) (ert-deftest cdb/choose-closest-path/prefers-deeper2 () (should (equal "/tmp/a/build/cdb" (irony-cdb--choose-closest-path "/tmp/a/1" '("/tmp/cdb" "/tmp/a/build/cdb"))))) (ert-deftest cdb/choose-closest-path/will-survive-garbage () (should (equal nil (irony-cdb--choose-closest-path "/tmp/a/1" 'ordures)))) ; http://endlessparentheses.com/understanding-letf-and-how-it-replaces-flet.html (ert-deftest cdb/locate-db/choose-among-candidates () (should (equal "/foo/build/cdb" (cl-letf (((symbol-function 'locate-dominating-file) (lambda (file name) (cond ((string= name "./cdb") "/") ; found /cdb ((string= name "build/cdb") "/foo/") ; found /foo/build/cdb )))) (irony-cdb--locate-dominating-file-with-dirs "/foo/bar/qux.cpp" "cdb" '("." "build" "out/x86_64")))))) (ert-deftest cdb/locate-dominating-file-with-dirs/children-first () (should (equal "/tmp/foo/bar/out/x86_64/cdb" (cl-letf (((symbol-function 'locate-dominating-file) (lambda (file name) (cond ((string= name "./cdb") "/tmp/foo/") ; found /tmp/foo/cdb ((string= name "out/x86_64/cdb") "/tmp/foo/bar/") ;found /tmp/foo/bar/out/x86_64/cdb )))) (irony-cdb--locate-dominating-file-with-dirs "/tmp/foo/bar/qux.cpp" "cdb" '("." "out/x86_64" )))))) irony-mode-1.2.0/server/test/elisp/irony-iotask.el000066400000000000000000000241341317254426700221660ustar00rootroot00000000000000;; -*-no-byte-compile: t; -*- (load (concat (file-name-directory (or load-file-name buffer-file-name)) "test-config")) ;; load irony-iotask ;; ;; XXX: No idea why this is necessary, test-config already adds the directory to ;; the load-path so irony is found... (unless (require 'irony-iotask nil t) (let ((irony-iotask-dir (expand-file-name "../../.." test-dir))) (add-to-list 'load-path irony-iotask-dir) (require 'irony-iotask))) (defun irony-iotask-echo-process-exit-filter (process output) (when (buffer-live-p (process-buffer process)) (with-current-buffer (process-buffer process) (goto-char (process-mark process)) (insert output) (set-marker (process-mark process) (point)) (when (>= (buffer-size) (length "exit\n")) (should (string= (buffer-string) "exit\n")) (erase-buffer))))) ;; Note: these tests use process communication with the standard I/O streams. ;; The subprocess used for this communication is Emacs. ;; ;; The following article provides useful information for using Elisp as a ;; scripting language, Emacs as an interpreter, it details how the standard I/O ;; streams works in Elisp scripts: ;; - http://www.lunaryorn.com/2014/08/12/emacs-script-pitfalls.html (defmacro irony-iotask/with-echo-process (&rest body) "Start an Emacs process that runs the given PROCESS-SCRIPT. The process is setup with `irony-iotask-setup-process'. It's possible to schedule some iotasks in the BODY for testing. There is an exposed variable named `process' available for use in BODY. Elisp is used as a scripting language because it should be available on all OSes irony-iotask support." (declare (indent 1)) `(let ((process-connection-type nil) (process-adaptive-read-buffering nil) process) (setq process (start-process "emacs-irony-test" "*emacs-irony-test*" (expand-file-name invocation-name invocation-directory) "-Q" "--batch" "--eval" (prin1-to-string '(let ((msg)) (while (not (equal msg "exit")) (setq msg (read-from-minibuffer "")) (message msg)))))) (unwind-protect (progn (irony-iotask-setup-process process) ,@body) ;; the iotask process filter does not clean the process buffer ;; at the end of a request, but at the begining of a new one (with-current-buffer (process-buffer process) (erase-buffer)) (set-process-filter process #'irony-iotask-echo-process-exit-filter) (process-send-string process "exit\n") ;; wait for the process to finish normally, or kill it if it doesn't (with-timeout (1 (kill-process process)) (while (process-live-p process) (sit-for 0.05))) ;; start with a clean buffer, ;; Emacs 24.3 seems to keep some (kill-buffer (process-buffer process)) (delete-process process)))) ;; irony-iotask-result (ert-deftest irony-iotask-result/ready-p-value () (let ((result (irony-iotask-result-create))) (should-not (irony-iotask-result-valid-p result)) (irony-iotask-result-set-value result 1) (should (irony-iotask-result-valid-p result)))) (ert-deftest irony-iotask-result/ready-p-error () (let ((result (irony-iotask-result-create))) (should-not (irony-iotask-result-valid-p result)) (irony-iotask-result-set-error result 'irony-iotask-error (list "blah")) (should (irony-iotask-result-valid-p result)))) (ert-deftest irony-iotask-result/set-value () (let ((result (irony-iotask-result-create))) (irony-iotask-result-set-value result 'blah) (should (eq (irony-iotask-result-get result) 'blah)))) (irony--define-error 'irony-iotask-result/test-error "Irony I/O task sample error") (ert-deftest irony-iotask-result/set-error () (let ((result (irony-iotask-result-create))) (irony-iotask-result-set-error result 'irony-iotask-result/test-error) (should-error (irony-iotask-result-get result) :type 'irony-iotask-result/test-error))) (ert-deftest irony-iotask-result/set-error-data () (let ((result (irony-iotask-result-create))) (irony-iotask-result-set-error result 'irony-iotask-result/test-error 'foo 'bar 'baz 'qux) (condition-case err (irony-iotask-result-get result) (irony-iotask-result/test-error (should (equal (cdr err) '(foo bar baz qux))))))) (ert-deftest irony-iotask-result/get-empty () (let ((result (irony-iotask-result-create))) (should-error (irony-iotask-result-get result) :type 'irony-iotask-result-get-error))) ;; task (irony-iotask-define-task irony-iotask/task-start-t "doc" :start (lambda (&optional value) (irony-iotask-set-result (or value 42)))) (ert-deftest irony-iotask/task-start/simple () (let ((task (irony-iotask-package-task irony-iotask/task-start-t))) (irony-iotask/with-echo-process (should (equal 42 (irony-iotask-run process task)))))) (ert-deftest irony-iotask/task-start/with-arguments () (let ((task (irony-iotask-package-task irony-iotask/task-start-t 43))) (irony-iotask/with-echo-process (should (equal 43 (irony-iotask-run process task)))))) (irony-iotask-define-task irony-iotask/task-update-t "doc" :start (lambda (&optional hello) (irony-iotask-send-string (format "%s\n" (or hello "hello")))) :update (lambda (&optional hello) (setq hello (or hello "hello")) (when (string= (buffer-string) (format "%s\n" hello)) (irony-iotask-set-result (format "%s ok" hello))))) (ert-deftest irony-iotask-schedule/task-update/simple () (let ((task (irony-iotask-package-task irony-iotask/task-update-t))) (irony-iotask/with-echo-process (should (string= "hello ok" (irony-iotask-run process task)))))) (ert-deftest irony-iotask-schedule/task-update/with-arguments () (let ((task (irony-iotask-package-task irony-iotask/task-update-t "bonjour"))) (irony-iotask/with-echo-process (should (string= "bonjour ok" (irony-iotask-run process task)))))) (irony-iotask-define-task irony-iotask/task-invalid-msg-t "doc" :start (lambda () (irony-iotask-send-string "ping\n")) :update (lambda () (throw 'invalid-msg t))) (ert-deftest irony-iotask-schedule/task-update/invalid-msg () (let ((task (irony-iotask-package-task irony-iotask/task-invalid-msg-t))) (irony-iotask/with-echo-process (should-error (irony-iotask-run process task) :type 'irony-iotask-bad-data)))) (ert-deftest irony-iotask-chain/simple () (let ((task (irony-iotask-chain (irony-iotask-package-task irony-iotask/task-update-t "hi") (irony-iotask-package-task irony-iotask/task-update-t "hej")))) (irony-iotask/with-echo-process (should (equal "hej ok" (irony-iotask-run process task)))))) (defvar irony-iotask/task-finish-var nil) (defvar irony-iotask/task-on-var nil) (irony-iotask-define-task irony-iotask/task-finish-t "doc" :start (lambda () (irony-iotask-put :text "how") (irony-iotask-send-string "hello\n")) :update (lambda () (cond ((string= (buffer-string) "hello\n") (irony-iotask-put :text (concat (irony-iotask-get :text) " are")) (irony-iotask-set-result t)) ((>= (buffer-size) (1+ (length "hello\n"))) (throw 'invalid-msg t)))) :on-success (lambda () (setq irony-iotask/task-on-var "success")) :finish (lambda () (setq irony-iotask/task-finish-var (concat (irony-iotask-get :text) " you?")))) (ert-deftest irony-iotask-schedule/task-finish/simple () (let ((task (irony-iotask-package-task irony-iotask/task-finish-t))) (irony-iotask/with-echo-process (setq irony-iotask/task-finish-var nil) (irony-iotask-run process task) (should (equal "how are you?" irony-iotask/task-finish-var))))) (ert-deftest irony-iotask-schedule/task-on-success/simple () (let ((task (irony-iotask-package-task irony-iotask/task-finish-t))) (irony-iotask/with-echo-process (setq irony-iotask/task-on-var nil) (irony-iotask-run process task) (should (equal "success" irony-iotask/task-on-var))))) (irony-iotask-define-task irony-iotask/task-on-error-t "doc" :start (lambda () (irony-iotask-set-error 'irony-iotask-error)) :on-error (lambda () (setq irony-iotask/task-on-var "error"))) (ert-deftest irony-iotask-schedule/task-on-error/simple () (let ((task (irony-iotask-package-task irony-iotask/task-on-error-t))) (irony-iotask/with-echo-process (setq irony-iotask/task-on-var nil) (ignore-errors (irony-iotask-run process task)) (should (equal "error" irony-iotask/task-on-var))))) (ert-deftest irony-iotask-schedule/callback/recalls-schedule () (let ((task (irony-iotask-package-task irony-iotask/task-update-t "a"))) (irony-iotask/with-echo-process (lexical-let ((run-process process) results) (irony-iotask-schedule process task (lambda (result) (setq results (list result)) (irony-iotask-schedule run-process (irony-iotask-package-task irony-iotask/task-update-t "b") (lambda (result) (setq results (append results (list result))))))) (should (with-local-quit (while (< (length results) 2) (accept-process-output process 0.05)) t)) (should (string= "a ok" (irony-iotask-result-get (nth 0 results)))) (should (string= "b ok" (irony-iotask-result-get (nth 1 results)))))))) irony-mode-1.2.0/server/test/elisp/irony.el000066400000000000000000000110651317254426700206750ustar00rootroot00000000000000;; -*-no-byte-compile: t; -*- (load (concat (file-name-directory (or load-file-name buffer-file-name)) "test-config")) (ert-deftest irony/buffer-size-in-bytes () (with-temp-buffer ;; this smiley takes 3 bytes apparently (insert "☺") (should (equal 3 (irony--buffer-size-in-bytes))) (erase-buffer) (insert "☺\n") (should (equal 4 (irony--buffer-size-in-bytes))) (erase-buffer) (insert "\t") (should (equal 1 (irony--buffer-size-in-bytes))))) (ert-deftest irony/find-server-executable/does-not-exists () (let ((irony-server-install-prefix "/does/not/exists")) (should-error (irony--find-server-executable) :type 'irony-server-error))) (ert-deftest irony/split-command-line/just-spaces () (let ((cmd-line "clang -Wall -Wextra")) (should (equal '("clang" "-Wall" "-Wextra") (irony--split-command-line cmd-line))))) (ert-deftest irony/split-command-line/start-with-space () (let ((cmd-line " clang -Wall -Wextra")) (should (equal '("clang" "-Wall" "-Wextra") (irony--split-command-line cmd-line))))) (ert-deftest irony/split-command-line/end-with-space () (let ((cmd-line "clang -Wall -Wextra ")) (should (equal '("clang" "-Wall" "-Wextra") (irony--split-command-line cmd-line))))) (ert-deftest irony/split-command-line/space-everywhere () (let ((cmd-line " \t clang \t -Wall \t -Wextra\t")) (should (equal '("clang" "-Wall" "-Wextra") (irony--split-command-line cmd-line))))) (ert-deftest irony/split-command-line/with-quotes () (let ((cmd-line "clang -Wall -Wextra \"-I/tmp/dir with spaces\"")) (should (equal '("clang" "-Wall" "-Wextra" "-I/tmp/dir with spaces") (irony--split-command-line cmd-line))))) (ert-deftest irony/split-command-line/with-quotes () "Test if files are removed from the arguments list. https://github.com/Sarcasm/irony-mode/issues/101" (let ((cmd-line "g++ -DFOO=\\\"\\\"")) (should (equal '("g++" "-DFOO=\"\"") (irony--split-command-line cmd-line))))) (ert-deftest irony/split-command-line/start-with-quotes () (let ((cmd-line "\"cl ang\" -Wall -Wextra \"-I/tmp/dir with spaces\"")) (should (equal '("cl ang" "-Wall" "-Wextra" "-I/tmp/dir with spaces") (irony--split-command-line cmd-line))))) (ert-deftest irony/split-command-line/quotes-in-word () (let ((cmd-line "clang -Wall -Wextra -I\"/tmp/dir with spaces\"")) (should (equal '("clang" "-Wall" "-Wextra" "-I/tmp/dir with spaces") (irony--split-command-line cmd-line))))) (ert-deftest irony/split-command-line/ill-end-quote () (let ((cmd-line "clang -Wall -Wextra\"")) (should-error (irony--split-command-line cmd-line) :type 'irony-parse-error))) (ert-deftest irony/split-command-line/backslash-1 () (let ((cmd-line "clang\\ -Wall")) (should (equal '("clang -Wall") (irony--split-command-line cmd-line))))) (ert-deftest irony/split-command-line/backslash-2 () (let ((cmd-line "\\\\\\ clang\\ -Wall\\")) (should (equal '("\\ clang -Wall\\") (irony--split-command-line cmd-line))))) (ert-deftest irony/extract-working-directory-option/not-specified () (let ((compile-flags '("-Wall"))) (should (not (irony--extract-working-directory-option compile-flags))))) (ert-deftest irony/extract-working-directory-option/specified-1 () (let ((compile-flags '("-working-directory" "/tmp/lol"))) (should (equal "/tmp/lol" (irony--extract-working-directory-option compile-flags))))) (ert-deftest irony/extract-working-directory-option/specified-2 () (let ((compile-flags '("-Wall" "-working-directory=/tmp/lol" "-Wshadow"))) (should (equal "/tmp/lol" (irony--extract-working-directory-option compile-flags))))) ;; TODO: restore functionality ;; (ert-deftest irony/include-directories-1 () ;; (let ((irony-compile-flags '("-Iinclude" "-I/tmp/foo")) ;; (irony-compile-flags-work-dir "/tmp/blah/")) ;; (should (equal ;; '("/tmp/blah/include" "/tmp/foo") ;; (irony-user-search-paths))))) ;; (ert-deftest irony/include-directories-2 () ;; (let ((irony-compile-flags '("-Wextra" "-Iinclude" "-I" "foo" "-Wall")) ;; (irony-compile-flags-work-dir "/tmp/blah/")) ;; (should (equal ;; '("/tmp/blah/include" ;; "/tmp/blah/foo") ;; (irony-user-search-paths))))) irony-mode-1.2.0/server/test/elisp/test-config.el000066400000000000000000000007211317254426700217540ustar00rootroot00000000000000;; -*-no-byte-compile: t; -*- (defvar test-dir (if load-file-name (file-name-as-directory (expand-file-name (concat (file-name-directory load-file-name))))) "Elisp test directory path.") ;; load irony (unless (require 'irony nil t) (let ((irony-dir (expand-file-name "../../.." test-dir))) (add-to-list 'load-path irony-dir) (require 'irony))) (require 'ert)