micro-2.0.14/0000775000175000017510000000000014663411671012251 5ustar nileshnileshmicro-2.0.14/.editorconfig0000664000175000017510000000023014663411671014721 0ustar nileshnilesh# See http://editorconfig.org # In Go files we indent with tabs but still # set indent_size to control the GitHub web viewer. [*.go] indent_size=4 micro-2.0.14/.gitignore0000664000175000017510000000030714663411671014241 0ustar nileshnilesh.DS_Store micro !cmd/micro binaries/ tmp.sh test/ .idea/ packages/ todo.txt test.txt log.txt *.old benchmark_results* tools/build-version tools/build-date tools/info-plist tools/vscode-tests/ *.hdr micro-2.0.14/LICENSE0000664000175000017510000000207614663411671013263 0ustar nileshnileshMIT License Copyright (c) 2016-2020: Zachary Yedidia, et al. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 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 AUTHORS 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 IN THE SOFTWARE. micro-2.0.14/LICENSE-THIRD-PARTY0000664000175000017510000017524114663411671015035 0ustar nileshnileshThird party libraries directly used by micro and their licenses ================ github.com/golang/go/LICENSE ================ Copyright (c) 2009 The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. github.com/blang/semver/LICENSE ================ The MIT License Copyright (c) 2014 Benedikt Lang Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 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 AUTHORS 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 IN THE SOFTWARE. github.com/gdamore/encoding/LICENSE ================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. github.com/go-errors/errors/LICENSE.MIT ================ Copyright (c) 2015 Conrad Irwin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 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 AUTHORS 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 IN THE SOFTWARE. github.com/mattn/go-isatty/LICENSE ================ Copyright (c) Yasuhiro MATSUMOTO MIT License (Expat) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 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 AUTHORS 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 IN THE SOFTWARE. github.com/mattn/go-runewidth/LICENSE ================ The MIT License (MIT) Copyright (c) 2016 Yasuhiro Matsumoto Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 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 AUTHORS 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 IN THE SOFTWARE. github.com/mitchellh/go-homedir/LICENSE ================ The MIT License (MIT) Copyright (c) 2013 Mitchell Hashimoto Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 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 AUTHORS 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 IN THE SOFTWARE. github.com/sergi/go-diff/LICENSE ================ Copyright (c) 2012-2016 The go-diff Authors. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 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 AUTHORS 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 IN THE SOFTWARE. github.com/yuin/gopher-lua/LICENSE ================ The MIT License (MIT) Copyright (c) 2015 Yusuke Inuzuka Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 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 AUTHORS 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 IN THE SOFTWARE. github.com/atotto/clipboard/LICENSE ================ github.com/zyedidia/clipboard/LICENSE (fork) ================ Copyright (c) 2013 Ato Araki. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of @atotto. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. github.com/gdamore/tcell/LICENSE ================ github.com/zyedidia/tcell/LICENSE (fork) ================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. golang.org/x/text/LICENSE ================ Copyright (c) 2009 The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. layeh.com/gopher-luar/LICENSE ================ Mozilla Public License Version 2.0 ================================== 1. Definitions -------------- 1.1. "Contributor" means each individual or legal entity that creates, contributes to the creation of, or owns Covered Software. 1.2. "Contributor Version" means the combination of the Contributions of others (if any) used by a Contributor and that particular Contributor's Contribution. 1.3. "Contribution" means Covered Software of a particular Contributor. 1.4. "Covered Software" means Source Code Form to which the initial Contributor has attached the notice in Exhibit A, the Executable Form of such Source Code Form, and Modifications of such Source Code Form, in each case including portions thereof. 1.5. "Incompatible With Secondary Licenses" means (a) that the initial Contributor has attached the notice described in Exhibit B to the Covered Software; or (b) that the Covered Software was made available under the terms of version 1.1 or earlier of the License, but not also under the terms of a Secondary License. 1.6. "Executable Form" means any form of the work other than Source Code Form. 1.7. "Larger Work" means a work that combines Covered Software with other material, in a separate file or files, that is not Covered Software. 1.8. "License" means this document. 1.9. "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently, any and all of the rights conveyed by this License. 1.10. "Modifications" means any of the following: (a) any file in Source Code Form that results from an addition to, deletion from, or modification of the contents of Covered Software; or (b) any new file in Source Code Form that contains any Covered Software. 1.11. "Patent Claims" of a Contributor means any patent claim(s), including without limitation, method, process, and apparatus claims, in any patent Licensable by such Contributor that would be infringed, but for the grant of the License, by the making, using, selling, offering for sale, having made, import, or transfer of either its Contributions or its Contributor Version. 1.12. "Secondary License" means either the GNU General Public License, Version 2.0, the GNU Lesser General Public License, Version 2.1, the GNU Affero General Public License, Version 3.0, or any later versions of those licenses. 1.13. "Source Code Form" means the form of the work preferred for making modifications. 1.14. "You" (or "Your") means an individual or a legal entity exercising rights under this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity. 2. License Grants and Conditions -------------------------------- 2.1. Grants Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license: (a) under intellectual property rights (other than patent or trademark) Licensable by such Contributor to use, reproduce, make available, modify, display, perform, distribute, and otherwise exploit its Contributions, either on an unmodified basis, with Modifications, or as part of a Larger Work; and (b) under Patent Claims of such Contributor to make, use, sell, offer for sale, have made, import, and otherwise transfer either its Contributions or its Contributor Version. 2.2. Effective Date The licenses granted in Section 2.1 with respect to any Contribution become effective for each Contribution on the date the Contributor first distributes such Contribution. 2.3. Limitations on Grant Scope The licenses granted in this Section 2 are the only rights granted under this License. No additional rights or licenses will be implied from the distribution or licensing of Covered Software under this License. Notwithstanding Section 2.1(b) above, no patent license is granted by a Contributor: (a) for any code that a Contributor has removed from Covered Software; or (b) for infringements caused by: (i) Your and any other third party's modifications of Covered Software, or (ii) the combination of its Contributions with other software (except as part of its Contributor Version); or (c) under Patent Claims infringed by Covered Software in the absence of its Contributions. This License does not grant any rights in the trademarks, service marks, or logos of any Contributor (except as may be necessary to comply with the notice requirements in Section 3.4). 2.4. Subsequent Licenses No Contributor makes additional grants as a result of Your choice to distribute the Covered Software under a subsequent version of this License (see Section 10.2) or under the terms of a Secondary License (if permitted under the terms of Section 3.3). 2.5. Representation Each Contributor represents that the Contributor believes its Contributions are its original creation(s) or it has sufficient rights to grant the rights to its Contributions conveyed by this License. 2.6. Fair Use This License is not intended to limit any rights You have under applicable copyright doctrines of fair use, fair dealing, or other equivalents. 2.7. Conditions Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in Section 2.1. 3. Responsibilities ------------------- 3.1. Distribution of Source Form All distribution of Covered Software in Source Code Form, including any Modifications that You create or to which You contribute, must be under the terms of this License. You must inform recipients that the Source Code Form of the Covered Software is governed by the terms of this License, and how they can obtain a copy of this License. You may not attempt to alter or restrict the recipients' rights in the Source Code Form. 3.2. Distribution of Executable Form If You distribute Covered Software in Executable Form then: (a) such Covered Software must also be made available in Source Code Form, as described in Section 3.1, and You must inform recipients of the Executable Form how they can obtain a copy of such Source Code Form by reasonable means in a timely manner, at a charge no more than the cost of distribution to the recipient; and (b) You may distribute such Executable Form under the terms of this License, or sublicense it under different terms, provided that the license for the Executable Form does not attempt to limit or alter the recipients' rights in the Source Code Form under this License. 3.3. Distribution of a Larger Work You may create and distribute a Larger Work under terms of Your choice, provided that You also comply with the requirements of this License for the Covered Software. If the Larger Work is a combination of Covered Software with a work governed by one or more Secondary Licenses, and the Covered Software is not Incompatible With Secondary Licenses, this License permits You to additionally distribute such Covered Software under the terms of such Secondary License(s), so that the recipient of the Larger Work may, at their option, further distribute the Covered Software under the terms of either this License or such Secondary License(s). 3.4. Notices You may not remove or alter the substance of any license notices (including copyright notices, patent notices, disclaimers of warranty, or limitations of liability) contained within the Source Code Form of the Covered Software, except that You may alter any license notices to the extent required to remedy known factual inaccuracies. 3.5. Application of Additional Terms You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, You may do so only on Your own behalf, and not on behalf of any Contributor. You must make it absolutely clear that any such warranty, support, indemnity, or liability obligation is offered by You alone, and You hereby agree to indemnify every Contributor for any liability incurred by such Contributor as a result of warranty, support, indemnity or liability terms You offer. You may include additional disclaimers of warranty and limitations of liability specific to any jurisdiction. 4. Inability to Comply Due to Statute or Regulation --------------------------------------------------- If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Software due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be placed in a text file included with all distributions of the Covered Software under this License. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it. 5. Termination -------------- 5.1. The rights granted under this License will terminate automatically if You fail to comply with any of its terms. However, if You become compliant, then the rights granted under this License from a particular Contributor are reinstated (a) provisionally, unless and until such Contributor explicitly and finally terminates Your grants, and (b) on an ongoing basis, if such Contributor fails to notify You of the non-compliance by some reasonable means prior to 60 days after You have come back into compliance. Moreover, Your grants from a particular Contributor are reinstated on an ongoing basis if such Contributor notifies You of the non-compliance by some reasonable means, this is the first time You have received notice of non-compliance with this License from such Contributor, and You become compliant prior to 30 days after Your receipt of the notice. 5.2. If You initiate litigation against any entity by asserting a patent infringement claim (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a Contributor Version directly or indirectly infringes any patent, then the rights granted to You by any and all Contributors for the Covered Software under Section 2.1 of this License shall terminate. 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or Your distributors under this License prior to termination shall survive termination. ************************************************************************ * * * 6. Disclaimer of Warranty * * ------------------------- * * * * Covered Software is provided under this License on an "as is" * * basis, without warranty of any kind, either expressed, implied, or * * statutory, including, without limitation, warranties that the * * Covered Software is free of defects, merchantable, fit for a * * particular purpose or non-infringing. The entire risk as to the * * quality and performance of the Covered Software is with You. * * Should any Covered Software prove defective in any respect, You * * (not any Contributor) assume the cost of any necessary servicing, * * repair, or correction. This disclaimer of warranty constitutes an * * essential part of this License. No use of any Covered Software is * * authorized under this License except under this disclaimer. * * * ************************************************************************ ************************************************************************ * * * 7. Limitation of Liability * * -------------------------- * * * * Under no circumstances and under no legal theory, whether tort * * (including negligence), contract, or otherwise, shall any * * Contributor, or anyone who distributes Covered Software as * * permitted above, be liable to You for any direct, indirect, * * special, incidental, or consequential damages of any character * * including, without limitation, damages for lost profits, loss of * * goodwill, work stoppage, computer failure or malfunction, or any * * and all other commercial damages or losses, even if such party * * shall have been informed of the possibility of such damages. This * * limitation of liability shall not apply to liability for death or * * personal injury resulting from such party's negligence to the * * extent applicable law prohibits such limitation. Some * * jurisdictions do not allow the exclusion or limitation of * * incidental or consequential damages, so this exclusion and * * limitation may not apply to You. * * * ************************************************************************ 8. Litigation ------------- Any litigation relating to this License may be brought only in the courts of a jurisdiction where the defendant maintains its principal place of business and such litigation shall be governed by laws of that jurisdiction, without reference to its conflict-of-law provisions. Nothing in this Section shall prevent a party's ability to bring cross-claims or counter-claims. 9. Miscellaneous ---------------- This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not be used to construe this License against a Contributor. 10. Versions of the License --------------------------- 10.1. New Versions Mozilla Foundation is the license steward. Except as provided in Section 10.3, no one other than the license steward has the right to modify or publish new versions of this License. Each version will be given a distinguishing version number. 10.2. Effect of New Versions You may distribute the Covered Software under the terms of the version of the License under which You originally received the Covered Software, or under the terms of any subsequent version published by the license steward. 10.3. Modified Versions If you create software not governed by this License, and you want to create a new license for such software, you may create and use a modified version of this License if you rename the license and remove any references to the name of the license steward (except to note that such modified license differs from this License). 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses If You choose to distribute Source Code Form that is Incompatible With Secondary Licenses under the terms of this version of the License, the notice described in Exhibit B of this License must be attached. Exhibit A - Source Code Form License Notice ------------------------------------------- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice. You may add additional accurate notices of copyright ownership. Exhibit B - "Incompatible With Secondary Licenses" Notice --------------------------------------------------------- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. github.com/flynn/json5/LICENSE ================ github.com/zyedidia/json5/LICENSE (fork) ================ Decoder code based on package encoding/json from the Go language. Copyright (c) 2012 The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Test data based on the parse cases from https://github.com/json5/json5 Copyright (c) 2012-2016 Aseem Kishore, and others. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 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 AUTHORS 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 IN THE SOFTWARE. github.com/james4k/terminal/LICENSE ================ github.com/zyedidia/terminal/LICENSE (fork) ================ Copyright (C) 2013 James Gray Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without liitation 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: The above copyright notice and thismssion notice shall be included in all copies or substantial portions of the Software. 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 AUTHORS 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 IN THE SOFTWARE. github.com/kr/pty/License ================ github.com/zyedidia/pty/License (fork) ================ Copyright (c) 2011 Keith Rarick Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 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 AUTHORS 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 IN THE SOFTWARE. github.com/npat-efault/poller/LICENSE.txt ================ github.com/zyedidia/poller/LICENSE.txt (fork) ================ Copyright (c) 2014, Nick Patavalis (npat@efault.net) All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. github.com/zyedidia/glob ================ Glob is licensed under the MIT "Expat" License: Copyright (c) 2016: Zachary Yedidia. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 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 AUTHORS 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 IN THE SOFTWARE. github.com/dustin/go-humanize/LICENSE ================ Copyright (c) 2005-2008 Dustin Sallings Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 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 AUTHORS 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 IN THE SOFTWARE. gopkg.in/yaml.v2/LICENSE ================ Copyright 2011-2016 Canonical Ltd. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. github.com/kballard/go-shellquote/LICENSE =============== Copyright (C) 2014 Kevin Ballard Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 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 AUTHORS 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 IN THE SOFTWARE. github.com/stretchr/testify ================= MIT License Copyright (c) 2012-2018 Mat Ryer and Tyler Bunnell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 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 AUTHORS 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 IN THE SOFTWARE. micro-2.0.14/Makefile0000664000175000017510000000460614663411671013717 0ustar nileshnilesh.PHONY: runtime build generate build-quick VERSION = $(shell GOOS=$(shell go env GOHOSTOS) GOARCH=$(shell go env GOHOSTARCH) \ go run tools/build-version.go) HASH = $(shell git rev-parse --short HEAD) DATE = $(shell GOOS=$(shell go env GOHOSTOS) GOARCH=$(shell go env GOHOSTARCH) \ go run tools/build-date.go) ADDITIONAL_GO_LINKER_FLAGS = $(shell GOOS=$(shell go env GOHOSTOS) \ GOARCH=$(shell go env GOHOSTARCH) \ go run tools/info-plist.go "$(shell go env GOOS)" "$(VERSION)") GOBIN ?= $(shell go env GOPATH)/bin GOVARS = -X github.com/zyedidia/micro/v2/internal/util.Version=$(VERSION) -X github.com/zyedidia/micro/v2/internal/util.CommitHash=$(HASH) -X 'github.com/zyedidia/micro/v2/internal/util.CompileDate=$(DATE)' DEBUGVAR = -X github.com/zyedidia/micro/v2/internal/util.Debug=ON VSCODE_TESTS_BASE_URL = 'https://raw.githubusercontent.com/microsoft/vscode/e6a45f4242ebddb7aa9a229f85555e8a3bd987e2/src/vs/editor/test/common/model/' build: generate build-quick build-quick: go build -trimpath -ldflags "-s -w $(GOVARS) $(ADDITIONAL_GO_LINKER_FLAGS)" ./cmd/micro build-dbg: go build -trimpath -ldflags "-s -w $(ADDITIONAL_GO_LINKER_FLAGS) $(DEBUGVAR)" ./cmd/micro build-tags: fetch-tags generate go build -trimpath -ldflags "-s -w $(GOVARS) $(ADDITIONAL_GO_LINKER_FLAGS)" ./cmd/micro build-all: build install: generate go install -ldflags "-s -w $(GOVARS) $(ADDITIONAL_GO_LINKER_FLAGS)" ./cmd/micro install-all: install fetch-tags: git fetch --tags generate: GOOS=$(shell go env GOHOSTOS) GOARCH=$(shell go env GOHOSTARCH) go generate ./runtime testgen: mkdir -p tools/vscode-tests cd tools/vscode-tests && \ curl --remote-name-all $(VSCODE_TESTS_BASE_URL){editableTextModelAuto,editableTextModel,model.line}.test.ts tsc tools/vscode-tests/*.ts > /dev/null; true go run tools/testgen.go tools/vscode-tests/*.js > buffer_generated_test.go mv buffer_generated_test.go internal/buffer gofmt -w internal/buffer/buffer_generated_test.go test: go test ./internal/... go test ./cmd/... bench: for i in 1 2 3; do \ go test -bench=. ./internal/...; \ done > benchmark_results benchstat benchmark_results bench-baseline: for i in 1 2 3; do \ go test -bench=. ./internal/...; \ done > benchmark_results_baseline bench-compare: for i in 1 2 3; do \ go test -bench=. ./internal/...; \ done > benchmark_results benchstat -alpha 0.15 benchmark_results_baseline benchmark_results clean: rm -f micro micro-2.0.14/README.md0000664000175000017510000003334314663411671013536 0ustar nileshnileshmicro logo ![Test Workflow](https://github.com/zyedidia/micro/actions/workflows/test.yaml/badge.svg) [![Go Report Card](https://goreportcard.com/badge/github.com/zyedidia/micro)](https://goreportcard.com/report/github.com/zyedidia/micro) [![Release](https://img.shields.io/github/release/zyedidia/micro.svg?label=Release)](https://github.com/zyedidia/micro/releases) [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/zyedidia/micro/blob/master/LICENSE) [![Join the chat at https://gitter.im/zyedidia/micro](https://badges.gitter.im/zyedidia/micro.svg)](https://gitter.im/zyedidia/micro?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Snap Status](https://snapcraft.io/micro/badge.svg)](https://snapcraft.io/micro) **micro** is a terminal-based text editor that aims to be easy to use and intuitive, while also taking advantage of the capabilities of modern terminals. It comes as a single, batteries-included, static binary with no dependencies; you can download and use it right now! As its name indicates, micro aims to be somewhat of a successor to the nano editor by being easy to install and use. It strives to be enjoyable as a full-time editor for people who prefer to work in a terminal, or those who regularly edit files over SSH. Here is a picture of micro editing its source code. ![Screenshot](./assets/micro-solarized.png) To see more screenshots of micro, showcasing some of the default color schemes, see [here](https://micro-editor.github.io). You can also check out the website for Micro at https://micro-editor.github.io. ## Table of Contents - [Features](#features) - [Installation](#installation) - [Prebuilt binaries](#pre-built-binaries) - [Package Managers](#package-managers) - [Building from source](#building-from-source) - [Fully static binary](#fully-static-binary) - [macOS terminal](#macos-terminal) - [Linux clipboard support](#linux-clipboard-support) - [Colors and syntax highlighting](#colors-and-syntax-highlighting) - [Cygwin, Mingw, Plan9](#cygwin-mingw-plan9) - [Usage](#usage) - [Documentation and Help](#documentation-and-help) - [Contributing](#contributing) - - - ## Features - Easy to use and install. - No dependencies or external files are needed — just the binary you can download further down the page. - Multiple cursors. - Common keybindings (Ctrl-s, Ctrl-c, Ctrl-v, Ctrl-z, …). - Keybindings can be rebound to your liking. - Sane defaults. - You shouldn't have to configure much out of the box (and it is extremely easy to configure). - Splits and tabs. - nano-like menu to help you remember the keybindings. - Extremely good mouse support. - This means mouse dragging to create a selection, double click to select by word, and triple click to select by line. - Cross-platform (it should work on all the platforms Go runs on). - Note that while Windows is supported, Mingw/Cygwin is not (see below). - Plugin system (plugins are written in Lua). - micro has a built-in plugin manager to automatically install, remove, and update plugins. - Built-in diff gutter. - Simple autocompletion. - Persistent undo. - Automatic linting and error notifications. - Syntax highlighting for over [130 languages](runtime/syntax). - Color scheme support. - By default, micro comes with 16, 256, and true color themes. - True color support (set the `MICRO_TRUECOLOR` environment variable to 1 to enable it). - Copy and paste with the system clipboard. - Small and simple. - Easily configurable. - Macros. - Smart highlighting of trailing whitespace and tab vs space errors. - Common editor features such as undo/redo, line numbers, Unicode support, soft wrapping, … ## Installation To install micro, you can download a [prebuilt binary](https://github.com/zyedidia/micro/releases), or you can build it from source. If you want more information about ways to install micro, see this [wiki page](https://github.com/zyedidia/micro/wiki/Installing-Micro). Use `micro -version` to get the version information after installing. It is only guaranteed that you are installing the most recent stable version if you install from the prebuilt binaries, Homebrew, or Snap. A desktop entry file and man page can be found in the [assets/packaging](https://github.com/zyedidia/micro/tree/master/assets/packaging) directory. ### Pre-built binaries Pre-built binaries are distributed in [releases](https://github.com/zyedidia/micro/releases). To uninstall micro, simply remove the binary, and the configuration directory at `~/.config/micro`. #### Quick-install script ```bash curl https://getmic.ro | bash ``` The script will place the micro binary in the current directory. From there, you can move it to a directory on your path of your choosing (e.g. `sudo mv micro /usr/bin`). See its [GitHub repository](https://github.com/benweissmann/getmic.ro) for more information. #### Eget With [Eget](https://github.com/zyedidia/eget) installed, you can easily get a pre-built binary: ``` eget zyedidia/micro ``` Use `--tag VERSION` to download a specific tagged version. ``` eget --tag nightly zyedidia/micro # download the nightly version (compiled every day at midnight UTC) eget --tag v2.0.8 zyedidia/micro # download version 2.0.8 rather than the latest release ``` You can install `micro` by adding `--to /usr/local/bin` to the `eget` command, or move the binary manually to a directory on your `$PATH` after the download completes. See [Eget](https://github.com/zyedidia/eget) for more information. ### Package managers You can install micro using Homebrew on Mac: ``` brew install micro ``` **Note for Mac:** All micro keybindings use the control or alt (option) key, not the command key. By default, macOS terminals do not forward alt key events. To fix this, please see the section on [macOS terminals](https://github.com/zyedidia/micro#macos-terminal) further below. On Linux, you can install micro through [snap](https://snapcraft.io/docs/core/install) ``` snap install micro --classic ``` Micro is also available through other package managers on Linux such as dnf, AUR, Nix, and package managers for other operating systems. These packages are not guaranteed to be up-to-date. * Linux: * distro-specific package managers: * `dnf install micro` (Fedora). * `apt install micro` (Ubuntu and Debian). * `pacman -S micro` (Arch Linux). * `emerge app-editors/micro` (Gentoo). * `zypper install micro-editor` (SUSE) * `eopkg install micro` (Solus). * `pacstall -I micro` (Pacstall). * `apt-get install micro` (ALT Linux) * See [wiki](https://github.com/zyedidia/micro/wiki/Installing-Micro) for details about CRUX, Termux. * distro-agnostic package managers: * `nix profile install nixpkgs#micro` (with [Nix](https://nixos.org/) and flakes enabled) * `flox install micro` (with [Flox](https://flox.dev)) * Windows: [Chocolatey](https://chocolatey.org), [Scoop](https://scoop.sh/) and [WinGet](https://learn.microsoft.com/en-us/windows/package-manager/winget/). * `choco install micro`. * `scoop install micro`. * `winget install zyedidia.micro` * OpenBSD: Available in the ports tree and also available as a binary package. * `pkg_add -v micro`. * NetBSD, macOS, Linux, Illumos, etc. with [pkgsrc](http://www.pkgsrc.org/)-current: * `pkg_add micro` * macOS: Available in package managers. * `sudo port install micro` (with [MacPorts](https://www.macports.org)) * `brew install micro` (with [Homebrew](https://brew.sh/)) * `nix profile install nixpkgs#micro` (with [Nix](https://nixos.org/) and flakes enabled) * `flox install micro` (with [Flox](https://flox.dev)) **Note for Linux desktop environments:** For interfacing with the local system clipboard, the following tools need to be installed: * For X11, `xclip` or `xsel` * For [Wayland](https://wayland.freedesktop.org/), `wl-clipboard` Without these tools installed, micro will use an internal clipboard for copy and paste, but it won't be accessible to external applications. ### Building from source If your operating system does not have a binary release, but does run Go, you can build from source. Make sure that you have Go version 1.16 or greater and Go modules are enabled. ``` git clone https://github.com/zyedidia/micro cd micro make build sudo mv micro /usr/local/bin # optional ``` The binary will be placed in the current directory and can be moved to anywhere you like (for example `/usr/local/bin`). The command `make install` will install the binary to `$GOPATH/bin` or `$GOBIN`. You can install directly with `go get` (`go get github.com/zyedidia/micro/cmd/micro`) but this isn't recommended because it doesn't build micro with version information (necessary for the plugin manager), and doesn't disable debug mode. ### Fully static binary By default, the micro binary will dynamically link with core system libraries (this is generally recommended for security and portability). However, there is a fully static prebuilt binary that is provided for amd64 as `linux-static.tar.gz`, and to build a fully static binary from source, run ``` CGO_ENABLED=0 make build ``` ### macOS terminal If you are using macOS, you should consider using [iTerm2](http://iterm2.com/) instead of the default terminal (Terminal.app). The iTerm2 terminal has much better mouse support as well as better handling of key events. For best keybinding behavior, choose `xterm defaults` under `Preferences->Profiles->Keys->Presets...`, and select `Esc+` for `Left Option Key` in the same menu. The newest versions also support true color. If you still insist on using the default Mac terminal, be sure to set `Use Option key as Meta key` under `Preferences->Profiles->Keyboard` to use option as alt. ### WSL and Windows Console If you use micro within WSL, it is highly recommended that you use the [Windows Terminal](https://apps.microsoft.com/store/detail/windows-terminal/9N0DX20HK701?hl=en-us&gl=us) instead of the default Windows Console. If you must use Windows Console for some reason, note that there is a bug in Windows Console WSL that causes a font change whenever micro tries to access the external clipboard via powershell. To fix this, use an internal clipboard with `set clipboard internal` (though your system clipboard will no longer be available in micro). ### Colors and syntax highlighting If you open micro and it doesn't seem like syntax highlighting is working, this is probably because you are using a terminal which does not support 256 color mode. Try changing the color scheme to `simple` by pressing Ctrl-e in micro and typing `set colorscheme simple`. If you are using the default Ubuntu terminal, to enable 256 color mode make sure your `TERM` variable is set to `xterm-256color`. Many of the Windows terminals don't support more than 16 colors, which means that micro's default color scheme won't look very good. You can either set the color scheme to `simple`, or download and configure a better terminal emulator than the Windows default. ### Cygwin, Mingw, Plan9 Cygwin, Mingw, and Plan9 are unfortunately not officially supported. In Cygwin and Mingw, micro will often work when run using the `winpty` utility: ``` winpty micro.exe ... ``` Micro uses the amazing [tcell library](https://github.com/gdamore/tcell), but this means that micro is restricted to the platforms tcell supports. As a result, micro does not support Plan9 or Cygwin (although this may change in the future). Micro also doesn't support NaCl (which is deprecated anyway). ## Usage Once you have built the editor, start it by running `micro path/to/file.txt` or `micro` to open an empty buffer. micro also supports creating buffers from `stdin`: ```sh ip a | micro ``` You can move the cursor around with the arrow keys and mouse. You can also use the mouse to manipulate the text. Simply clicking and dragging will select text. You can also double click to enable word selection, and triple click to enable line selection. ## Documentation and Help micro has a built-in help system which you can access by pressing Ctrl-e and typing `help`. Additionally, you can view the help files here: - [main help](https://github.com/zyedidia/micro/tree/master/runtime/help/help.md) - [keybindings](https://github.com/zyedidia/micro/tree/master/runtime/help/keybindings.md) - [commands](https://github.com/zyedidia/micro/tree/master/runtime/help/commands.md) - [colors](https://github.com/zyedidia/micro/tree/master/runtime/help/colors.md) - [options](https://github.com/zyedidia/micro/tree/master/runtime/help/options.md) - [plugins](https://github.com/zyedidia/micro/tree/master/runtime/help/plugins.md) I also recommend reading the [tutorial](https://github.com/zyedidia/micro/tree/master/runtime/help/tutorial.md) for a brief introduction to the more powerful configuration features micro offers. There is also an unofficial Discord, which you can join at https://discord.gg/nhWR6armnR. ## Contributing If you find any bugs, please report them! I am also happy to accept pull requests from anyone. You can use the [GitHub issue tracker](https://github.com/zyedidia/micro/issues) to report bugs, ask questions, or suggest new features. For a more informal setting to discuss the editor, you can join the [Gitter chat](https://gitter.im/zyedidia/micro) or the [Discord](https://discord.gg/nhWR6armnR). You can also use the [Discussions](https://github.com/zyedidia/micro/discussions) section on Github for a forum-like setting or for Q&A. Sometimes I am unresponsive, and I apologize! If that happens, please ping me. micro-2.0.14/assets/0000775000175000017510000000000014663411671013553 5ustar nileshnileshmicro-2.0.14/assets/micro-logo-drop.svg0000664000175000017510000003677414663411671017326 0ustar nileshnilesh image/svg+xml micro-2.0.14/assets/micro-logo-mark.svg0000664000175000017510000001430314663411671017274 0ustar nileshnilesh image/svg+xml micro-2.0.14/assets/micro-logo.svg0000664000175000017510000002715614663411671016356 0ustar nileshnilesh image/svg+xml micro-2.0.14/assets/micro-solarized.png0000664000175000017510000077122314663411671017400 0ustar nileshnilesh‰PNG  IHDRè ߇PLTE(3‚”–Vnuƒ”•Xnu*¡˜(35Aƒ”–???(5Xnrƒ”’(9(45B(3…™”–+5///*IPmu*3+3+Q)=ƒ“‚Œq2Z<[#,3 )3Xmhi’–ku& ˜UJ6-r*.BF;YN7€€eGUQ8As“–<4B<4#iŒ*`sQmOw ž˜Wkdl^EIbj2[l.ERfkDLUfb/*:?Rw„UL;z<0%——D$«ED#„¸#ˆ<†•'vT k„)’ohbt~ „IMZ‚qp ƒHo'~[#fa•qÌbng[A}†¬i!%‹yu•grj5<±|NŠqe&O7z [®%„:|XscX)G–]~††eajX|~.T>R%G\/{L…·…† IDATxÚìÏOÛLÇ#Ë »‰²„.Z…"Qé@¨D¢¨RDQl.üz¹rA‚ •P‡JEꤊüèÛK‘8 ^*ñöÄ­«÷²ÿÔÎŒãØóÌÄãà”PúýÚLmŸñLŸï|{ûð û;ð  àW1ÀmM&…¿ ·À€`€a€`‚ À0Àd€`€a€`z Ü#<ºûåݻ˃y`¸ð[Pù’Ø_· ›Ëïë”ï–¡³Í?ãÕÏËf$g~•÷¢¦Óï¶¿üý•ÎáäÒ}Чñ0Àÿêùç¿ë`Gl¿N5l€ŒÞ.×ú¢ ü³å³û¨*Ûo./¿þ^£äøÕ·û‹ñîúý¼ãp×x!oO ÖJí×G¶`ÇÛ#W²Ç ‚œxe=(»ùh¾šÀÃØÂîIa 7oö.v:6ÓvS-¦ÕEÛe­ç—Xs|.kO<‚þ1ýu üýq¿úåò}S'\ãiyü€&àOãŽxÌŸª£Ç‘ô6”~þ\†á¾ç3)¾{ ñèØøoVœn"ìa$¾ }뮎Çï] ê¡3¾a€Í4À#',ßy‚;zV´çÿ:??*.{»LÛF\.É™†–Ý|d÷u5l€Å á·€ÔXX^ù¼}pùH[¾k<0œÜé¨nNg2™§yà¦õ×mºÈ×·@ê¾fL¸"ÆÓòøÍ1À£“ö&Ûõ¬ü ×ãhzF?2ÃpßóY Ü·@~”óvòfïü¯ñÞgá ñé›c€mÛ²·ž5¦‡Îø†4Ï^Ù…qOpÛ?“Ë¢0ò¦¶Ï'Ëh€‡V“³þçXhÙ3À…™Æ pߣÌçÁëñúw Y†—NEË&»“©r+7£ï–Ÿ€n^ÝÚ­žpEŒ€b€Û¯¾Š¿öóöÚŠN#êmý„¾_¸ÉúØ .—ìÞC~ºÔ›÷¡O ŤoÌ¿Íd¶NÆí­þFô°:¾a€M3ÀíÌÛn}³j‚›+%—éûH•l2k2À•bŸ$°´ì`ÛÙÞóoŒÓ«î-lšãÊyÜÀF±#!އnba À1À>.êô8¢Þ†ÑOàûe€›¬à¡Õ°Ï Ü*>n€ùt)ž+Ùo;8Iu|Úh€7¿¶{‚;Ê>®}ÊÃÉåUƒî.Ìȉ®0£ÏG¶åHVc8V.ÕÛ=}lI7·iø~àhý…  0Ì·UŠÉCGÕÛú |¿ p³õ±1Ì&h ¾ŸÓP|®æÏM72ÐÜñ  hšnåªë nyÑ¿ö•óNÒ¤Õw`0ÀáVäù¨÷›%VW¨àÄîùxqs™/Ò‘˜n²ìd¹rém¼&๬ø7/V¬—OœüÊêëò'dRöÕÏHM² œ9?*Úâj`éöXlãÝQ¶p³÷Þñ²k=Ý'ã/bìÏ› õøçÎ;.³XŽGÅ>§½©ýõñͽ.ÝñbØeÛÝ yìÙäæë½Ó::æ4ŽQ½~Dhh}Áý£x_¼Mﯺ ·~¹ý´?4틲 ÎWñ¨óΓ:áª{}ôøë'ñ,d½y+û±¢ƒ¯·¦MñZl€Û?Y½ÿÓèqT½¥ú©Ëÿ<}7oöNû C¢’µ?v)ùU“o4ò"éÿ|Æ|B¿"çc{ý z£ê)?› ðê£&i~¡/½ýóM{¥øtó¿þÕ 0k9ï~e}{jã;„ÞEÏÆñx0¸MÜ…ìü)Ùx\\;1àp+r8ùèª$Þæp p÷‰³æàüŸq~3‘å¹tÅq¡8[ðı5ñD'ét‚'à4ÃÉ_¤,Õ_M°©kñOu °{z¿T]Dd&î`¾äá˜Xøçä† 0‰‡’Î'Ú»±nùe”ç‚4ä,Âè%z‡>ýmÙÔ‰»C=,ÕgèÀûãmz™ 0i¿ÎËíK\»hêOÕÖ±î¾>Ú•ê'ñlLÛîÄÕ½‘nè_Ãõ&ý"~@« p.oMiô8ªÞRýÔäÿ'}ضȤUÃÀÓèXWLkä|CˆSý‘Î÷Ü”ï úÕ„|,·Wk€ýz£èO`~Vâk±>ªñÈó6‘Ã&óåz“øÔù¬žfÒÇ>i °ÚžÚø6è]ôñlœ?ªn?¶ÖVÚ÷×Ç ¯÷k/h3àîU[Z–%|qf½ì©àÔ++¹|‘9Xï(-ð7=Rg6Wíô1ÛÁðr>¹×ÆÐª—๥eR¿“`¯‹ó{¿_eë`ÿöܸ½uúy{ÿ›x5†àwÙåuë啸ó‰z|&“a©½¾¦ñPäóñöNÛ[߯¾YŽ2‘ã™ }˜´Ö¾¯0ïÔ<Íóã3Û_ô·èS¯ŠöÛaêĪc€åú ý£x)Þ¦÷—ÉÓö“þPÚ—f‚·uøGfû:Ò׎ëÑŸ´~96 to¸³¹!ÿ?cê_Ãõ&ýkŽÐJÜÞ>º{R²}o px½¥ú©æÿa{~¯áûîÈ3 ®_Ð9ßPhþ•ÏgÌwýŠžI{µد7J90?‡1Àw©j:ÿ!úçàÄ™ûësb€ÕöxãÛ¤w‘dziþxÀØžX·¶.F®óöÖïbQ¬­•6ƒ»"GÕ§æøúŽfùÎÉmé ÿ^¸s‘e¯rvy:¹ë^eÊýÃÂR‡ÞðˆT,¿#ã/“ú{¢b~ÏÛ¾'fèö…¢Û”…,‹i Ïõ s‘?n3°X5ÀÒñ¬Ä£\é|, ÛMðEÍõ¿«çlçWVª@?!Zue§®–ê3õ*ðr¼Mï/³VÚO °Ô¾Äœ»‡ÞÇ”wÎ|Ç÷›ûSW¿?žÔYõÑD6Ðùucÿ®·¦ã´Þ3<Ú7 ·T?•üÀ U\è~þÔª“$4†AÎ7¦=Ÿ)ß™ô+j>¦íÕ`IoH9D~6¼|·úHã¡ó JÅ’™¢ûkÚKã•ôêŸÏϹœ`¥=¾ñmÔ»¨ãÙ<þ×ÛÂñòGŸùrX•¬øu†@zE7m>ÍÛ;Žöž ·Þ†¦“3üÑÚWÖÛŽÎa–W]· ºnc€Iý"Á:nã|ï½Þû¶³,íº<‘º™f¹šàÙøí °õKòùx‚®ÊD¹d/©Ç³½¶ÊÅL¶C!`rÉ•½ºXªÏÔ?—âmz™ °¦ýÄ×i_ht}tõKã#W‡”Kâ3cÿ®·¦ã´Òó…¯v¯Ö‹¾g  ¸½¥úIó_Œ·‡t¿çt†AÊ7º„çÏ¿J>2ä;“~EÍÇ´½:,é )‡ÈÏ&|§úHâQæ¬ì¯io ¦úç5uVÏÓöøÇ·Iï"góø<`,¯óƒHƒåa[ü*p .“{åz÷"]̲Tï3Çç²Þ;$¬ÌÒßlj²·¡8ñäii¬Ãy‡)³½Ë&Úw8L‚Aê ¶¶ G:¦}Ø·ß`¬Ö5Ä?äyÖ߃z˜þ RVâ!ór>þˆÎŒ«c¼Vz<”Y·f!Héý<Ûrsøýõ¯ÔÞÁªk€¥úLý£ ¼oÓûËd€5í'˜´Ï=>´º>š/Ôú¥ñátsí"û×p½5ý? Å˜«íqÖ[Ë`€Ñ[ªŸ4?øÇèÞsÏ/è•òÉ¿J>2ä;“~EÍÇ´½:,é )‡ÈÏ|·úHâQæÊüL~ZÙ_ÓÞ@LõÏg€'ë=MÛãß&½‹<žÍãðP ð'Ë~Y½ýöÎå'ŽäŽããvkp˜e˜õă—]a³Kd#œ¬ñ ¶¢ –¬°=Qâuå†;‚(b#d`ÁÖ® –Œ„x%ó°s°%)B²9­’uPÖj“kŽ‘rHrÈŸ®~LwWUWUO÷0<¾ŸƒMÝÕõøñûö·§»ê¡6Ú_Ôt/ó¾Âþ毰þ€êàš+Wõæ~%Jo)ýdòóª¬eÖ‚ ƒ?¿³øò/'‰óD¿"çcº½üe¯Tò³â:À{£T}˜ë ö†­ïÚ…ÙŸÓ^¡¦õÏ5Àí#NÑ´¦Úã‹o™ÞEgiü©®)ôçG­™6À/x‚<ëõ9)z›c€kj}‹ö7À~µ(jÝs¹»dùß›Eí¶gC;_—Vú¡âSJÂ`öà¨8—’½¢K}ìÞA%?ÑÇó MmÝÌXÎ~“†6À¥GšÜþ`Éø ¼UߨÇ+è|žöÐí`Ϭ¤†R–c€ÅãÉ-ßä’ “vV)Q_AóÆ€ýn€ ¹M-*àPzKé'“Üw(]n{X`µ÷§ó;Oþåä#q¾“èWÔ|Ì´—Ñ™–çgE¼7ú˜ù 0µ çàP˜Ñ?×?ÙNì‹o™ÞÅÏ’øVÜÔ¡»+"½?\oC6þã ò©KùÛ~IÈßNH 0É~k­ö;À7ýw ßd|n¤§–r§¸$àä^ßO6¿0¨+¾3s3‚fߎh€™úÔÒ;ÀvZ¶ÛOÏ74™ä8Yâ,·|gª]ÅËÆ'Pà+5^Aêî–Ï´_b€-[Jf,ÃKÆ“[¾?>È[Kmöš ªãØß¼ñ…`ÿàBê…Š§·”~2ùÁ|‰6 +š3i4_éüÎ%™ÌØù—“ÄùN¢_Qó1Ó^F?$X!?«à=ÒÇDÈw€É$Êm‚ë‘2 °OÿܳºV^l€ýñ-Ó»xâY€Ãj€;oè£ýÎ3Y-¥¥‚ß&3P麻Aj€Ó†Üûg5.™Åæ%2m`WÇèÙÓðÝrVkÑôP³&–g€ÙY #`¦>Lÿг@Û ú¢qž {|¡Iò—¹1ɲ­dù,Ÿ@·ëûx1w¬™ò™ö‹(XÛ†FàSƒžgýdýÃŽ'[>ïuè7œÁS߀þ毰þ€}`€ëŸh}Ë 8¤ÞRúÉæ‡&j}qs2åö«ZÊ1¨þüJç÷ ìüËËGÂ|'ѯÈù˜n/£¬Ÿ}õ«¶>Òõ‘ÍmìàWvèp@ÓúW2Àu+\}fÚCÅ·TïbŠç$– àè`C‡SóöwÁ)÷•ß`fFד‚º³°}s®v¡ÁL†f:%ÿ“L_ð®gÝ=’§ÆGRw”¢Ëk€¿m¯ƒg.ˆw:ºfê“`Žõž¯” ÇGtk9êxæà£çÞ-wÝØ&ëÆæøˆVÐywð“ŒOÀ;õ}¼˜úúËçµ_d€«1òÈ”¡ÏA@—^+"ëFÊú‡í¶|*>ŒNnëÝnûU °Óßœñ×P}ÜuI/™^‘©·´~2ù¡qDÏ¿°¬»n*QŸüW’œüÊè9ùH˜ïdú5ÓíeôIb€ò³¯~ÕÖGº>ôõooï“]ôþa 0­Ž®{ž+½Ù+4ÀT|Kõ.b<+ÄàÐ`ò€³9á³ý„³!Ä}ä]¤®mÝ#ÂØXß`z;È'Š{õÔZÿÓ©…Mó›E²l1”FV53ðÓÙìµV+/×­èùùáÍV½û¹š`På‡5À‰¦½wùéÔ̶y~eœÍºK©llg¹õaªï|dj†ùÇÙ©%=;É9ž¤Æ‘¾¡åÇF‡=Êq¨[jmkºUŸœæ®Ëœd|X÷×7öñ¢/¸¨òyí÷Žû¦÷®o.é-A“`¥j©¡ÇFî^–÷S?Nùt|™=KïÓIÆWÖßœñ×P5ܵ»e>f5sC³V_ õ8¢ÞÒúÉæ‡‹ƒÆ'ËÃÃ[«KçK†|ÊÕ[FéÏ¿<½å;™~EÍÇt{}’`y~öÕ¯ÚúÈÔ‡º¾`éêÐG3¦œe÷W5À޾ÑúgÐîáá…ͱ‚û®®O©öÐñ-×»hñ¬€Ãb€É´Ï%L;С§vVÇrúdÜäz}«ÏÑÛ¸ý†½LA㣂uvidSëcò(NÒ³Ž¡s_ï½Vs€¡µYéòCàÚ¹ëð¾’ê˜Pšºð‚Ye'áSõ¡ñŸÏ\¨ÝÚ´okRÇ3øªî h ÷Ѳ.«2“m]9Þ:‰¬À‰Ç‡xª¾qsBù¼ö{ǃißø¶ù˵Ù`CH—"4_–÷3œœòéø ÛnˆÇWÚßœñ×P-ÜdÓÝU"·ùy¾GÓ[F?Ùüй­y²· щÑËl~eõˆ9Ÿ?ÿrò‘(ßÉô+j>6Zæo/­O2¬Ÿ½õ«º>Òõ¡®/xWÆ;¦w{xû+à’¾Ñúg`›ÞgÜë%ú4*¾ô.R<«ÄàÐàú¹AòQjg¸Fn€Ç·ý/’ÐÛÁ˜¼Sb=\;³Ú£åwô›7ÜŠšu#Ï^'ÁðüÄ'_v¾ìÉï,gª‚A•Ömš^ʇÏZ^6²¦êÃâ=IÐ=Oó©ãÙ;à3†tR;‹gùÅ_!ý·ØPZˆBf€ÅãÃxo}c/ª|Nû…81nßT 2„uc=F“H…¤ýÃ'S>颿íKñø²ýMÅg|ÅõTÉwŽXv&µ³U£`€Cë-«Ÿl~ éAËßy°Üàó«¿ÉgL~åè•+éüËžO”ïäú5SíeôCf€ò³§~Õ×G¦>¾ë þ‚µØðä,»XLëŸe€ýÃ+0ÀL|³ýs<«Äàp`.sÓÓëÃ*;Öw®ûòD-µ#¥'F“É£4‚î4ýª$³µâßïqÿUú|tù²öQMzA."¶£X‘I5Øøàöw õ”c€ÿhrém¾®7,ðÆ´!· 5Ö[¡~šeÓ ùU®Gtþ ›ÿ•òwÄ|æmoEô)DýöDÃõW¦ý£õõ­ÇñV¬ýS‰ï¸ã¹ÜëÀá0ÀÖ»H*{¥³µ¾4Coƒ=7À ,êV*»ìA…ÊG|°¿ °Á±Òej„Þ"ß ’éd&^ÿ[žþU<¾ÏÀ— º™ôÜbÆú!`Uˆ}^>â€ýo€OÄd€¡GúŠxÀÃh]Ô&?~:µð2W‘I/*]>â`è8ŠúŠxÀÃrÈ93ά>ˆå#>€†£¨¯ˆg 0 0(‡©]sÈ¿&dùˆ`€¡GàHê+â @Y¤KÿÌò0ÀE}`€†`€`2 ÀC`‚ À0À 0 0 ÀCo`p„IϬ¯ŸŽð{€`€Ãà+3›ÓÓë !ùÂ¥üyÑvl´¯L¼HÚ?5Ý õA"`[FØý÷Þö‡m_Øöžº”º]^-›rÍ—Ëêßö­¥!ÂïQê«aú?ìßÃ^Ä£¨þjã];7Ö£å'†f¥•æ þý‚#f€ß:óÆõ@l‰íÖµÒõ3ä¡Þ~õ;wî³/[E;Ç^ÝëL"ñÎÛo~OºóßÿýúÏB•^÷üÕý“ßûO[Àv0o\¿÷»µg1ŽÈwN~ëú‡©ß÷ÿ2îóŸúý½O2e6ïÓ?½ûE˜ýÿ÷®Éëï²h|ôêþw½ö$@¥ ð@«Þû W€Ÿ/é&wÉÆMwh¹&2ÀµjÝgÛvj»Z*N·ݾ¹>Ҫ轧ŽÝ>ÙñÞöBœ4.'r”_q¦ÿeÕ0tTý}ñ¥4Þ+VRm“þ=Â`~Òc‰aß疺μì1CZ›\4À·þuÎâÏqIÔojVSÑúÏ××þkãÀãßûÃèeAñ?>i¶Þ±~ô¶H®;qâÍ?¦[¥ó{³pYØíŸ¨˜:ÿÏOzŸxí/æ‡_ÿæ‰|­¼¼Òߢ °¬¿÷„ÚߘÝ ¨´î|”Ó=øÊJAïÛ]]]* …2À]£¾ºôv ¾ØòúõÂ¥ÔÝŒýs:›Íz  ·e„Ý¿ ¢ÏïmØö…moT\Vÿޤnf"ü>FÌ«˜þ—ý=T!éúûN§2Þ†•Ï ?Z°Eý=Vš½>qà+Wõ ClÇrzjÞ¼ûüC=E>XÒõæå üßsç¾ü?{çÕ‘ðý•çfa»À+¨U@WW`AKh‰‚?(w­VŠž¢¤Ò+EÃ(¨w)DHZ+ñúXµ©šxþˆ&œ1žÒöõO4¡Ñ´1VZÿÑœ¦673ï½Ý7?Þ¾},l17ßDÜÙyï;3ß™y3Ÿ™ïkMLmÛR>N#Ô]³ýhß»°ëpû÷é€ËM·ŸÌVÉ:Íûœ'c“¯õô7t8…þ°Ù]Úl=?/ë¤ÉXXeŸpØ/}˺+uŒUit¸²ÒåÂîѱwDäOsìç÷÷ÇÅ üÂ… —‰༳]¶ìÂÇüÃU…‹?’¸îØ—’œ ¶:@‰úE†Ö¸2_ÔjøÿQH:‚iö€bP¡ç%`²üFËç‰ É¾B~möûÂØãÇ€Yù7jÿ ý!òí‘Ì¿a7`EÑë-À\^RŽ9F՘Π[]5üp¨ý—·Íg[¢1Þ¶ßL»ÉUéŸg à»heÍO€Å{n÷Óîû7Rî=t?€¾=Ðß¼»ö ûÛ)šüN€'.>ÜåƒJ–Íf]£¢?ìèqI_4´t%d_îh IßÒÅÕo‰EÖO–O­Ï“Q—’t¨°oÀþ^`滟U~gg}¡¯ñrGOj(åà3lþݬž û rì®/l¬`•—oÊk,´h°c÷aݪˆ¹š4œß`€EÚŸ¡U>¬?õEµ/uzD{¢Ú «<û Àšú${nSyá¡?böÐiO¬þ@ÙSììjJ´û;OØ› —IÀ`œñú r ÆØí·ÒdxéNKkt'í¦`j»sKí"úŒ¼ugˆéYɳÑþA 0tÒ¼óšÖ€×~ï!àçm€*«vÇ€7ÄN[‰UF IDAT.õß“æÔ?˜QáÎíùÛΟöK=6éuûZ—£³ât‘y]ÀÕtü& h&Ó—ô›±ç.—ûÿ¡o÷@7YWáSKPÇõ\ëú6*¿tùqûú£ëxYôIö †bý«  àÜò†S{οfÊÓÙú)˜­|<<2úLÙ¥ÓýÛïÈ/þßLÈ0kÕXÓÞPn?ñŽ>» ¥_ù½÷ËÔ ¾òz‡a2=:ž–øMÖ]üü/.\&€§à\–PÚ3Í–2BØ©ò‰e£‘Y<¹A,± v™ïSøì„>¿lš€(”(ÑÒ–U±3GvÓ¹ZA,tùp}pÂ]ê.AŽuј>ê~Fù×Ë~ʤ”uʇ‡Û,uÊ@Wâ[1› À™’Ó_¹Fqýt¼ã¬Ïà$ùþÒFfŒ3ž_£Œëc–OÝðúb´/ÆÚ ¾¡þCµ¯àú JÑXãöÐiOŒþ@Û3>+жY훈'ú.“€§gØ–¨}LæõZf¼ËoáQÚ`ŠE À©·VœDË|¾’…µ6(~Z”üÀ ¯@|´Ÿg=Ý¡<P¨ã+3â]?P 2‹qq[ðú]~a@vEÈ4{¥mÆ€×k[« ÛsÙ‹‚dú@ÐgEe^z×,åLP…?ÉüRå'ìCêƒÜp¹²Z´šUô ¼ܾ¶þ]ŸÈÒ¯´ÜþyvE¿€¸nxeOÏ+Ì`M{ƒ$~“ï¿ãR€¶òWIç#L¦GÆ3'ˆ›ñŸH¸páÂ%sÀRWÓY_˜ýaÇ匰-»ñßÇà¤Zæ‡ 2ƒ9;ÀPg/ ÑD0›‡w³aUìÜæ³­èé+>daâ [ˆnÞ÷eZæš^ÃÍçl¥é!èÓ`\˜ð·$TÕ[–@g“úèû©ò{²l¥Ð'oKWzå£K©©ÌU¬:ÀòÉJKÝÕj€!K\´~u¤>À¯˜õ;•k_›Ê(Nú#¯öžj®¯ùá®yZ}îO߯Â÷0Qúq0ûô‰wôé‹a¯÷„HÀŸ£ïµ}öxX~×f°¦½AðzŸ¥ÖýDæW´¿‚¯¨b„ÉôÈxæ¬þÝ\¸pá9Ÿöõú¤·|ö¨_ƒTÚQ¬ À% ø©@2LüÀ§Ú-Š"Ü‚)*+J„䀙l|V#\@s¬‘ %?ž&ö‰~  ¯/ó•‚TŠƒ0¹_*¡áû寄>r 4Y>BŸ'£´p‡kaÎŒ®®¥°¬¤>–}°ò'­„+—(·¢Bùˆ°³×²¬M. ã³l¾¾©h/ÒGèWÇ#û,Í—Î5ÎOO^ax{^æ«[?¡ä7(ö§ô1˧êªúrìö€‰öB¥o°ÿ틪omŽÚ!˜ôû#a†½ÔõÃê”=Û|Øk°ÈúÁâÉþÁ…ˤà‚,zx)IÀ\Q†À`@¾p»©|RÞ€Àücü ËzPPpô륩pç¯yQ+‹°î?Mí¾ï^^yïa¹ q™½3£/¡^L®¨êðüé„mü&3".À;kŽ¦Ãœ¨ØqÒÌ<»K¦ô¡ì›ÄOcçn^$ŸìqÄ)êôó'½L”Ÿ°©ðÎX¨Ê¹UZ‚&Ë£“> À|ªx®ã3×Þ•z|æÜrZ?§fG¼f›„v‰PÛ‡½£C¯‘ðC9Ö`-{C }Òwý% Ýb€–!:L¥G^Ïœ nˆË˜ .`ÛŠzKÓ±}‹ÏfØšà¬Î–––Ãõ…`Œî[ ÀF<`¡IqÈN°â+Àìµ ¡jUÔû¦¤ZyF'Õʼ£ ÀXÞÁ²–{ RŸÞ`BŸ'òO|ÚþY&üŒüQöÁÊA0P>2\…”9Ö0OmÃí¤ H¾IýX| R#ome°s¥Ìy"¸?eâ{ÀXòk€1}Ìò©ûQ_¬ö€‰öB¦?†þCœÆë›5=QŸÖë¤=ÈüõC•iOG1öÞ%ª¿«ã {sá2ðÿ²ùw tB…¶@›At:˜+ÀiƒËå0ŒîAx(X[øš5ÓnE( 2ú/¬ÁîMbœ  Òö^yC.ë ðVâ̯:œôºyZŠ|=ä;ÀvÈ¿k9Š"ÓÏW¯* ©J¡JœF5"BÚDB†*U>T¨ R¾´R«*êÌììÞÌ›ÙÙ3Wš÷Å7ÞÝ7ófgoçwoæ=¼tsOäp¢¸†Î»§Àˆ÷v„ü±²ÛªÍ܉mqw¢©ûá¨RŸ€¡>4áߌ1¯%~Iû|8ÆlâÕ²–Ka”»¥?Z V”Ò_-æ/õÃã¨û¨cPÀ™ã¶«]%ùC#ñ›ö}o<ÀÏ®¤Äˆ=ÆÐUýJÆgoÄ^è¹]ˆx…þ|а’*ÎÉhèot+0÷«ÎÊžìÑßè܇ö^Þ{îž_ûêßüçßGŲP<_òÊü°ðyÖ߈#ßÓ|„«¶pá¯:묽»äÜÈ¿ª„² úœßbmÎo™¿´3½¾¼º®)1en‹8Óz@,ª°¬øÄ©a{-g²¿<óÑGŸ€¡¾Ê |9ñÓÙ~Iû|¸´JiŸPNÏcA›<· –ÍÎyX3Ô§’ŽýrN%3{Hu’×t’óN%pD'bVÜö` O´}Àý’Ž/_ãÖŸÅóØO_`†ý¡O‚}:ãEµâÃ8ŒLkÆoÛÓI7 –7ÿ:Q ¯ûE¾e¯€öû(öhe ¿ïïð®K³¾úÃp@7•µŽ}¸Öfº àe/¹K¢s "v¼Üž¨ `)²¼~T+]¸Mçã)5þ Éð§èæí‡ýõ!6¥½Ô°× íQÕ¯`\ûì ý%ê÷Ód´çŸUè,€|„eoöèo7¤ keïCjLOˆ/s•8áùP°9ï›h FŒyúüzÄZG9÷td/‡ñpí)é 9`¬@à‚=‘ÃØsUÙ]»$•¤j§ÀÌÎF/@Œö_Á!cwâ¯ifç¡t*-èS0Ô‡£Þâ ?>Oøeíó`°•VÃ>!Jo/^d*Åæ…%À"èÇ ñà´µŸ#ê×Üêö½ŸXçG/v¯/údÕÞ`Ìë“ØÇ=à~IÇ—" 4;^„ú³x~<ò?&ý¡O‚}:ã¥Òw ÉÓÙjnÄÈTø™U- ûìÿ>ÌD|½<;y€=\€ä¢D"Ê(úOìãÄõ€ÖæüôçŽPðÖ„¤N?ÎÔtÓÝ£€‹·ñÁœ°¶h¹'óöÃþú2y€—†ö¨êW0^ç<{#6…°Ð_à§ fñ§¡_!꜕ÐRXÀUUŸ»ò£“ÖˆiWëƒçË8ïÚJóÕaĈ‘§ ÀÏ´¥­…ÎG>C[$þžô…Ü™äÁ–EÑ`4-nJŠ–´X›Û";Ôn»KN N{b,°/iYKÕ`AŸ ,ýþ`À’ö)=À³ÚÊdói*)ßñ êÇÑlêLË=Àñ «›ÖÔ!K£ùc$UPíŽÀ@Ví‹öqϸ_ú`»ýAXçùy²œ„)Š|Ç“ÀãEoÏ¿#Ó€ñû•üÀœêöæ_?ð—L`€ûÅ—@;ذÂùä'nÞÕo„qº\(3y‰`°ÓØ €½—@³õǶÓ-²¸~êæƒ8ã%Èo¯Í¡!³„öBûaÿ@}€¡=ªúƒ°Ð_@20{ïsœ8k°G­1*x?¸?Ë=À~+:8v£ðYã6bÄÈSàT•Ɉ´Ž]ó|Ú€‹kx0eÉw\N%×A‡ÑôxáY'ʸ”Öê™'GéÇi`æ)÷ ú\c*»-­=À"Ãö©ö³Ü¦e(/oGŒp6Ò”Ð`N¿(íæô·[{€7ÀõGȶ߂å—GâŸf”U{§À¢}üó ¹_¾{€Áx ÀÏÏ`ØŠñ$ا3^{€çw‚‘ÿN“÷k}»4Ú¤'OR¦›¡XµX ‚åáO² KžJÏß°ˆª³7)xnp4z°w,¶~À´Õºré`~/ÐåÛ ó^gŸÚ+Øê…ú\ŽÙ íQÕ€…þâ%ãN|Avßf½Ø£¿ÉÂjvuº?K÷û°}SÃ[;#FŒ'y!Ž€%x€Ýmz1 àô·[dº&Ùn”¦©ÁJÇn¨¿7’v|Oü„ý-ĶúVÓ<­$aë,µ¾âëP—ãûœ%Èœ}@ŸdɧÐ>¡ø<À½Ví{öœçTeŸ¬Œšby-—øB{ùãkêˆ&Ä“ûÓ^y€Ù‚ØÉÇô+/‚C§ 4S`Á>ø<€û%_l}`<`ç‡_€a(Æ“`Ÿt¼ÔŸãâ Ï;{ô·#Ó€kÈÒ«úv7–༿óÿÞ•{€1OR‹J 2Ì®Ñu¤<¬€–- ›]g—pcå¼_j-Æy{©34và˜7¿˜+ÍŒëG¼‰ÿTßÿÌöà®~#<ç[!y U†þ‹‹°½‚ý  >èìQÔ€ý€O˜¥i€Cnº_&0[ö`¯þÆûÐ>Ðór© € z„ú 1bdÚð $$à3úK_Äx/Rã8y!W÷Àë W½[gÿ¾KZÈ=3¿eqú\TTÔ‰ý¡ëh NGâ;/µŽÝ–ÒGò  Y·ÍO¸ÑåQvŒÊE^e0YŸ¿z¤Â8âæ9eÏ/éíØ9z¡¨¨õLÒ6$Õl­½xppÜêX¢¡/ÿ¬UûÚ®ËVÓ»‘ÌîFÆ>^Ÿ0á—´OèÞþêntÅè®]WÏ,QÛ'+´¥-/'<¨_ô€F¬õ—.X Ǹ Xn}ù{"ñó/\yG¿^k§r8ïƒ][o]aMÀ¥EE'Ï. ÇäX²=<ç£K}Wr¶~¬+”ÿvaÞµ›èû+HnX‘÷‰NýÅÛÂy×NŒ_ ¿9^hlª ÿçê¥#W¾çh¨q£¼½‚ý  >èìQÔïÀ¿¥ß§" úyÁ¡¯Ž ÿ÷ÏUU¢XýðØðW÷©+–)'‰„N‡z¾¨ªzøÇcÃ/}ßIƒäçêÓàØv~Ä1bÄÈ“`öÙ•&êêOœëKZx)Vueíß7ÒlY7e¿Hw&×s¿C²lBîVG'äxy'€ Ù¶íÞéL; AɉZ·²‚”]&,³m´,mLv¬ÏGís:ľ>6Tg;¾ÕÑ·†”ã;gf¢0söñúÄ ¿¤}°€ýõã´Åd³Â>iÿ×X^ÉR ú…ãýãäøùclh¶¾’3i»¹ÿ‘¢/¨G½>šE{Eæû‹íyûÄç!Êß/ÉøâêãÇS`Öy~Øñ•%û<|(Æ“øÚ«þj©ÏQsaGgëªë[r‹.4©Ù]^±ïuËCDõO^ã ¿²}t~èþ‹/[ŠV7íí®£¬ü8ÕÛ+…ˆ,_ùzu ÿjlâZ…æ^`w$8Ï–ÚÔ§ñöF'ÀDÿ˜û9^ÊùEÖGÌ'ìgÿ‘Ï¯Ø 09Þó‰±?(ç‹§ÜB<Ûˆúu*ß$/óë«Q”÷·ßºý®7ô`J€…ûæ_oë­Û|—ùp׊²ôÝ^R€ë.OÐyç8¿€Zë.ßÝÁ~q¼ÔXwq‹Û¥W€M|󥯬ºËký .S€'²ònè«ß4^¬¿²»x»(¿šrž=7Å ú6õ ¨É5æˆ`Éâ/pD{û÷³ÖŠoõŸÌ]žâ0ÝÍúµ˜8¬,Ÿ ³í_l”n›!V¿ë«ëíÃÚ;F›Ü̘%Àêù–”öKÁiÛ;Î{M:˜®O÷ž“~Ÿ€x 0“æúú£@gt+i8)„ìSf%>†y*ÖôãqbrÂiÔÃeóQ•Ç,ß@ÿ"Ù^z)pJ9g<ãG¸S.—|WRºræ«_õÝ•íUÛäã•ùNàþƒþyÖìõzÝH¾ˆ­[g¯méÇà A× ëë6}+s`:Ë ‚s{Ýœ0ÁÝn/NpŽ._ÁkÞ+ÂíÝîÂ…fÿÑSbŸLÜ΢}i¾óÚ(k¬o ¬ò±ã¿XÞŽ‘Ÿ(ûcôcÏ¥¿ü /ǹ©ÑþÁzr³26UpÒF:ó(›«Ðjº>-†.É;Ï@—°ÿÚ`=ïreóÄg-ƒÀ¹Ï’â7Ê›;!?)†ý!>Ò¬‘UYI#Àâ)µvÍÇ à˜™•ñŽLW’øšËêSeösCSWè9À©“sB#Žw¾ NI} €8 pÌdäð“rW IéxÊÓq‡_@r °=qãï˜í‡÷Ç쒔خN U§ÍÖDù/S€“:?)žo&SÚÏEv&!@€.žhËÞöN®ãù”Ödž׵= `Õ§FI—*'_ &šíöe pç'ÕóÍÄy"Kº3Ù"|€ À .¸–K÷; ßl<ûþÓh† À n¼3×QSù^ŠŸ>¼Î‹‘@€D)KäõüjŒ|'¼²'ËÄa‰áy¤ À 2 ÀX0 À` @€±Þ`Œõ€`@€0†`¬¤¤ád}ýÑmª1{A.Qôp¸8fî«ù ø{’Å&àxÒ¤’ñ#õ ß\Uj)ª©Ü¤G>^FËËáx>žùÐ_~ŸÜ¾£ScŒ•ùt™÷Êï9QU€ý‹ë™'ÔâØ ðæÏî}î&þ¥âþ¿ïefÞû`ˆæ¶ýûåMÖzçxëÕ«MO{#þLþ]íwäÝ[‘Õùsã?­iirÎciã~&µåÏm¬´¯0´Qß/ûu¶%ýFØ­Š«n6öÊ7™v]ö#µÝ&ܦçÄÚ‹ûilÔ{ÎàgÂÅÇÛ=§ZÛ]Œ_eœ\ýONœgo6fÕÕ~¢¹ÿ»¶J;Wú"cå{öÜl,3Ý €ÄðÌA¶Ç Fg[lë+2«,ÈürËw¼aâÀ®Pn x¹ðäQcbÕ'×öxø °áötµïi¯.ö쓲h[5„D>^:Ê#úްü„ pÔóO‘ïÔžß<¸|°Ô¿Îx[RÞ *Žƒg¿/óz>῟eúÙÈÞBx¹ƒÓ+žò#f©¯ÓòÙo¨®º::\l:Ê>m@,x¶(Àc¤ýÙÄÿ6Í/¾ñ`¢>\xj‰4ML¸‚àœ‰Bòó®ôH”;г òTÙݤc¼c/Àäø*ã¸ö?4×tüÈ¡~¡Ýú¢Þ/ ?ñ§ÿòªÑÌöôœ*þQW{q˜5¿p¡³G7j6!ÀŠýW¹Õ‰ˆ´€Dpñž\›L€Köùl3®¶¶¶ø*™±Ú‚¿x`¸¤ÜV#,®U¹6ÇV#À»>ÍìðÊ-ê£ÌÌ/Goß¾ô*_’…×?×)D&§ðe÷廫Kÿ+Ðd1e`úsábÓø©Ö—¼Ñðàg²ýø•7í§&×¼´_-MŒõ¹ é='œ ¿c¶Þÿ´ø™môpE…ÛMlÃ7T6[Ï I¨ ˜íçFëïØ 09¾Ê8ج;I’û홬.ÀÂð˜#Ì.›weoÛº5­ùÚm&³0 ‹Âí¿Œ`?·£-;§¥¤`ñønQiH€3ú•RP¼†«-È…ËÓ圎;—ƒ™>Ç“ëS_È9Ϣ䪀pT,H …/ò ÀQÎ?:ß`’S€3ŽŸ‘þ×<ȶj1#އgo^ê&„øï™nÑ¥T¾¡Và¡K¬s%vnóF(À®u‹ÝQtO`¹Ý¬O†Ýšæ÷Øø 0Y_P±ÂÈ‘à¿éc¶FûÇ܆¯>UfkÀ±à^CÚÖm{çN–}n®`:ßÌüÇ«ÿÕgD®e¥¿!ýq &=â_0ëViA€§j[×°ìˆtÆ…¢=‚ÏjÜöõ#öëú©u·°Ãñº3nT€ÇŒú:œGRQ€3ZlkÏùWðèxA€c*À3}ä±{:Žò Àñ`’D€eëpÑø© IDATŽ0qLo‚Ź Ó}_õÜç‚/Ò)D—÷Î)9.BŽJ€ ~<î≱¯tOŒ“õéàÂ…æ¼7Ül™D€+½6Ê:«{¢Øª»ósÞï»B€é|3ó·þû¯,ÐÃØâäOè¿\V`ñr~^ÖËçí1I,´Šç‡±ÎRùEjE™ö¹ÏQ ðÐ%yç±€äàš·É p‰ð§ü—g:V[sFßªé˜ØÁ!ÀÉ,À®âR:Ž­ò À`¾VÜm’ϱ#\Ç»@D]Ìàçt çÛš×ï&X€;&*oÿ•oz#Àd}ÚìZQ–¾»ó½1`é¤Ö‰`nÊ@ëK\0ofþ$À±Õ¬»¼ ÷?ÚF)Àâ”0«h®PÝ´|#í3,ÀÂ7L$¡w+-8$Àcç“÷º¢c•Yß°‚.¡W€ù§lë9ñrbÉÚÆŽÜÀ=6Bº|xzÄì,·mà\Å­->[@hø†ÖR_Må!f{BÂ8É2a€r{I ø†ªRYô '½Y@Ü^Çûåí)´*ß³§tÿ!“ðß ‡íq6 Û7ug´wø [ðìâ‚ù ·£·WôOhïrËCò_Oé˜Ì—2Ÿäò¤/º‹x\]™oE{ À$™g´ô}/L+Þü©t·«ŽàZ[³à/~ÐéQéµ—ïnñ2Øy쵥≱ºƒìÙùñ­?ýe{°}ßZ&ðJà_Ä—ÇÝúçßÖd˘Ÿ· íÍí!vÍK1*Ë#ëÓà‚gíß@ÊlÅýÚÿõEçYR»¾ºÞ>¬½#Ø':f5Îù^8©1*€aÇ»ófg9ÏÚçzãíŸÁßU`j<£ƒÎ73ÿqë?%œ³‰CcÎØžÞ”__ÍÒ}Êp§GYYl ùÿìkl×€gÇãeµöo½¶–`ÀŽÆoXˆ›‡‰,Æj5 F1`baSÛiiKC FÁ·BBSA8ª‘¶4n„h›?ŠDBQÔ?¤È¿J½Ïûš™]¼ÆFšóæÚsï=÷œÏ|sî=÷ŸÊþƒù^ËçïWËçésCÎÓÏ?"ç¶Û|ñÄ“Éài<·E[q ¢…²Í9¹ Xô³8ÄäÄ2dd6G G›-:z£o‹¯ÕÒÀ¡ËQØšØÑ(N» ·Ž‘ôQ0SM5Njl3âiß`çó9} ó—¡ÄÉ@!¨O_/ªß´OÖ7Ø­÷Ð_K¼©X ˆL}y|¦„€jÁ.ʼ~ {º°ÔžèÞþ²½„þ]Æ'Ø?‰ö¥«õ2­ÆgÕ4­/úUú[èéÊ%R|{.÷ƒÜ_¬YGS-Ì–(ú_²·ŸÓ'‰ö=ñÄ“Ç À%ƲUå‰`¿°ŠMŽ<W¡4ÏʤGÁC9W¹Ï›Õ—ÑÐß‚Ó|cÏ19z^ΓÊJîC©}á‡zðFW §ýÙFŠ8 < lî… ûa…F¸êÄÝÌ/qd7v ѯÀ ø×™Kê&ükpÐ`¾=¾?W652S‡QxÿC”Ùù>Ìa­…oÞ©$™žýª²€ý;k‚n) “¿9FslcÀߨ? $ÿÊþ¯ˆöVÚÂÆ/gh{âû&F¹>w?¨eCNÒS†à‹>ʧ6\öƒŒ…åþÍHd;`+%ç? €ýJ–ü«~…ÚÌ#¶'žLQž>¢÷l>Ô»bá®вÍ9¶ÃàÒ‰eA’`À¼àµ=t¼½ç†µÀÚ·fœ|9Þz`ý(žÒêÖ³:OG†{õÖyÉ0SÍÛÍzϧ[–(7/ uǦc§"«Žê€ÝÎgõü³hg¯¾rý[ õ9²ÆèÿtôŠõSèK-_½F塾4¾êV{0ø.–ýd{Š"øKjÏ€öZVôï2>ÁþîíKÏWýGÞ‹ \Æã+Xaô;50tÅ ãô©*¿ _8§`Þÿ|{î€*öW¶‘ôòG‚Åûƒ×Ç`O<™Z<}ú³{Ö‰•Fb9½S #‚œØI ³ró©§þhÁ`»ï̉wÿåS/3-k÷e°¨ú('ûêg§†Æ|p¢+ôÉàUà?ùøðR-æ«àÚÀ™ÎÓ§†o4"^=7pæí÷*F{Wæ+˜Ž[£|á\æ·_½ õ.zžþýöƒÅ·7ÌüíWÓ†Ö}a0å_U˜oOèÏ€«OúØ9ãf¿{Xyÿ»ÿÝ®¬¼x÷þ«û?¸Mö:ËjFñؼô £¿-†ÁO?òwÆÇÀ!×|6Ìû3]q)OÐøEà,*¤×nn;´‡TŸ¿Ôr(ç­m)pÑÜìÄ=£`p&t€Ð]{Â=8JËûÏ-œ3u=ñdJ08>Ç»´“Ë6ä–(¿ªS, „˜)ЦiÂ)™¦MD°¡¦g€†Ý0 \G«nœ¼;Ú„&c ‰f­ c^„@ ê£`¶~îcwüD>šô­s«<«~ç³ú”•´® WÕÌyg0¼ÚbyMÖ.½3OóÊ™S*ë:®¯ÜGR©X߀³FeÑ^’=R§à,Kãaí…ì/öï2>Ñþ®íK –P.¶'hE–ý}Wðf»¢>mñVh»”ÿ¢ÿÅö\î¹??à3y +àÌÝXŸ ︶ï‰'ž´¯Òù¹¯¡©3åµ€w’ä?–îj¦ú¿¾ô‹¥w#ZGÙÁL8UV«1ÐB­;—ù„¸31:ø÷îaMSE€…öìË€gbn¤ß#ÌÞ©¼W¬ùaøÀ?·+ïÿ¦-ƒå²m—|¦ €ÎþNpð"vDy:]3å@€ýËùs² (ã3¶Ù÷ô÷È…¯O©¾p?(egÎ÷SàHõñÂlfÞ…€¡§Š¥þ%üçVÞ¿ò ÿ†œù{òD°ÑÔ«÷8¼ør‰Ñ¿^*«È©dÀB/ñI'ÁÊݨ£!Ú¹&k‹ÛAe¼lô—jØ@s¨ i–¬ €Ùúh`µÅ{«nb;(™$Øñ|AŸ²ÈG¹›àôݲM€iýjDÞ~ 5YÈ8ÁuºùúŠ5ÀAqÍ/[–ì%ØÃ €í‹"¬ÑeìUªô—ãøDû»µ/ýí^§“cn‹ÓK»- —XKúX'Ø0ç±=—ûA1þàHÜìÖ{–h6,êã­öÄ“)À€x+Ôåô'ÁbØ Ó5ÀáÙv¯°Lšè¢Âžâ ^mRÕ.k Î â‡Oì9Ì­0 D(Ã5 À ¼½æZ¸Æë’`1ù° gfbà=ô Ì x÷ËÃÀˆM>àÖ[ü«ˆóí¥ ÀB%³c ïÍD ÷æÊ¯å²;&fJ ýMØ9‹"œutñ1(MZlùSAè3ç²ûØÎß(–'€]¯wÞÖågñÅ .T˜b}Åý jšÆuütàì„j%7ƒ¿CªþƒÛ™ Ê¢ÿÒ²¸î¤=ì{âÉÔ`Cïߊ÷?2¥²òœZ¬”²@W¯ÉzN}îÖ÷„sn/c"è{æ*ÛDgâ’Ð]ÌÖ@Ó?‹Pý@Ý¢ñìx¾ àµð3œÍL˜Žj 0Å!£†” &ÖO€%{ ö˜æì¥ð—ãøDû»µoïÀÁu4•¬¹Ç/ë~°Ð>Ÿ–ØŸÔž *ÆkÖ cá{Ëúxì‰'S€a"ʽ£½qfγXž@ÖÐ6¿ÎI°"7ÿËð 8wAxÓWŸïoùÆÈÀ;ÑÎ|‰æ¿©§û´¤ Àó—0T`µGxæK×?K€1ðjå¯CÒ-G»ÆBÁük毸¬Ï€-þUD€ùöR`Àl´F€I`îç~íßd)0ÀÜÛ•÷fKe[.*´O·œ:úð©U£×j/@?ÍÆÝÚ°åÏ7Ÿv½Þ9†„YŠÚDfÄúŠûA–òÚESàÀ[×Ý#À}ýs,úÏ-œ‡ö^ËYä½qxò¤0Ùn€T!–•ä!ö×Pã²aLò ðjm¨yNi[¼©¸ªfY8-LéN-ˆZk"UU³õ¶] )ªE§?M°ãù‚>e%ð$§L•«*!- ö+@‘D³E=ÃZ¬Ÿ*Köì1ÌÙKá/Çñ‰öwk_öµ`P&ºW£CIs¨ýåÈa[fû“ÚsTÕøã{fÛØ[ÖÇ`O<™J Ÿ®#Q.ë•Xž@vÏ xcèËïùB SEuú0•wYïïä=9E¦Çõµ˜êkg¼ª±lŽ€®²?éÜ—,ÿ†ÄiWC`57ЉË$\w.“ß?ð-À¿³5€­5Àl{)pð"?UÙ‚Ypô¹¿ãaå}òŠe{^ÍM­:û`ÎA–ýr)æy‚µ¶ÌûS²G„S.?~Nâzç¢íå]!!P$U¬¯¸Ü•ÐÇmüx ôªÑk$°n Àxƒ2Eÿ,Kþwv˜ññ<ïQàÉÀïëÆJòåyDïY/–Uä3`¥6»õ=0ò[¶iἂ(iv¼¼Çϲ¸±{•yÉ0[ßÚGV PÌÊR ÀŽç‹úÀ,ЀaUÀTŸå5ê)³Àü}Õ)°4©~Ê,ÚK°Gú˜·—Â_Nã“ìïÖ¾ý ÀÌÊcÌ®²>þ¾ ï:]Ø™¯`®?©=WVŒ®«^–ggoI€=ñdŠð´g›9ëÊ“†ü|è-N!FðGf 'ý©oÌxã'T)L{)‡ÀÄL kfߎßù×O' À¿§GK?@_ò—pîÁÌ?‡¾H`qõ™,ÐL{ã`k`xôµ¶Pn¾¼³Äòc`g#ÎþñßzŽ„ Lìµ`ÖŸ“#i¿ Àu'á$âÜv| ‹õ÷ƒ¢Ó³I/™%I°‚ÁµÙ?wàºv˜¥JÑ? À’ÿÒÀÙŸ,ñž< <­%nÀxÿilȳ‹ÁöÁ«ißg.`&€³Ky=#Ò¢u„N¾Ó‹ŒûI2&¢àìŒEûÁ×D¦÷©yoâ Àá:´,¼>š®ãc`5´ï€±ú˜0ômN¥Ú¯FîsÏX¢r>Ñ>û=À?O¦ô—UûûÇÀò¯—0ª4}0¥>!Ó*åØp,{€ñëe—ú›yCR(||`õaÌ„Iâp‰ïM+}ü<À⾯¿ÉÄ@Ô3žREØçÅSVŽÈØX”=À§Õ 1á5œNŽ@ù™25 ‹3†è»GùWªE·ºøÔz€ ßòTÞ‘kôšºäyJ7 ÀzIQ0Oº8ðWi·oÌ{€ã˺¿IVC­öZ°HàÜìà~Gm^Ë­ø^=''yÂϧŒR"-™t{˜°±~T–Ñ—r} €ÑþÓ:oÊjoÌA° _âž`A°˜L._É+ ç<ÊO_JèÄ F¼Âpi¾ÝÍŽpzÏœ˜Ãyß텾̊ҽjÔäx°Uפ>á:¹¬Âfw, J&¸LÕ cõ1à‚:%ñ+i¿šߎ¼â™Ö€¨œO´ÏièÑ`¤=¸½(ýeÕ>Âþvåãl)}êsó–Q ñùŸIZ%üz”(Жã¼^¤ˆŸŸ Z«ÇÄ&©eùL˜0N=ì®>`¡§X jYÝ´ ýc«“¶¬NÊ/ƒóâÐ\n¬ÁjÔ7‰ëV`@·2IÖs^£ëTÐÓê8`¼€iÅ<²ð¨Q =w €]‘ÏÑ—¥O§üRŽõÇgåEÀh$fÁoc¿4HÖýði%½m¸–Sé_pâ°Õýްÿ· “Ûf(§áçSƃeM0 ¾Ø€a~¦ÅÔë#Œ÷¸q%Ø\å̘¿ä,˜åf2A¼w}=Š/þëÄ 9êX„XÛ)R^ ‘•¼„K`R¯EuŠ'Ã5¥JÆqw&YŸô"‰#;çbY-¥¡Íç»K$D±;«)Gšyu3a?`3Þ– ˆêùDûl˜°Wü³?f/²¿,Û‡Ûß¶|¢<É’€7Á%а-RÞ^)ïd²>)­J¥ÄÇ©QÈðëåÙŒ¢ý fpmsM†¾ ØhoJ}¬ËgÂ„ÉØpEĘÄõñÜÜ·`0AÖKÜ÷—ÿïüe±r~SÒåyÕ*ªJ¯U|µÊó×­<À®×¦IÀ©OÛ,-œ¬’˜ºxÓ—¨i€=Ÿ÷_Á€§R`ȺG”Ä1»Â»À§R}dÎ-¨¼°ZžFìUø·Bÿ¡Tó+i€]Zº_C`£nÀék¼æ™gb@³þÆØ5¯, ^·à*סú‘þµñıýv…×x“ÔN%Î'Çe‚<积f €s§éNà”›eò-äõ‘(Ðxÿ}g™Äíá«ÉÛÌ–@ã—0“‰À©@ä€Ï©©Ê‹·î=ª’_À¸Ž½CªŽHYpökr°°ÔÎÿ;·¯±?¸tp„BΠ<)ï  &mI)œàõ¥%à5îÀuKNyÖí;´êXû Q"õIéã‹{ÖÌãçŸ €A]}‡ÎuæñmRžY[FëC`_O°}°—/þ¹`b?ÿòÞÌɇŸO´Ï€q{9`CÙ0ÒÂ^dY¶·¿mùÄíâæ—œ8ÙËO”ò»²«ø%޵w )‹Ž±ú„š«ôƒK÷gP ôXy6㿞¸\v!£úÔ¨gF{Sêc]>&LÆ €+FÎI˪ºVº¥l ¸>ZhÐߟ¶üàï“þ)¨@|qݱ¥7ÎrYï zy“ÔMÂáÒÆ~ù1õj’´4åõ´äË×ûÛ[[>U€G<–|iÕ±Ý7þû U‡ü¢úüÂ=†¡¹\Ò…-ŸrO?+Áúaíöû××Çá™2ÕCgÀ//mŸsXÚô]žÊ?¿Ûíyá§é‚r7øädkëù»·ßÐØ•½Ës º~CoyŽ~Øúx·ç3-Rž6Ú ú ‰Œaè«­{ö|ýÎìÙKxÿÞì/¶uσ×/®+‚0Y™™Ë9Z´îo€Ók¹äË;†ÎrLJÔ<¿HÿN¼ XÖí§0à=¯š2‰8ŸäùfYÖE‡ xj0¸»o·ýG.*?uî܉ƒï¥)ýDFÆû/TÏe½}¢ålÒ7‘ X†ù51~ÉÕ®À„I0 ó¬É|øIM)ï>Ø’Á·ÉKŸq}!×d,A~ÇÂuÐj—S€.¿…Bsɽäέ)Q Â >-Êq8OÒ5Ÿ'®[°+´¿D®Ž[­9RŸRa¾ÆÌ˜¢@»„ ¹2m³*2ªV +Æ«~S«a¿ô"~ãT3ÆÎÇÛgÀ¸½œ°±¿ìii/²¿,Û‡Ùß¾|ìþ‹ IÇÚš-°¸S¹BõŠ=Àý­(êýG\(Ïz<à׫(U¸¶Pßl´7­>Öå3aÂd¬8;¯9_¯Å=4}”Šê¶õÿæ“´#r§ÉYo¸í¿Ò€kßß@iÏ+îeÞäkÅÏUUÒ §Í™…rÛ•ÌÀàx•“S—>c¢¯‘—V„ÇpÁ2éà§få—=ùkɪ”®o—X–SŠ›7·ÛãYëñ|±>_ b坨%}â‘G+,Öt{þuð?Ê;A-0Zž6ÚKòjÑ‹$°"˳£@¼²È¿AຠÀP¾\Œp¨ÅJÛèòàZö7À\yÑîñ™ùMjž[cÿN<°uûi Z y>9ˆùR­×ëµ<ÀÞ¬‹:ëƒ.~ÉIO­Ív &L€SwÖÁ|ÃJÄ+\G$2„ºqÝ»R[ªŠ7íXE$Ëݲã.¬ï\Œ/»Ä®ƒUîâaÃå‘ú”ߨ*>ˆ- 9ðü½™JìGêCàMU% r¯Û„Ù/ÅÜ /¢ñ|¬}¶ŒÙkØØнÈþBÛ‡Ùµ¿ƒò1‰(öQ|ÐÙ›.¿ÕÄ]#U%¾MÃ{s\N˜,Ïz< ×‹¬Ônó‚M4F˜Ç|2¨Sócãww¾÷?˜¡ö¶ú+i9< 0(¯à…ªñÊôoÉÑ>^#Ô}ñKcQù/œ&9Mþù;àO `Ÿ¯-÷ý™2p°þã¹|¿xï†àº×‰iVi˜ûï™@€TT€gb¡‘ìw~´@€×þáo¿?ó³_Ÿø÷Ã:G ‰æ©|}<8lÖeÅeDaywüçïøû£ Ç/Î IDATf~-v@€A%ÿ>ð é ¼JG8ðà €`@€0†`Œ€` È€ @€0Æ[ @€1Þ+-À©ñ;§Nݘ*r@njMlsKI<¶ù§«£•[æFZ@Næ:¥ÄHö´gÞúÖÐáâïh-Ÿ½F’ ¡OK«90û¯2åÀ*àmÜð±£kƒí½p¡Ô¿üÓ5ýaø@ e÷Ñ*ŠàþF¹«9ŸŠ,NË*ÇI¢­W6pƒG¥ŽM.éµ+Àñ˜Üµž}¢aNÒÚk»Ð7ïÜ8]šså³÷_ŽÛê_f.±|K{Àšà+ÚØÚ÷„UàxRîX­üþó½Á‡‡jþiüe°\n»“NÍ)rß_/]šV²E p{Òê{|zí pSkèxUýÙ…±©BíÛÑÈøér˜/Ÿ½8Zn°Œú—[€K,¿Ø·«B€SÝòls19tŽ’ɨ»jøU:}¿¢7h?»ûïÆŠ¢ ï>ù@±œZœ–¦G®(¡¬šhû’¾|ë”ANb¦‰mrË`h/»Î•O¯aö7Ìû9cs{ë% 0_>ÿ¦"‘Ï_n.±|0`M päÖ=õ¿ÉFæëåÔ ìB ™OÂïC€+À2ÖŽÞÛgºm<ʲ‹±"Ì·ÒÇ]䌲Å2ßɧװóô+®sß% °GùE pe([ÿA€kR€™q8qÁHdb¡¸` À–!À#Oš#¦§ÈÃá=£^`D.a±>ý$ÀáÉuËk©ìU>V‘2JÈàöÖPv P9¤¨ÜšÜ~P0Ï«ÊJö]p‹‰€n»4­Ð-Å{¶êÏuŽd¯¤NMÇB#’ƒ@yæçŽ_êTò/×vË'ýZjù|Úç&·O<ž8-¾žcW«º;z/[¿£Ò–uE °µþÖú Þ?[¾àþ¼»–gÏOß+iþc³uôŸ¶ÿlåÛÛÓV¿ÚÉyâñÄì&¡»õÏŽF9¿:¼éàrlÀ p䊴ù;}©U·´eªjx׋ô3_ðƒ×KéôÒçÚç÷O^“ÇGš…œO„_§ßl²ç¿™N_ó5|±”^º-Œœµá·»¯ùj3/Ÿ>}®ÞmÃY5èÕU0€Ò8`àþX߬è’öG¡¬Ë€\D¬ZsKf¶E j¬ç.¯‹Ø+?Oƒ~}Ÿv¦ZíyfÓáɤ–óSáõ^© † ¢Êw31ÅèWöúsõµé§µ|ov/ϖߟ1š×a »µýß7XúO(À–ö´Õ¯-§Å¸–ÛìíåÑ?¤m†6êwÊ(][ñ¨zŽ7Ê=ÆR«¡Ù¶ªàb¼iC€ëµDzéŽ_}Õäy/ø*½¤Û0#Àfþ0à /˜â<ü’:ïîôõ&0€ pdFˆLæ:Ç&؈WmÝÒåfç¹aP¶„eâÓ¬?Uä®Ù…hÏEIØsÝÒГ¢A'ë¨à½0[/¡õÊo¿^ e¯G§rRß6C˜•¾‰ý·byfÓñNytvalò‘,¾ž'÷š‚×2h ÛqAœ¯G ¾¶ò¹ûÛØ£<[þôýGǦîL¬+ ÿì‚Îõ_¾­ýùú5µÊ}4Æõ©i‘{ôÏGIã'¯¥WÞ‹Ó‡U-À‘HjübR66Å“¡sªàç¯ÓK›|«+•ÙÁ©–^¨¦kà»uâ`6?à‹$ë¾ )æ¹_(ÀWÿPóàÙ—ÿ8Tó/¿vÊAÓY0€2 0y45§h‡Í2 ´”¡ý.r&fݕʧâDôT³Êè+(—׫‹¦Õ<5êS{H½òÛ¯í­Ó|H("L#±®ëT• fÓDÖºèâZÿð#íðZþzÑßb ؇é’Þ°1ƒËÖ_J¾¾Îœ/—›{”gË߯ô©Þê/¬ÿlúo'À–öäë×ÐMgrÕ»‡ýööòêŸÚ9iÏiŸËÏT·«kdú&zò±&/÷T·/¥ßÌû¢¯4~›N?£ŸÖ^¨ŒF©óFU|â`&?^Jß^ Ü/´×m\ó´æëóäõoò‡ÿÖC€”]€å®œ4zcªm±QÝonN< 8ÈEDÀj4–ÖæVª~….^ î“ô¥ÀbñÊÏCüHóâ0¹~£ö„ÍH†¦æV—#"LÛ鿉m>öÊÏ™{HU"Âdعº¨Öš¦œº\¶hùëË/ÀÜûçê[¢Pž5x²‘\ùøÂùÂúÏÞÞöþã˜mO¾~LˆÚ«€þ‰«3ÃáŒÇYQP LÇØ™‚e|Õ\Ý{€Õo,ýÁ O]mrß&Àâ=ÀL~5 ´Š“² PA¾"É{tÙ‘ô}¿ú·ÒNr°ò;]5f54K(ÀžùmB¦È#Ç ¶ùôcs˜ ¬if§lC^°NzÄR*M€­õ·Õ·Tö.Ëï¾3MC0g×Ò΂ì,Àl{òõlUfÛ«€þ!ÏõE,Àjà@ª[Þ¼?‘d–sUz ’ à¥ÏøÅ5>¦¿Ÿ$?oÝ‹Ù0‚Ësðé‘pðúÎ<³þ·€(À’Ç/Ö`¡²í*oеm¯ùxw ²xE*•?ËŠ>ÓDñlÂÝ.Ô³\³B.ù—[àj*h¯M|ÍHƒ8ý¤YaÝ^óéæù~vZÃk¦íÙwÕÃþ˜øšïöý´¢A\Êñ3çÙ–N^2Æ8ýÔz¾e茕½óƒà†ŸùO—[ýi^:–Ä?X,›bÅý5Q׿¹±Øl³%^P~6ón¸Ês¸oöIæ¸ÁÛ@ÓDeäpôÑL{)ÈÚÂÿ-çÍ7›¼½[C€n¸o¡s•¸‚oÁI|30^z¬­º®¼Ý¶4Y³}måîx3Zaµ¾Ó&0¨ºÊñÎê2‰ËNÔ`âKQz}Šá`¶¡Þª6Z>8*»8.ÇFç_T³òTžÍŸñIòónḶ毯$ß½óˆCyÖKÓóó"a<aàèNþÙ¢èÆTjs™ À›;Nt Õ3À3ׇðÌÝqÞÚµÌâहöRñçœbûG¼¿J½ Q!7_Æ>Ó!èÁáºà„ñÁƘÛYûþ9­ÏÌ“Þð–‡u6˜ë2p¼Ž§¯1'žÀ8‡\€MtèúêÂ!À–Óóyž ò2—çYWÆËÏ[ÿމ¹s/æNLÌ•Ÿ\a(ÏÃÿôàä‰çðc F¡‹êáŒ}ÂÑÓHC&À¸á`´ pÕÅYÑœM^J†:'Íå+f®'XK€ï»ÝE¡Šñ¦X«9ê½Að¤v-mbNš%iòp„ °sS^Ô”Î8ôBÂ&ÀÆË‡H€m|Çc¯ËOý=Q`"Á?¥„¥<ûIá pÍÀèà1K2íaà=± `t ð˜l®ÖàœbïÜWÞà9T©ð(°Ð¬.À¾xÃM·ÇžšÝî&SIŸûmªï»)ž"š›3úÂ;«SÔøÙ¿›'L 0ÓøçÛk¾ÛG4’YÃÛä¤vÞš&µ¯ty…ÁÛ‹ô¡W¨œ«c'351ÇÍëĈáÏ'$üʶÒÅ4ŸÎ3¯Ú "°òí&SV÷éyÛ?Í?&4å§UžÚyv¾)üª<~ß²¾Mk °4>¦¿µ­›"FnÒßšKõû“ÈpÏ¿¨!ÀüþÿÔšU©ùÇ+ýiú¿¯&À%_¥}ë"ûô§¥¥]%ù{'ã†7þ$Üg*ùþNÿƒÇ¾6UóÏWúù½ ̉¦r=âø‹F'sâšÆõ‹ÓK]€(bìùÇþNSJ¥ùÉ%áÝ{D€¬l|+ϸE2ûÁãs´Zþ+žìîÿO‘jyÑO~kp7™áLÄ÷©ÏÃ$ÀÌ[æuS7Fµ‘ ðЏÂÅØ¬‹ÜÔX–ɳT+dš¾ï¾Lsˆ8Eà"ÀÞI¢=íaò–‡/ÔxZk{ç‡^N?•Ç·Þ?ï29Oš_Oá\Àz€æö}{ Ç줙÷Ÿ0Ðap²,©ïDZA$daÉç{„Áqy=? ‘–‹·3ûÛmV+ú -ð"£+«¹¿¯Ç+ޝ+À²ø8÷ä%þE*Œ³?át×vV0Ú²¿õÿìklWÇ=¶e,ã8¶C „˜¤I(\–¼@!¨4 P`¥¶!<"ÕV i¨»éV l€€"¢Ph>° R´bYª¥Ñ¶Ë£[ò‘‡R-ŠØx› Ýí}ÍxæÎµÇã4H»9©õŒ}gæÞ{.™ù͹÷d«×.*|y ÀŸô’ÐNe¿ ‘ð…Õ ‰ìŒð%ªHšð¨´ €Ÿ«ø =(²Š;a¢¬j_ÿ”­’:Œ ¾ý\Åà„LJ€“½¾c[i §&gOW;Á_¬Ÿþ˜L}ÍŽgC…_0ëÑTWœýuûÞ~ÙÜw¢òFýA:!Ä¿‚&Ï|ÕþzÍKàŠzg­l@ `€S[·oI=ÜôÆÂ_b÷áòµö…›ž*Þ.Lƒ¤d@BêñŠ=À¨ÈæÇ=…=ÓdþíkΘ6÷qŸ€Abè`œrÉïîþîìá^ ¯‰ÜV…ð Á%z`vu¡20äjÖyÌÐÍ^Q~Ÿ;?†U-ËÏŸjˆ Àêß‹:çZÏÔ\y(µ|O¸©á¤¬¦ÒþÝóÜpÇ#^I›·)óõYÐ!­É×ãoë¥s§? Ñe•¨¼í/­gvýRŠÀ_õJe§ÏYê¬ †HûÍ`äýýmmÃ/~ÌáxHàQD¼Ãjy½z5à‚‘gÃ/XhhÄ¿‘gsÛÚF`u{tõÏË•û*½¿õµ_Ó_18ÑñÁpÒ×OßDª/ªÆžáAŸïÞð£›Ãÿòù~ôZÆ<òï 2öì¶ö§Ì|:ûsû8'?#÷‚tÞÿ»Q¨ÚM@NM]¼ï³–dD^ø›¿Ÿ?~<¹áA úŸ`´…`—daØÎ\ÁK×ZíÅÖïN2ÀQ0"_ìDc+…ñäAýç0‚Ž^€‹èŠÉôz =Açu"6Î\±«³lÅBOôÒœÊuw“çkÞcf Úó`ØÕpn6F€Ó¨ýïó69×·¯m“<³ Zœð^È]wywàB.åxíñâ0_Ÿìâ펣û€­;_X*k&åÉ®Åsħ@{HV¥÷Ód zØÙå_¿Ÿø2Eî' ¶¸h3‡Ã§@c¾×Ë€&2i´à¯o)Ð+pm#ò‚l9Œld@MìÜ?kÛÃ×zHÊšÂ:÷· ýÚþ°‰ñÁp²×w]Š‘j„³§¥ýѳ@û#_uxðÉÆ$êkz< ™í9:Þ{ð)}¡ÁÛŸÛGö v:ð[jwýx‰ÛäÇ þ9À  ÀKAÒª™¤Iƒ$ @ ÐK`ûò&ëŽóG˯¼jgÌ»øøT»]ZuCöÒ)Јœ<).¡¸°§šíãâöžÂ¾8ëý0#VCË€§/‘²ÈâAzHÞhYV×Òˆžñ»ëݷѳ?úBΛ*ÇÕ­™ÜÊ­‘Tïsç§“p1/DX zbî÷mÊ„V2É3»À™5ƒ®§Cë7`]}¸€Ly¹NA#¼ø¨ yq%`‹n °Ó¶>ƒö¬@úƒ|È+Ýà`¤‘~­`2%z(‡¨™òO£LR °¶=|ý-E´³RBõkËëúKÀfLJ¶Ï“½~¬@Zºï‚}{ú'Y_sãÙ@Ø~´#Äž:ûsûÑáƒì}_<^âöVE‡´~üµM\FÀKïÀ‡÷îÝûyÓè›ÓóÇ €Ñßøç@ `-Û­;¶ÐðÏ4Vù©â…‡ÖZ…÷crC–Hï.¤Ñ¯X²¤ae-pLÆ¡‡v(GgWâ¼0éõî<Ü··JµÌÕæ+ÀàlùVè13îü(Ò.+™µH Àªß]!Iö]!Ô¬õf`\Ê\½|ÙuI0_Ä1nAF=Ξ¥±ÂçÇ‹!»¯/‚¿Õ ‰ À“¿eŸ¥³_</^úÀ @ àh¤•Åv9ÝÑJ+º#—¯µÓŸÊ?¶Ï:iÆ<¬Ê¬àÂjCvuù[63°&G®åˆýñ†ÝŽKl'6àÄ*JÞTÄ)8¹¡ rC™öü&Xï#óõÑO–Ÿ[*KÑ–+Ä–|âö&Àµä2Ð IDAT„]FpÚÖÈ5óláx0 ÀAsLÚ#`º¸º¨r#_^g?1›üzÕä®of ôØêkn<0ö໘ðúäø,ö{ï‹+mçÅ^ÿpƒ&4£ÛíäcÜþŸÇ €gÖ¹»OžMƒÔÛ fÔ;³Ä͈ôæ¼ßÊnÈSå­¸Üǘ-þå=À ­¶X–­°]/`k€5Ž£¢†¬«è{O—ÿÃ^㨃‹flU艢2þü&X¿xŒ¬«.– D¤½®œ§öí:g\vÅ`‡7ÐŽÓeŒ€ÃŠGÇ2À.%ã.ÀéõÒšüD“8ñåuý (|Ÿ'wý„ƒ`¹¾æÆ³ñ±m=1íÏí‹Ö 8^»tHµ•4±¸X ¼ÇÇ €§‡¤õv@À*._kg ¿e¥fÒ{ öFQØ€ûÈs2N$ðãži†ìÙàwËQv×äh`qÝCì‹Ëë,#Ÿ 0¤PeÉ“,ŸôJΣæjÎo€õQ ÇÀºúèÒ É~Rt^/ö ÒÐKóê%€—Õ©|m y€‰Áåð¿oÊLk€±ß÷>;, Àáö;Œ˜¶GW<ŽT¹w°b|y]ÅÊÄÇkI]?á4Hc¯¯¹ñl`hèÄXZÀqÓ A`ÐDàÔ/¬ïœä¦DŸ„4H ½F÷áÉ­ xñÂÞÓ5Ô!\<ù˜!{pÞßáß»b0&ã¾-íˆ À8¿®“å¶í¦Å]ŸîG߯°‘©¡"É“¾ y¹d5næ Â%ózÝš2ÊŸß,ëò€uõYÐ!­É×#^•¥6Üì×z¥ßÉTe]²Ë‘€xT6B>8l†b“k€qàjrÔ€Àÿíó=óÆ`¹=|ýYÛäNЕçû+P&>>tÞÊd®Ÿ¾É)N5ÂÙóg¨¯¹ñl`lÆ¡i “sŒØ–ÓÿZTy€E³?€AÀ$ÒòùÚ X¯Ì@ h|8‰|FŸìFüÝ¥WÉ ¹ücš8ue‰:OCL°ómsc_ax 0ñÞ½µgJóã¾;±Øs¤ŠpÊ%¿ûú_Ïìü´é!8(ÑS?y¢ÆÀ“–vôTÔ‚ÉdzHšñíù¦Ë¶Ú  âÏo€-EÎs­gv^y(µ|oI€O¤¥E—`aFûiÂú Ž}Ì”‹Ü_m9[óÍeiÆm¡EÉ}ýÐÕËÒ¹«rž_W—ßÝýîÙ£ßü§ÑØ~|¾jn[˜Lfï%qD¬¶öÑ{·bp€Åm☤}üað¨«ÿ‰ÏçkŒÀÚöðõg|äd)’ôåùþ¥Éñ¡àd®_‰çaOkÏŸ¡¾æÆ³=ðL€àеýmý—½³mâ¼ø99]"'±Mº-Xئ°ÔZ©hÊ&± ÚnCEÔb1T¡¦]¶Œmd”ÐH-$D-/ãËÚñ¡­F‘¥*Uú6hÔ>&H®:µBm2òa ¥ê¢Ý=Ïù|¶Ï/—ø^~?UµŸóÙ÷Ü=þþùyù „û”œ=ÀF³ozfÿÈ€ÙœA€3^q§¶À-!À gOˆß—Û×Ê5¯–´™Jv[Î`M U–XC_;®š[2 °È›*ÀÆTPñâk耮k™8ù|Rë/п>šw…Ãþ©×+M(ÝÞéóUÝߘ7ì<2[KnÏÔ×Ýýœ£=ô;b̺!vç!ÀQ+qÒ5-³g¾òÕøðw€[J€g/T×=l„ÛebàÕ’¥z¸}²{•ª®{¿Ä#®ø¿7œ"ÀÁÎÆ¦²3foç)¹GëÁ’<XW¢Ïû{ûûªy€SXßöÁþšÞþ }we`cN­ìï|gOåÎ÷Z»ªÑ';:ï”3E¥0ï|ï³.y­á=;ß~.ÈW’?ßu°¢,9õúòï­~Iºl^=ÀÙ8¥>Jà‰ÆòªÂXÜf¼ú€•8f¥q¾[¦Ç/ˆ¨ãïÙÙö™ñ¹z€µèÈØ€±ôè5[vׯõM±ÑÓÕYø‚£+cú{7UÛVŽ~úE¶ŸOrýÍkn Rúù§^/'¡tu¤ °ëãW¼Z9+S‡gr{ ¾îîçí!”ÖX^,syqþÿ©ã¥NñÚsj˜X{8ÜDº‰Ð7ŸS&Js¦AÊ‹B pÖTl·®Of@†ì¬¼ä+}ùÈúÐ  F®™ª8 Z=þÿ™v%4îôŸ[þ½)S'ÀîŽïooyÝWœÕ÷ìí9%L¬=\^½<®Çï.}Ãq;ÀM!À¥×‘Wt^Ê’’ F€¯ž:¾\L%»8<|mÁŒý¿‹‰tH‚‹ÿž60ÎÏ}þMûñååÒ¦H€]ÿ»ùJKsöï&Ús*˜@{¸&¯ëQñj¥Xék3é€{oŸ¨”ëOTÓ6€#À×;õ-ï¹8:_8|yƒµmÚWãô-#Ù•.F¶¯k“{þ®ï¿ý¡¶·¼”{N> ´‡û ˜ßõ˜yè¿{|0 ÀÞ p±X3F€o®÷`¥ãÆ¢Ò±Ñ$¤±cÿ¸ýéoËËÛ¾ü{hªÎ\Ç÷®ó{hüí1¾‹˜ßõðßv«ìi¼å¯ `¼Á¾„tà¶ |ÌTŸÆMÈÚnd@€`&  À0@€`@€‰·0L¼@€`@€`ؽGÚ_ÛµëØ>³4J'šÈ®øŽ9ß|(£Ë`›_“ã/ñÀ~x¡ºz±U žìV¿•ú{ªÈ(´D€ %ÀGWÉ`»îý&ÇøK¼ðD€ë_¬Rm9T«®;{øpwíSŽ—9ÜR¥®ëB€ #À‘Õ§õ`«Çײçã/ñÀŽœìV—­JpðhmÙQ¨ÿµþ¿†¥êê­Òƒ[· "ÀÁWNˆ‡Î…ꎭñ—x à©­'~Rd ð캲-Mö—w¬ÏÖT1$  @l ´ËºÒã/ñÀ~úýÅÁ„Gô§¶žÞúµê ùlÉRû׸wõ±CÍ–ÛËiþûi8<º`ÏUÓ_ ¡ýZß4¤KmŸCY÷ßáMÏìÐ Øi€ü8Œ´¿X'ךL¿0Àd°þlß¡Z‘†pÇA‘¥A.‚U¿½HO.d@î¸ µWŒ†ÖRëé¯éŸ£ÈaS€kú{õW:œxQsù Õúãíù~6] o[óË eþ¼¸'•SÑ…vT¨ãjøòt)´Ã±ámzE>Œ °½Ü1þ£^ÍñdƒÃþy pÃR‘øÙ&§ø‹LŠ««[ŠZí«?¹PmÕÕ÷á:µõXS¤óTÑ™S^pÔìè=½Ó¥ïÊÙµ6æë,À3ïóÝaø²øyå·7Há-5|×&ÀIåÔã"=VQFÂáÝrCxØØ1!ÀöòE³ãW˜ó Ãþ.X7àÅNñ˜V‹dÂßÙuÆçàš:ŸÕ#ûÖz!À¡žþš šôÛóòA“z€…rvœÜþþ;Zü;¹zÕý•b ´.¼³þi”WÖͽסœ‚®­—å\Þ‹Öœ_©¸=_îN/ë¾lÖ¢g |¹:}€|çGÚ_i©c Óâ/ 09,óŠ„ ÆÆöºk—9Ö´d©,z~.Ü!ùÐcÍ N°¹LVTÑ´´·/j.µ(ß$…W …Ö (å¬%­Ä²VRx/›ËrE•ä²Qº¾+éiêþ.ÁЍ2VÁrˆ¿0€ç|´H}ÀÌCx h‡} VÃBO~‘ž.3!9t½ ÅGDÛ{€{C™ß~cqÛâ˜=ÀØ÷O-§ p8³Ø¬ÄÓÙ=©Ü¡[ÏIßÀMàȃêÜõÙâ/ à•—¬©U¿Z4×€×T•uy1$ËÈ„4]‰‹ïP| °2”—/j.ßœ´Á­ÇÂ翉¢å`ç`Æ+Àz¸5âkæø‹ÿŸ½³¢Ìø»Y&»ín»  ¼öPà°¨”Ò6*QV ¼'¯"/‘Íå0¯ X¸XNJm‚¼HËËÞPš `N£ÀQPñ"~4‰Æ@>xæb.äæ™™}™igv»»åð÷ûÐvwž™}æ?Ûù?¿yžy k¨ ŸÚ™[Í@Ú<ÐcÖÞNò/ =Vò°«Þ¬ÿ¡‰ñ$5?g#! ×U‡A þxˆî¿ªÛ°«´Ö½¡XýË÷ÚæÔXxìÚGGV ²à@$úø_WÂs€`H[€Ç=¡výv”`€L pPApRù­'âYâ^ßqgµ±XÍê0¬ÊwËýËzfG€…ôêý¾šù^ÿô‡ëÆýàL€óO„ò>º´ýkuߌ}3 Ž|7räç7o_qïëèc:ëÂüÓË›·ßýZï F€ ×Þ¦vónšÑC›ý99ÿ"À™`1íd õ1GsÆxü¶Ôõö¬›­.÷¯mo©ïñ/«É’{ÕîÞ Ú‹â]_оßaª=8`'À®²Ö°Ö¸pŸ+IC€½‘{ѧ ýô¦ŽÄœô«€4xàpϬö‘nGëý½Æü‹äD€ƒÍ‹Å[þ—'._{ÉÊ3“ų¾ŒÎdU°å‡ëîÿkˆ˜K¼i+À._ó'¡×í(v¥!ÀBiSœöÛ;ç‹]ØåÝuïê·Jñ*W i på+åj~õØÍȆü‹dA€-inh8M¿ÁMÇÚj¬Kfª¢>_\‡‹õ9™#ú›¾Î× x»öÙú ÐÞˆÓ¼Þ@A—¯t*Àîyëv¶à ¢À"¿Û–øfbþE€r$ÀÚ½ÁNJýNãŠü€½+H²ícå_" Uv!èX€å”˜| € À€ À0 € À0 €“`˜„ € À0 € Àä[&ß À0 À0Œ À°*74‘Í„œæZchcýíõU¯ õ+ÐÞ‰vCÞŸ9–¹#õø¯9ÙÞþÖloêòðóî¿§·fiż¿d¿~]ŒoZË­¨¬»ÖØP×ãûðäúöæ cÜÐÓ;àêM'•ôÚV“;ÞòË­_ç—ÈÝß*,¼õuIæÏÇO‡Æ¾ÑÅó¡=}÷\k K´ 5ž3Ü3QÜÊž8«—‹½g¼ø{Ö[5Yà²%ªfÉzª,­§Ñ–øtÎ_"öÏ­Èÿ²>ÝT…5uíUIê˜ZüG}R¡”‘òŽTÙoßÁþÊ[5¸|§$÷«)°V?¹£úåüø%Ä×.þNÿZkÕvvêÇ;9¾½&K²ÜïòCœ;r ÀÆk¹vÖ¥ëü›ñ|[ôխ«†SEä—B7lWNU€}F^JìÏû,Íó]ò‡œà29¤,À•{z{:àê™åžµí-;=þe5Yàü¡~—÷ZÿZ‹––Ÿ}Þ½¡8šÛŠŠŠ¦ }ÐXø£|®ª{ª „÷eéÅ?0e°ä¾¸ö¯×ŽÆ{\;Þ¾ƒý5”·jp=yP–—ÚêfT€më—óã—_»ø;bÔ I>²ovAÇÛ_ߦºÓ’û\‰ ²-ÀÕ“Dvm©ëíñoÍ‘»vý\x'ñüù®°ð·ªíÛWüwsÆØå@€S>ß™Ïhò¹‡ŠŠ¼|áÀ©WŸÙé=>&ÀÕÇ¢ÔõoNéíÙ§üªœÔÃÿ~–xÚP÷n¥ñî‹)Õú冶ü€[€û—Z¿íŸ7Bòüîéw3 Xjñd°<良·ìd‰#¶Û_[~opl°€Ž×¯¡äþ8~Iñµ¿ÿÍK¡ÏÖ.¾¾5­aȾ·©¿š‡ëã­’óoòmÑ–†óË®›…w”7"ÙÅà5m‚+¡¼Ô?ö§u¾3KvE¿©|× èáY×63žkƒ±Žáž×ÕŸëÔëÐãä3W…;×™\€§«ýÞù+¥~_ÜœRü{-’æé­@À‘Ûí¯ ^ Éržm3L`¥~MÑÖQà¾<~¶ñ·¡l‰Ô4?ϲA›¥Â=· ²+À yxô‹ü›•[޼ÓýÊÉØç4Øòà§Ãú(C]¶¥µýÎó]€Ôxí¥Á™æ‹ÍÁwÊ'*æ;§·GšU­ˆ²–¡3ž}§¤éƒ~÷ì6Ô½¡[ÆpuM€W…Õî{§Ûw°¿v‚Ö÷)©~°<¿Ø™+õÛPp_¿. p`U8owqºŸgÝ 7%|Æé GÜsJ¹‡EþÍÅ,Ðß%ÝœIöu*ÀBIPœ­ %îY-’­…Ï3k‡zaú{µ—£{{–eG€OHKc8W[ îW½ ˜¯ù“Æõ·wxc YL"]ÿ}U|ùé°¤lå± Êª-XÓÚx®ÊU¦üü£y}G’³çš²ÂÅÕ—úˆá·úh«ÀK¡þT“çøš+© ¹õ5?]‘Wñû­º•Tž=]±ñâÄÃQ}3” ”«×B]êŒåÍÛ3s÷æÕ©÷ÆTÿ$!reOIÉ œäøZ p|—µ ï€Ý»Ê-èh¹k“ܼúƈ¯¨ß›íŽŸE|íâoØžy_(/O!óˆØ?uùåÍÿNâ«îÌ"›‹R9‰össþÍx¾Ýò³:ÛÕ¯Q€ršÿ¬ó­1Ÿ&–õû®T¸ço6 pÒù.½|ÝkQ7];€ÿ_îi-ÀãÎú…îV¾âQ¯CWÎð_šáy´&ó(*Z©p‘‚3+k «m‰&uاHÈk.¨SkŒý“f(Êr·ZBà%Jãîzñs~Óúxö†¤µ^„:ÅÊ› PùWÄ$¼²*P>íoYÒg¶ô½sTv‹úÌ{Áë2—7ÔÜbsùäíY5jn*Mš ) °V¥6qDxRà/jQíàø˜¶oÚ_ãñ2•7_תpÓùS!½Jæå"`jÕø&ׯ»_R|í㟴=ÓþÞ'½<ê‚ú…Õ'åJ9¾jtVUЩ+8ÜóL9ÿæ@€½^!À^ƒüg>_˜ò©¡|T€ý7A€œz¾ö-I¸„¾WNê!¦¾ê9ê Õzƒï”ï«™äy.ó³R>¹(ÖW¯â*>\ôøÂN,ÿD(ïŸj¾ 5×„àƒ ÒØ}ÇZ+4ŸR–»ÏmÝ¿~[«¤ p]í÷7¤þu·¯…Äsb’Ö·gÜd©éòÞC5ÇÏþgj=ˆWÂM½[« Ô´¡òåãG¯{NÍÉ¥GÅëš3ßHM_¸Ìåã%¦óšË'oÏ‚]B€¤(Àk¶¥µÆ²IñíD€õý5/+A3_e ÒôAÕ_™–+--ùȲǶ] ‹ø&ׯ»_R|í㟴=Óþ–”¯Œ(üú"éȾ–’voÊñuEu~>wd]€ƒÁêM{Æx ž£ù7óœ‹"E‚c{Rr·pùÏ*ßó©±¼.ÀÿMìN:ߥ—¯ã£™º"ÀÁå«g«¢{žQ¯O¯ž­ÒÞ˪kïN¬´V»ã±×"IoÀׇDjÍ_)ý½³â8ãø­ït^Çq¾Ø1!6A ØórØÜHyQÊ›Óâ Ó¡(‰€ŒÛ@c*G„—”#‰E¤JùÄÇ ¤JU¾ð¡ÍE©*ugfwo÷™¹Û[ŸÏ>’ÿÿ ¶ovvæ™cžýí3ó ?Õ žkœ4ö'8÷öŽŸ©^;°sâ™j¶l–\ïÝÀڜ߉#Iu±kó©çzˆ=§ á¨ÍÔ®Ó—ó„ºÓ׉E¥¤¼ P‘óüB©<©O%vŒ…ë\‹\ØlŒŸr´>iEà=íO´-A>#ö¥õÓþ’ñ’Ê»Æ÷çæ_vÆÇ¶éöoîñ?39ø7 ØLÛW ãç´¯—ýi}R«w§8Úà Z»N¬ýöm_ó»~NóN´ APž<?xïb•ÿ-À–£XÀ À{¼˜ø?i¾ þ””<ÚÅ¿tðX'Æ_G~˜† ‚òàÕ³gŠ37,µïRZU=ÀoLÙ¬bžxîgo}™¿LŠãg¶ñßçZ›-ÖŸžÊÖÁ>-–CK×{ª.åjŒ*‚¨3J”+ð6{ª¹¨””·*Ö×Ï#vRyRŸZ¿n6‘ƒÓúMfΰØ'µˆúØ›ü˜b|ö[ý%ã%•wޝ ²æTåzÆg•ëéçì~£Ûµ)ïl¶ÛWLãGì›Õþ´>Ú_ú0YS­¯=%ìÉFÍ¿}Õ©€ ž¦ð¿I‚åàp2níŽOLzû?:_Hó=)Ϙóï¸@N<m@7–«@Aù°á“7.vp߬IÓX¡"àªV+PÜÌCk†CžÒhzUækúÍr,fU_Æ¢dU­†¥×çÐ8¨"ˆQÌ~¶‘7P¯¼ówûòvÍz7m´tM\*Ïêäâ³7RúòFUyw}04øàðpì|ÂÚs+ÙWàtéx©-=¾ˆaåê6‹äÎtüÓ÷÷à?,ÕGûktPäîJ°ˆÇjúƒo ¾éŠ|žwAf‰(wŸí˜åXö¿à_@”9 õt¾ ó=-ϳ@þÍ ÀþýõèÝ7ÎüA48n:Zb9cÀó»ò+zsä¸nsz#󢆯4Ë6§ ‡lð€ùo0sÇã— 63*½ÞKõ'tWãTDëí3?f6ÖW­éÑëÅ‚¯¹éËç¶ñIy Dޤè+S•åÝõy+ìP$’€‡&l-1¶Xa_ ÀÎþÒñRšk|ùš`¶÷¬v‰ˆ¼ÒÏy|8 À™—@àøù`©>ÚßÈy÷gMµÕß3ÕFIÿöM·Êµ´‚ 0ó¹}åv,‡ÿΚšú?:_Ðùž–7¸um9á߬ì×_³À9å IDATA3_4AåÀ/•Û'þNŸzÑðÇ,!eÓÖB$Áò ÀÍ-Á®?Zºê<—°Ž9d3>è`±Nšý‘;Tz½—ƶeÈ"ì:FÇyE¬s뀦ë•w>s_Î ž“Ês€Švßzó`< ,8ëË[C Àv’)3©°¯ ÀéþÒñRšs|ùž°¯Å…V4ç玀¨àœ’` óøù`©>j€­ˆ7áú·/‚†€K›…¬Vÿ;â`êÿè|Aç{ZžE€ü\®ì×_3Ž^oÄW ‚ Ê€V„Ž[ °Ø1H—Ê·oJ'„ñ°Æ(·›[†¨˜^ï%;\ÇÙeÔ¯¨@ ¼ãÒ”Ø)ªŒ €r¸*—gª°ëóÔ§’YØjžl3d¢§Â¾Î­ ãå høX§æ4:þiÜÖ’õ¤¿<#À.{ÈK -nN?ù¶¯£",† áàÒ—JF”üo1D€]þÎt¾§åùàs-"/Á ØÓ_G’;ÎÞLLA‚ Ê€W—úK:eÉÌ…"!ÇêÙ¡¥Å°Ø˜²rÄ`+õR«®`z}s†M"Q‡ x¼áBŠ‘—ri€’Ë»ëó’ç`©ýypý<“8#‚Äöõ`×xe41¾,Â`‹XÉç±ó‰^1\µëøÀ±ö}Pdã—ß`·=ä$Xà¿Á,ëÛ¾™R!*(Ï2Ý®ÓÿÃ`—ÿ£óïiy¾¸¾=Ý9qPœ“¿®?¡­A,‚ (?vw´º<$ÞG3>XZ Y À¡ˆŠTµmš€éõâàõ3:ÙæTÐy>jU«hKçM- @Èc¡Ê"œ äò¦Â䘚Á°Ôþ@`A+‰õù`£ÁÁì?f(Ra_WýÉxIåiÄ¡¦: Àì~ôóm aµúvMY[D¶¬"?b߬ö—³@“ˆ­t ’ ÀK[Y¿}Û׃AÃÀ†e™6¨ÿé0õt¾æ{R^ƒd̶ѷâƒ`¥¿¦Â9ÀAPþÜ´(ä á†¡g˜™)ÝG% óô¿ÂÁFÞýP~#mø_h›~Sëf±/É¡ÒëùÁã²²ïâZ7÷„Vùµø0–Ü.áõMÙßžÉÍØRûY6½¶êäÛÿÝ’ƒý¥ûÕTës^ŽÛh*Û×U?í//©<_÷¬ïJU«jOlÅ<-xeoÇCíµß§*›íc,:µ8ÆØ7»ýI}RÄ6¶¿ÅyÐpMuô‹M'ß08}Ú×ñb¿† ¨<ÿîE¾ßw÷ kñ¿àø†ôüž€‰ÿ“æ i¾w—780™ÖûËÌlÍw9ùkù ¢õ|ÝæÞ«Â¡Fœ•\Ï‚ðw™šŸð@°ØšYôÚ8uaƒÐÌÚÍêG÷õ› v_çPaR^ªO!¾îù½ÜŒ-µŸÙû¦¸Gï–\ìOïé«Öƒ·Þyû¹ùK²¯«~Ú߸{¼¤òd|—N¶—ï(küAŠH,XÂ/~ej À¢}+ß¾×Ï~/†ñ£öÍjRŸ±­]gf›¶^GðS™´Þ?Äa_Ç{‰åÏ`þ„ Âð„B+ï;ÖQš¹ÏÎA9©àÌdE³G€‰ÿ“ç :߇]å-fóМ-™Øžïrñײƶe|9 A€À¥MŸ,dŸ­Ü»¸´(8ùÓžD÷­í'T¬7zº¯o|Š¥Ïx_ñF™\/žeà@¸â“{ì‚g÷òVÎgõï‹g:F§âì¡ï{Ñ.Ö¡é—RÝ·~až‚ãA”ÊKõIÚð¸¬ì¹¾“öó{ܾ×ÓÝõe/¦÷[ÀrëAëG²¯³~¹¿®ñ’Ê»Ç7¶?‘¦´m-Ñ÷ãß9ƒõobÀ`à¼}zðTcqŒµ¯‡ý]õÉýv8<1 ÀÁ.fýŸ5ƾv¯Î%*† BpÃÖYÜõŽúëÅÒ`°Ûÿ)æ :ß»ý¥ ÀXs¿ß€½üµ¤ªV,W ‚üp.j8»k×éi…sÈ>ó}Ê<>ÉpÛC,%o|Žðö­ éøå×ßx}»VùEÜ`¾N!Èç~£¿J‘V‚†€ƒk·ªVðá]w>›+.L+Sæûã°.æ³þºÍ•Wñ]ƒ ‚†€‡Ý!»î?R$L~rN&Gþ~Éd¤x û1ÿ/gfîŒsp~ê¼Ð¢g\nA/f[tÏc~JþvˆÅ<‡ ‚ °?mx\öŸ8¾-Ъ4ýÔñUñ¡àÈîŽ-þ… ¡`\@-¬_ɼ@‚ €UºÿïÜ·ÐBPñ©óBŠo5Ì€kY1;ž A¸È5ú\‚g6|¦€ ‚À9*¶áÛ²ðÀ=Ñjè¸×3»¤ëúñÏ`O?)ª8ú}† ‚À~´ç#|S ‡†`p$3Bø‰R$™G&A‚ ü“sȱ$¾(A‚ ‚ÀpÈA† ‚þÏÞÙýFmåax<MFÎÌ$L $,¨„‘&‚ BŸé¶ ŠtùÈÁ†n!°[ ° K….€ ‘¬HZ±•6 `ô?€›­¸âb÷r¥½ÙsŽÇ3ö±ÇÇžd’™ò>ÑÌØ>ãülùÇ>>`2 À @€0ò€`ä- À€`  Àx¼9tïå‰Èáž·³Ù»Âáž?å«Âg"ôwCpÛ»¼ÓXŒ©o}÷Ë%ÓÆ(ZÑócßu9£$À%ÓŠV$àÆ£;räæÝ–Ä'yä“›w³S€+Ê‹WaŽy>9ɇ)—Ÿ>=¹®ÌM}´öõqþ¹Þ>M‘º/‘Ÿ€_¬¯Ÿå[6G}YßêK°w§Ⱥéi äšvXĵÕÑÀÚêåàýÝO ¸©GéF:]&òxºÌÁw¸¾]-P«;So¥f¨“ýNÿý¡p…?™Œc í|e‰šµ?ÖøÔ}/ýàïë ÀÂ<A^•ntž"‰|çÔåýÛÖk§9®O¢}AýRÉ?CýÜnÅ¥#?Ù$Àõ'úìØ0=Œ?ùæÚ?^V#cñùfoJAAÁê™ï”“w«á7õP˜…äÃINn„õžûJ Üè[—Ÿ ûƒr´ûŽ$?˜ƒ ÍܸÜ÷ùÓË—»'úrO«ŸLE?!¼l'X˜‡îó*°}²[Öç“(HxzßüaÉÿtšDõѵ/ª_ ùg¨_º¯#?Ù#À÷þê›·$.¸G4º½ôCnz:yõLù9ø+Úûðù©{ÇØM=J7ʇ®];p–¤Pð¹Ã/,g&ùÔŸ3çýAÙ?Ô@‚Ò-Ày¬¯sÞ©YÚéæ=Ì|¯ÔjF ä¡K.ÞæR€¹|²Ï‡êNyÓqfÂgËÖGß¾“ú•Ž¡»½Œüdç]ñú¾¸û‘7.¸yñ í¾ƒÓÓÈ]ÍöÇØw]€ÝÔƒ °ŸÊãê™M“œ}á8 pM‡TüC~&í¡gQ¹m€´ °.‡ç£/}šû],~?€;ÊC—ë?íX€Mùd—ÊnI /¿ßY} ígš{Ü 0ò-üùã9y‚›w¾–ž‰N:}ôY¹-YÞ.N]€I-Ôúze¸‡»šƒÊ2k °ŸãðÎYY›Ëxê|U„ë÷@€Ó"À²<æF€Mùd›SÚߟl®±ý_À`ä' ;8§‘Ê®…டÿñ9»é£(ÀƒRBÕªØ/yŸ?ù_9õ‡{Þžc³­‘±Aƒ{Þ4$¦ßi¦ãPT.%‹VnÞ?tâAƒ§†ü}ϼ¼“õ3ÌO®-_9õ)ùŠ »,Þ»ä–HáO"%KÍå ÖL׫̼þ,Y½žO^ßxýÔ8»ø’4ðhïãIæö,¸þþèáGË®«M ë_µ%°}F¦í…í €Ñà¼+Þé/Øà÷};Ô[Õ+Á'ÀÊ©Yw¢ÁžGoØ(‹|~Y勉ÿüüÏU~Ç_h—‡®ó½+R÷Z*™­`]^),=+úÙ?UÑß–o‘O¶ùê*7Û仹>ÆöÔÏ>¯ÛÇR€íÛç½&Àʳ(íÛíp{#?Y À9Ö‚»è~î»é£Èá‚‚ÝDx ΄§f¨™ý–èe݆¨ðìfC#Ö}¨*™.³9bÂÓAîmè_Ö'ظ¼n~*–¡gtÐÆ@\€õïS`? ÒZºº) 0É÷çëOkî0¦òAƒe{†úQ¿RGµ ¨kjlÏ$ÀÊùþ€L—ßð¿ÇAýÃgš‹ʸýÁß â6`Lxê,ß-Z_èÇ99—¼¿š“‰¬¨YÔ+\~Yæ Ï_~ž0aÂRGß&ÊC×ùÞ©¼Ü¤‹ÇØWáÛÒ¦2êvò?wŸ9Ÿù œ‰ÈúÑ'…õáÚwR?Û¼6nSýÌí ò>&À1ÿuº½‘Ÿ€¬àúåÞ¾9c!À ÚãÇgfnÄ ª¶ØOh0|øâÚ©a©÷{Ux~–êúnEÕ+ždºüàô·ÎIªðtw¾y%•tGß¾ŒÐq¹å…ðóSá}ÖÜ»ìæ¥Î¸ëß§*ÀakÖ#‰×tHêyenýI`l—nô]~%ÅoÓáë««aÑ ©÷É7×Zîÿw•¹=“W÷nœþ¶åÞk©÷'ƒú×\•ô}Æ2e(ÜâøjHU€óò^œô¹¾Õ7oÇÙKµ{3ó1H«gÒãû9Ý¿žì±¼ÂiÊž¯©oNM€¹UŽQ ³§l-Ю€ ëa-À‹†#•Z­Eyð+PÕ©î+s{acýȺ*ÕýV¦˜ÛëŠ$–¶Rµ%°– 9w í%®éLv¶=ÓöePJáÞ'p#À‹Þg>Ùÿ Õë«õ~±.'#¸+¢Š¥ß:¿¬ò…ç³M˜ðï2g_'ÈC×ùN¸¢œ>e@`.¯*úIE+zúÉÁ_é Îå“0üS_Kâx`a}¸öÔÏ6¯¹íÃ×ÏÔ¾(4ÿu¼½‘Ÿ€,àõóç}—3&ìa'‡k²ž) U¯¡g iÞ­ È›&kÑF*vó‰&<’Ùôá¸%j÷WÓò"LóÓsªÔwulxo„ìN°9¹'¹ؽÇìQëàÄ­Ey@í¢Eꣵ¢o« ȨáËùöxîŠwèb Äõojæ‡8ÉŒýÁ¼^&¬%lãù‰>_L`C`óAs¾˜ùúw~‡ù(ÈC×ùNS!ÄFjV˜Ë«Å{w'|ØN^/h§ $ËäùP?ÜL~,sV®'õ³ÍknûxL÷Ûå=`æ¿“Ýmoä' +˜ÄÑ’™¿Ç•™é©N"<• ±#.=åXѯŽñÚTÑz–µt# 4~y¦ù‰ðª ³xþô‹÷© °tX ~ýI`©g¼Ãýò>s{\ýôu°lvf§— Xh¥CÒ†Ò [¦-_X’£üyƒÌØ*Êåc8€Ò*ÀTyt×j} ë/ÕÎ;ÙêõõÍÉH&SüäEÒü²Êa¾Ó| °Ë|g§EWÏ”•1æóª°=¸-üIðùn©­¬hÍöVù$ÌÿÊ×R@VŸ„+ªß¾“úÙå5¿},ØÐ¾(ïÙ(Ð ÿu¾½‘Ÿ€làÆK^톤ŒàêNÝÇ­¬ËklÞ¦hÝW4Obq4ᡇó¢5¬3 H~y¦ù‰ðj]«Â‹÷¦4Ô¡(ÉxԮˇ B·#$á-׿¢\[öV¹Öª®=¾~žš«Æ§)ñíq÷/H̾ ¼Ö_¹mê*•ûÃjë®o0ª»ìËFÁjüÈ;ï4±`kÿÿ(|¾\ ÜqÜ:¿¬òÅA> Øe¾3 Jv1æó*Ô!°îˮ撥·ÊÛʬòIœþÐïI‘Ö68¨ß¾“úÙå5¿}Ìlh_˜÷ž¦È†‰ ÿu¾½‘Ÿ€là•ÕGf¢75Ë=Öø>öØU…¨ð¶bgt5áQûÅÒY@òË‹0ÍÏ{¤›ï’t°ŸÞ;ýͯâ9€‰S´ºöøúy ÛƒYðíq¬›Fé aýG*ÀiÛà€1àœÆå¾éëXøæžf×½±a¡3í1Háý{îH@ñ“ëÖBdΗb/À.ó]½1†¤d`>¯”A©^ù-ÝX¼ª:zÁŸšæ«ƒB‰êc`qýìòšß>ž¤Ïfí óž^^BxFRN²¾ÈO@ p}«¯¯%'c¯åžŽØ-@4Y¬„‡_^„i~—ü™ž&ë;zìiŠÆNäò러¦¨öJ¨\ýèÏà ˜|{Â+À‚ú´ tÚötáàÿì}lçÇïb×ñ.Ží³Åk€2h‘&DmÒB6Jx)Z¡À@h#*¢–uKJ²4IE¨x @¼TSHP”$K‹h$^BìŸü1MH¨š”?€?(šØ&íž{qîž»øÎö99—ïçŸØÇùñãß÷½Ï½=`¤8syFV›¾RÔ}èžz‰Ì0žºË×"ò“/èü2Ê—X˜åc¼l–ï’s—B­ÛŒÎ3B¿;²ýSOá›®cÉ@>‰\-ur³øP(³úè/6¯_¬¼–öI†–Ïð,¶oš÷â=ÀÊ¢C;Y^ÞÈO@ ðûc³þ–éTOs`1*<œ"<òݯ+7x„‡þ¼ºùã`Ó{œ¢é$=ÈÃ..|“•Æá£û ,rìw“Q jêGfÓ4Ý%Àú{€ÍêŸäC°R¶>à!€‘àù$v§,pïW®ÃR^9M€&,ꊈ§ éü2Ê—$pœù. 0³¸ÜÕ7à ¯„›Ô+L÷_ mÝ"^DÿC°ä£;CdÓúè‚e^¿XyM/ŸaXjß4ïÅ{€ ·„|ò-U–—7ò~,¼¡‡ tÖS ¥§ÒÂ#Ÿñ»$_û;{3k(<ôçE*vì.4uóÛ,Àòh„LÍuVàŨc§ 0y8ŤEýÖJaÆ >P©ú‘»®uyÃ׃X÷h³ú뇙ýõÁ0€àÀ™Œ5'ÈÑgE{‡TØy,(fî7úü2Ê—$8VZÞžSìßò… òJhpýCéበſqƒ¥ B д>ºaÌë3¯ÿ–—]?ª}Ó¼—†AzïûSÐhe¸þ"?i(ÀÅï¸iÛu“që䃑ܮ=º3~dûM|νÎ6#’º€¤?OhyÎó/† kzþ$îæ ò1¡\ÌÙ×}ÍJ`EoÃᬌƒKæ;úÃËE”sä9XA}ÿ•À/Pš{jµõÇ̹'ý@±?T{´ëÆ6«á—¬öš«Ñ_¤ïôZ&’àÒ×ÅØ´Wºÿ(pd~V›óxâEyÓœ-]TûFõ£Û•×ôò¡ëG·oš÷¢3sS=Œõåü8_€ÅDp…¿™òÍGjÛÕýûˆð±p8ìQoð•Ü™ì !_ßÍÓu»ªÁ¡Îø‘[o|}{{»ÙŽ^²åÖ$õyÂÀ ÏóUÃôŽžßæ‡`MÜÂæÜ>WÝíZwGy–ா«kÛÞúO•…zÂówsµL’]¼–‰êþ4ß•Êöe·ºå@£Û£ê'0e™Òs®qV÷ }{´3ÞŽý§ë.?d[ÿÎX¨¿ÿ@5pᨯÒŽ…t 9¤J€Kû{DßmZ!>yRH_qàÀòníP Îà76·þx³²½î`WD4!:¿Œò…FWVöbä¡¥í¹úûbÇkWäX1@…’ NŸOº|P·?{Á¶Ób,}Ô)žI5­Õ¾QýèzÅÊkzùÐõ£Û7Í{I€™ÒUlë ÆúòF~/À3î(oËÏÀúåœÿ>Lö†® šÈûïæbûH¥O™å^ÓòdõX÷¼ýÊ•ÏîOúOÎp¯isà0H…_Êaà’6—t~å :¯¬#-mÏÕß§°h~ú¼"á#^-):’¼Uw X“êö…í»1ïÏKºåaÌë£mߨ~t½bå5½|èúÑí›æ½,Àd¾…U–—7ò¶_€nßµæPÃÚ¶œ/FkÖw=ò”}½RâÖžÈtâ8ÀÏþwsÈWO6©ù% ‘6_hÔyeí;‡ÍCkù®ú¾¨“{~õy%¬»[ÒÏóI—êö »:%}̹zÖZ}´íÕ®W̼¦—U?}û&y/ 0ã/锞+Mÿýþòœ$£ñ£üq/çýy?Óò˜ÿÀÆïKžp<ßé©xö¿që%ݳãñP5Åú ·°9W‚Z˜ì;qGHR€]ëkO3à€ ÀçÏTMkº(LèÉ 8R€e¸˜ù;_Lò*þ<4Ùž'ð}±ó‰ÊMû®©±¿¿ÿæi«õ1Ê?mýé?µ|¨ú%ÿã%`¸?ü@€Ó…A~Swß[uhÜ>ç@ž°q{¼súSÓUæU] 0¹DÔk: ’ÎŒ¾yG^9áûè|Ò僶ý ÇŸ\û£^¯80ÚŸ@~ ÀéBÅsþiðÕX#œ(ÀÌ⬷ãÔZG,®©º›u!¿v °7^þypN3Ôù”Š|pRþ%»?ü@€Ó‡'ñÝ’¶›š®ˆx«Üè÷dv9ëõ:ê|4 vB>¥&œ“ÉîO ?à´Á_ñŒùÊl°)À ST}¿Ù;ܘòú¾Sg±å@€6Ÿ&¤(’ÉîO ?àt¢åЫ³F8U€çG ` Ç|ˆ¹?ü@€Ó%ÐÂXQ@€0€`2 À @€0ò€`ä- À€`  À8&¦óçzRÈ-Ï_5S*žý4ÈóƒßÿÊ‘Uó¯¯ÿ*¿ôo|ðÖW±?1ñøýæ2Ö÷ÖHx¥xRî„òa¸¸é"‰×eª¼&¨Þ;N€íÌëüi9+lÏçëõŸ´¿@Šªï7OÊM >%¡…»G°¿ÊëûN¥& òU#¸|TíÀ5²² IDATù„ȳèžý~ׂÈú*ìà|~–{ééeÑj÷Wff6ÝZ’A^gÔžHQ ‡…íåÝ\Mž>ç%v§¦,BóO=‰¼ä÷¯ý3O~mI€¹ ›a¯´ŸY"eíš›ËdýíóÖ0n!À¶æµ \SÝ¿ˆRï»ÓllÂ*²˜«Û3Šý)ìÚ.®6ñׇ‹O€w†Ô×'øîÅÝß1å¬×›s{¼fâÑÇê5ÁÎåSq“÷ܸ!¹4ªö8Jx9#Nfý°E€±?€c¸èøX÷°xÏõIÿÉ“‡Ýî©'RÈGŸðOóÔyúˆç_.:thÇ÷¤¦,Bû/’øxéoýCñg.¯ü£‰¯œáý¦­=ö`°|þ÷7‰×ê±î¬ýÊÁèyß‘÷kÚœzØÎ¼¶A€WÎðýA3añ*×§yI °×˲¿µþÌ}Àz;N­ 2)?l(Àqõ—kªîf]ßDÏý·ü0“ð&fà§éö/•»¾±Ù+—FÝž¸?6cH-é÷É®¶0öpˆ_>ìž·$*ÀÅçª3„‰ã{+Éqéâæ»ßNQ ‡[v5û˜*L¨¨HÑ¢"If2ç¨;æŸl&À%û¯óÖ8/Þ[Ø7K¼Þ*³ôu÷ÒJɃk+*Àvæu*Ø_WL´1A€×µ·×íêíôvÌ¥þþëSΩ¦ø pMáNÈwE|q:‘þr5]e^Å€ýdßêè㻹ùÛ— ÀJ=ò;•—t{“)á¥ß'³~Ø"ÀØÀ8“á®íy/CàÌ@ôÄðj÷_Ä åmt»ÙÔ$ç@Ê®}–6Òv^}b*ÀÛs¾ÅÊ`£mœÉ˜×&þùx­8aùX)~ø,ó:œ D€…Çq®uy£ÒŸÂ-lΕ`¢õIäà’2¹Ç žpȾq­'¯ OxþÿìlÇÇo}öyµ¾;Ÿïdcl@Æ”¨8¶eƒ}þ!c'Ä—mŒ 6á\Ù¸YAv©k£F×)¿Š“@IHůÊPa·¡Ð„PaÿTEP¥ ‰D¡ ©3³»w;³³·{ç;r–Þ÷¸½;ï̽Yí{Ÿ}3on?̸‘qõ©'æãƒØ>@N+DÓ˜ð³Q†xJþå¥ât ?ØW¶DûºrSòŠŸC¾Ç,1НáéLݯÁÅ@À<5–¥í%™•yV%óã÷À9üu‚0‚º“B”Õ¤&Ùww½ó½¼hí#M €£•ë¤7W‰p<ŽGßáåà·ŸæÅa|‹3ãW™­â”`ˆA ÄàÔj »^UN/Bª[—'Òo“»%]“ 9Ôìp÷øá ƒ[û¿Ù+ÿIÎB±É# ¯¯È ‡=6`«^eúï^ø£ÍÕøõÍ›ÿþ9~‡½ú("Îl³Àzæg€g_'^vgðó+û€#÷×Òð¼q¿³ÿ⃘ˆFU s¯ñb’!€ç:}c°ÿ²…ÄSà,$†¶ov¨¨Q°¶wlðl•­ýûCNü`À¶9Ç0¥°ýåÄè|èµZ°Ø´?X•gÆý[/.9¢´gß.ÿþJ¥¨ÂV±c.ý€€¶ÛžÍõþ ôƒ.¾q 'aU–®øí-;¬Ù[ÀQÚ/³MX¡>yûÞCt…<Ùñ)òo­Ïý’¼êB¯~̳oXÖŸ¾>yíSö5?›mº8œýƒ^ÃøâA(A8•ÀugÒ:)N>4ûÕgÀv¨,î_”ŽÕ“XbHž&„’ë .š(X{ÌQû£È¸÷k̼/|Œ ¸0–BG6\ ¬×¬yÉ/6`NÛ\¤¾Ll6ñ×’ì‹EA®^2GÈJ&0Ήg«Â7ÝþèþZö=äíCj™Æ¬:{Sþ ¸°O¶eû«/rÐùìäŒHÍúƒì±ï˜hÇß_ýS‡P½¤UÅO¨îR/À´}¨ö°ÝäöHOVø×š½CìŽÒ~Žn¿œÔ”Fþb=9ûñ)ò½+â©éÒFûùüÈØ­úúä´OÛ×lüÈ ÉDã*Ђ@‰ À•K“Ó¢9î8Ù×ÞŽœjСwTÈ0تÈuÒë=@ °Æ·¦Wo{¿\®}U2?Yyî<«<™3åêûÿE‘úëeωGw~ÐW|¢çGÙ6nø×mÂу¸#—‘áoF†9ÿܪ¬ž®w„=þonxñ¾Blü`À¥‡ð ¹`m|Îg?‹~àî1ÀLÂÃöh8}WúR¨O&„ŠƒÇÇüìP³‚v}èöê^†.ï?ÜpâÌC¬ò¯5{k2ÀÑÚ/³UÆW\`ôö·÷¯~û]®Œëñ)ò¯@¿{-Þxè üÓtö Àúï3×§¾}ƾfã‡,/í7šÉ‰ ØÀ9A^˜Š/ŠºH÷mî]ÄÇ›ôOp^A &—´’Iàú½U]̨ÇÁnefìôg«9$Tü\ 3|*»Tþµfof p4ö“Ô%Ó#÷çÜÏXÜ~ïáÚØO‘¿ã4™š^ëï8¡þ´ÌX÷}æúÔµÏØ×tüFŸ³w©›¹jSˆà¯`‘ŠIË1+ÀÒņŒÇ¨V(¾6ªAkœÙÄgŒVsåmt`â0e]{šÊªõV¼„x®!s쀭ÚO À’ÇãˆÏø`ÎY(œ½ã¼f‹ 3×'Û>c_³ñÓ$ðÃ0ă PÂpåÊäƒàµÎ˜¿ 0³R(¹UÝ´®]+_Xòõ˜ðÀ#?æ«1)m/uœEOÑ 0’£ïô¿²r6H-ˆôh «õçrö&ùŠÚSvÒ¬‹ ¡Í€C×_€Ê’Qüy”` €õS )ûèÚC¦--¯>Y/¯óµjïØÐ~Üç1 À¶îz;®#FÒ\ŸlûŒ}ÍÆOóü¢›_âA(¡xUVš¦ÆáâÔÔDÉÏl¦Š$FÀÒÈÿžNSQØt ð$X¾‹ MPô€ù\¦u»©Õ?K¢6b˜²`,§rÌOrgA’ …€ ç69E©|Ò-3ްtñƒ!Ë(ÄöWÀJ¯—5‹Ñ­VJ’X_‹²®=ô6ÅYd piÀëÈ£;ÎÞ°¡ý˜sÄi|/Z.â-‡bÀšë“mŸ±¯Ùø¹Oy‡ä¯¶ †E° €Ó)Ï›¾/iö«©© “ÎY¨TAœÛ$ŸÏñ,ö}@ `#¥˜ôÊ~í èuʚ੟&ˆ·‰!”#ÃHï„@Ù²fûÏo»%Ÿ /jfráK?pN€Û_€O)ÛŶ *‡ë§Š0ÖoƒDÙGß^­ßÞ”§Q¼ ê­s³Gæ^ öŽ€ìÜ)¾ãCذcÀ¡ëS×>c_³ñëöâwãö¦W/M¦çC¿Øš8`¼žòpSÚ²#Jöìà=e”øE°€A ?®›¯u»©•Kižªàœ?+¹äÉ»Ó_&ÜU8ᤦä" 3(Ir…EààºLÉaXºø¿ùêIÀx'! ¢lÙøñn¼dBتfTÃõ‡·,.=$hçܲöѵWsHȽ&wˆ´GØVr½‹-Ù;æÙOþÑhgŸXŽ`›Û‡ë›Æ€ÙëS×>c_³ñÍñÛ½o=0”蜎Tý/»ÞMZO\·.¹s‹ª†ø°§Ý¼‡…`×I¯óü¥ú¶ôÜ%#~ô0ã±ÃZߌX³¯;`íFïz:²áj@ ` ÷~ñ¹öóÊ$¹òä0Ù¡ò@YZgj°U]Ó6ôàÒ†Ã}»Çü„ÄrBîåã=ãö¦+jUâO6nø|\6*gsK:åý?{wÛÄ}ÀqÜv.Nd'΃B“†‚ª²’ˆ‡¤óìAxH•„´òPê <¬C ”.e[š†t)Tžˆ[¡„Ç>ŒŠB/JÅ‹H[™Z¤I™x±•¦I»ÿÝù|¶Ï‰iœÌ>?/ŠÏ¹ãœòûÿî1ï£G¶}úŸ5zÁÙåUòŠ-XÑãõïSº(ˆ\€÷ößw£X)21Ë=¾(YjÏ»´õÂiûÑ ÅZ£hyä‚u,÷h÷WÏ}eïüÜf²ØóVñÆ´Ñë'v~å‹Ä3½ÇÛ'œ~N/À¶ÚïÛ;Ÿ³%¸¾c ð#­?uC€ºçs˜¿µ«Œ8¼~µÂkEOßýû;ÿ¨õ;È÷W:ÍžuqkëWöŸü4þ}€©Q€ :$ÝLmŸïøˆ¬3Ù$¬@B[•9§hò©:u0‘uñéoV€ßxP”ÀA[àláP¸+Ó_TÄû!%KÍïdl.Ÿ Í¿¶o_«OšÚ­æ±{ãµ}­³%ó+O¦JN8¯'ÿÆ®¥µ×ÎqO*w˜ùh”~_ZÁnïüÅ@ éR+ð‹æñú‰:N½`)3;p•‚å2¬èñƒM;êWzK#ÿ碲Ó1zy£Ç…êë¹GŸªl Ý·v€å‘ ÖÎcêìþÐi3Ù,ö1†K^Ìú‰Ÿ³üKm1`ñ¾ï¬Ip}ÇàG[j/är¿Ÿ8ذ~•«|^ß‘=m|ôïgìü£Öï`ß߬9Ê»?]¿3(Àqu°sŠ\]—;Š7_ÙÔSaûfXNì¢&øPR pÙ¢¸›G™Y€ý몕luÿ±7"7^mÌI“F)œücû˜ }ýĬR3ê9ÀøóYÊ Ó_Ö8ÙßOò…=É_&ð?ãA€üãôŽìüÌoq°x‡œæz$ çm꼩´8â V¦sDÃ÷Nž §êr ‡C[þûa<P€ äD=ÿdîEÃC€Œ)À¹`$lÖ—öÜ£û¦Cvmi=mÏÖþËx8]åŸ,VÎ,z‰U`òñl8õŒrêxêà‰âšOö¿šñ pš+Ýûu‡ð€LÞb@þÖiP€]%sÚ.í‡ï‹ñ @Fü¬ðz¬ “·yÃp°ËÃje<P€ 0`ò 0`ò 0`@€ (ÀP€ S€ @& @& `ÁØò^{ûñÞF9ž’;¿?¬=ô,Éfü޵àÜÇ=ç °ÿ„¸Uä-y ÞÜþ§?ÀI àÌx_p¾4¿…séFoDŠ_Gp¯H@ç/Ô]†cwõ5èm7¸ÿ‘àÀg‹‹Çû‚¡ùÌfà¶ï|U»«BõܪòKÊË–ï3øÊãc½Í¼ªä7Ê~~cP—?ÿÃ@åÓÎåþ÷Fïþêû•ÿ9Úòëo `€Äpq"œX\<TîûŽÞ^ €ËË<¼ü¥Üo.g6E·´tl/Mê—ÿüåË+ï»Ýõƒ/KÏ|Tyâï¾÷å‘—[;¿¢üÍõ蟯ß$€[ÚÞùíOzNþnöÔ@‹HlgŠg÷”÷ÀÛc™$p÷€ð-äKÛ|innîâ{¿¿c[ךs—Úz¶´vtÔ?ÓVªÐÚ'¥¯v´u–µ®ûýå_ÙYûìè×ûƒlx UgË[éþᆿ¯³Õ?T€Xð–§gO?xó^]åŽ8Õ½ÿ^ €o%vß±ÎPþ«_\ï~Ö¿F€¤pI*û58I{ͽ@äÛwô_ÝëòmH`g0àDùâ¯Ý¼Û·@Û[ÀñÖyôïÝÿÑ¿ØÞ€Ž¿·~ê{ €í-àøët¹Ilo@€¶· €@Û[À € ° Ø €l@€0à›x©'{òµkóŽ± àûîßúXLØ^Ÿž|$Ø;XyXØÜ03]ûý#Áî°¸¤ýý. €äðê}­Ûã°½ \8ÛÜ"€ËφÀm½½¯\ZJí|ÝÁ q¼j_WíqÔØ^.~|:Ú³2¸ÅKs5ÇÓ+O^H‡À˶¿Ð~ØÁ i\¿¯kö8’ï¶×löΗ¶wöòÄJëfòu¯J¿Z}8Þ—ë 7€ï}ÜÁ i\·¯ëö8šl¯Øì|âÓÁ|ƒÁÍŸÞ[=ztGnêX˜ünSªH\×ïë†{¥¶×löÎË«Û`p'Gœªþ(x_zÛ{apÛ‹©çr°HV¯Þ×ö8Bl¯Øüœi<¸£Ÿä¦V~<³P3€wÊ>Õå`l”E IDAT¨n°¯`{ @\¸°/}~°v‡†Ü|&ÔÞúÄ–7+’À ö5âl¯ˆiç/ Ï<¹r-¬ócšÀöUÀÿ#€'G†®Tß’4‘žÎ„À÷<š:x—ƒ@‚¸á¾F<€í5ñ àÑÁÔXí Cåka…À->Ü~õó‡ €ÄpÃ}úU í5q àâ¹ô; UR8Üné_Je³î-@R¸ñ¾F=€í5q àê«ÒËÓA½ùyt©Ç œn¼¯‘`{ @ü¸rYކ}%œAî¦ýê½­‰ à+›#€í51 àɾÜJèæ«Ê§h•>„t¬#©ƒw;X$'€ïkÔ/‚e¯ˆ_—>©Ý©îf nƒM àÆûê6Hp§¸¸/X¿¾°½`“pù¬bypkg`•¶øð®CÙ§º,’Àëö8zl¯Øôœ¿PwŽÝÕk`m»³Üñbêyï) ɼ~£ÀöÜ ïö|ëY é¶×lú¾MÍùcnÁ=ˆ]oyzöôƒ7à;»·ö‰n?ì`³.IeãÀö|›:ÞYJí|ÝÁ ~œQÛkðíz©gù$±÷,pdØ^ €›À[N^›ºË±@G:€í5øvµõö¶:Nàh°½@Gå¢ €@ `À €0`À0`ƒ 0` °½ ØÞ€ `ˆ\ßwÿÖÇþ÷οqinîâåpöö¥žìÉ×®Í84àÉG‚½ƒ•‡…ýÁ 3Ó™ü…ôÊ§ÛÆ041€ûG‚Ý•Goü~Ïòâ¦gB à’ö÷»À…³}€;ÀåÕ­p~"Èølqñt<°B·õö¾ri)µóuÇ€DpñãÓÁО•.^š«9ž.=Y à™‹¿¨¸ìhh^˯2WøìÛÓåÅ ×~&Üü½ÝþBûaÇ€$pù'¼³—'ÒµÎäë^•~uùë+_ò`hb÷åúVr7Ÿ¯ÍoƒámVßû¸c@²øÄ§ƒù‰õ•›?3¼wZ@X<º#7ulíÏ{ Ç‚mað»=N Ùœ).¿ñh}åNŽ8•ÀRö¥·½·.€GŸ+Ÿ€ÊÞ¶½˜zþ!Ç€Dp¦q~’›Ê`)€óg†g k¸x.ýÀ“!íí®CÙ§ºðº.ìKŸ¬½G8:ñÙÅA Í àþ‘Ü|fm—ª¸ôlH{»õ‰-o:4àuœ¿0<óäÊE²–x{L@“¸°?8?¶6€óã}¹©1 w4€'G†®T¾377·x|O)Ï `hJ'Ò³Ó™5œßÓ¸›³·÷<š:x—c€^À£;‚Õû;Vøx$¨¼)XÀmðxßЩÌÚžÙ ›´·>Ü~õóG\ÀÅséòÖ^:˜™ÀЄ®½Ô¼*€7îßfímÿR*›u/`p}W_•^w®Vƒg}‹àëðxõ Uó·èßfííèR@¯ àÊe9ÖŸ¬•Î]ÀÐü.lÿs÷o“ö¶ÿ™ö« ô¶:>àg`õ5(ÝL挀æp¾ª| téCõåçÁL¨|Ï‘ÔÁ»p}—>©Þiõ)Ðû‚04!€ÿËÞÝþD‘gv'ìdwaa xˆˆž9Qˆ°®âÁ‰è¡¶øæ4¸jˆwàÚžZ9%AO1žJõÅùøÁÏkržU›jÿ›4½øÊ틦/úªó´ëìÌ,¬®¿ŸÊ:ë8;2~™ßü&6ê*zp Ç5¥Îãló$l `¹t«l~]_.N+%€p"€ÃkÄ¥Õ0°2+¢°6K=xvÛ1eîçÈ@¹X{’Ç àH/Ù,6펪væ|»°QZ›Å±¼Ï¸`˜†c©þ3hÃ¥Þ%sEq×W§*D±ö‘‡À©Ž©uè–£ŒíÂ6îÀƒpøOê"ßo{`þ8íx°xÇð>ðÐ"‡n9þTY“C'äŒÁ‚Çg¿ß»û¼Vp¯rïÖ 7''§fúàñ7‚ÒåÚŠ~Á¿ÿ­ xö AºÜ[—5ö§÷P[¿à¾;“o>p¬œuµŽ,wÉ'Û¶<Ñ×MàT+×wçUÄ8rý`T›KùÃÀ…2_“º0¼Û¡rÍt÷9Þ¼Ñ×™{›³ŒË'Žån ¹ï–ÖþP(øëÛ÷¯ßxÍu„÷§·½¯R¢€ÀIpä‚+Àë·Õߺfèã¯`Þ}äsïžÛŸ¹¢ì ~*½OœåkªvvRŽÖJ}¬nc9€s¥ÕêuÚÙ Ò¶ßjs¶÷²FÓþÌxr¯Ç·x¨^™çËÓØp^žw’À©Þõ¨4`àØ‚ž²ªf'"/jvvVJï-aõä÷5€KBþÃê郟¾Í2[+ýg G×þTaÿ…o?ð¼d®¯i‡)€=+Ë|0°'¢Ä®M×—×*'à%›íæ¾z·|CØÀ;KýŸ‚ûËôÄÁæíú±ó@Ç«“éÉ®?îýãIk³¼]›û+…u¿¶ymç_?ÿùÓô¤?Oø‡þПª.Åþùý!ÇO/» m?{ôj­2p9=mj‘œÁÊ$ÎØmþ*÷amùËú3_:NiZׯ,—w޼–âeò_-ÞÔÞ×ywAÚù×ÜÆ!"`Ox¹kÚ‰Ö+ÀSžÀ8Àû^òƒOóª>¯ö¬£œ™“³]¶YrÁ6§¯Rý¿ÄѸa¿‰™Þ¯oÆSeÒd)ÀÆ×6¾ù9;;{Y²¹ÝsEr»•µýRý ^mÝ’›‰9nã.Æê5·QÚ0N àÙ•Ü”î.°Ù~%PÛªS9Ïÿ…ºÿŽWÆÝ<œâþ´¬?m¼¼Ü­¾Cà-òÖ­Ý«üºa\¢ã‘Þòs ±y6ܧk õ Ý¢\4•x_£p¹÷ü A»Ö´ýr Þ{(Ìï½ÚÒþ朋‚±¬SÝŸ–õËËÝwåp¢Oи­åå aB[èÕó òÜ¥Ç#·AËc €ð¤r_·ÇÀ@äÐwå¢íGìQœ àÀ…²uÑ%'ΕiE:; ¬ÅÓ ÁVÒ¢ÝñšÛ($UXæ÷ËÁÛÑrù£495£÷ÚÆÖdgÿ³0¹Ï3«AZ­N€<»AÜÔÂÐvµ7}ìS{·+…9µÈß©N =«A˜ÿ;ëöËÚÜpDÏí‚úÜ߉êÈéw¶?-ë/iQ7'-óxP à£g²®­ëß_x­¨xSÂãaÛ °]‡×ˆ½ÕÆ^2W} ð±j À°p}ù¼;ú±ªÍµçê‰ðÀ qO3÷o7ܳ:T°_¤uYZæÆ`qWe-ï—ƒWRz×Àq¯mþBßõbðkÖ h HÖ·{¼ÑÄøÞ$o…ä¾?N NåS›·?•äÞ î­Víó,®ôþ÷§yý £7óFXš0SÞji‚6:ññ°nÀÆ)8>síiöXX.àR€á `ù¬?úH`Q=C+Cµl¦Ãö~]Œ‹ƒÉŒ¶¼_^­H?.Ÿ²ÀæµÌô¬´äØ»E¹Œ«’Srm–²RÁãgoÀÚäÌ©WÜ_Z·_^kñ½0•K¬rwš»=¥ýi^ÿÔ+ú,ÕÑVV•¿J¹êöÈðóëÔhS9«b<·ïè$©àñ%›÷'ÁZ¤O‚¥? X»„jÞ~õ1EZº¾“býZ•X'­ü¡À  €,€WºD£nC/§ÔÀ WkÓrDÏÐeâ´ÒèÉzJÝ(¸÷ Û!YÞŸbo5ʱ,¶^VÿCúÆ&¶AÒÓ4¤|eÞ~s§:zˆõ{·è·ø*¥mÀ C ÀoÀwâùNÀ W×çùîÒ ê‹=®OšGÃ=ÀotaÑòþÔøÍïVee…ûBvÏ2L–¬]2°vK°eûcêÕ8ÕI°†X¿òy´Y³^² à„ǃI°Àƒp@§ –‹kã2߀a `ù…a¨sx¨¿Š,§U†Y µY‰¶~q$Q[Þïl[gÖ¥+Zf]aI(:ÉÔÆ ’¾±®YïÞŸeÝ~ó`ócRÝŸæ+À·ôÇûÎjlØöx¤ñ$ðÐÏÖN³æ{€•›jÏÀ SË¡k¸,Ÿ‡}Ýúµ`_÷ÈÏ­<—v¡ö÷vëK}óïììÿ$ŠXóûß"€½§ÿþ_» ¢½ÖI°,ÏSÿhÿcïlc¢88>Ãe2ì.ìnPdA7PIU0pê²_¢n}ájõêk=áR8îlˆ=¸Š+MµWÜ’¨§6Ô€õe›ÁÄÃ5=±éé?^ðK_4iýÐ\š»\ró²/³ÏÌÎβ³0Àÿ÷Aû8³Ï<ûLwþó›ç™™èdâ¸ú êYI «êÄÉÐæ…SH¢ýŠàê+ôö…Æõ'YÿZ/# \WÒÂX°B€Õö‡´MV×kš¼^ñx,å3'!À€1,LÀð™Åg±¸ÜþQë³ð󉩧/G¢wø’ëO@€Oÿèp|·@ç·© ±ýg.·ßxF…¡àÕõÁšúÚÏ yÅ]E}-^æ6ÿíÛFiq踬Ôz«©oë?†i÷÷•ö+F€ígýÄ‹†ÓêO²þ‚zÚz÷ó‘aºÄɹR€Uö‡ÀÚ¸ño€'àuc×ÅiV§vd-;× ™`á1ÏQÖ‡'=¿!7Ý=+²sïô´ÎÉ>¶;g²X8WˆÍì-)OÂÕC~éd‚¹)µRvQ€ÿ¨zbý °8ïù}_Æb»’6·ï]¡ýÕWèðÖ#ͯÏR4èd»6ÓVq µ¬T\›¦ƒ竵_1,Œ9ZhX’õ[ʤ•û߬l~+ 0'`åþ7Y¯>3 À xþ¢ì]c=BÜ.;“¦J€óº~'¬’{§)ÇLq]÷:÷÷„—7þðRC€‰õ' À?;ÿ*Ôý}ªF†½÷7‡ßzT4pá—N§5p÷ð\õúж„GÞ›WVʄ־µR½ýŠ`ÊÞâ·v{ŒêOeýîuv/y_¢VÙâ°°Àp*ìû`¹Źw®ç@€€ °.ºNžü¦)Ç,l'xe§N?wÔ¿~ªX_ÿ/ÝûY9{âúÚþ66ö ¬¬Ò=À e×ÛþüêÚ}+ßÀþLþ‰$Ÿ·=ò2{q0Ìì;v¡T[€£W›y8ÉÇí¹œ00¥,Ý+lâ@~úRåÌÆaq¹2^Ÿ].À©Q5N»šçÿº¶!?+› ³V€…[ZX}œ ô(0©læ@nüÙñSþÌØc`jÃ8Íö÷î6Ep§Z‡iþ €Ç ÀÆðôÇTnÑLµ yÅ[ͦ¾ýµ4Ëšj< Àxư½ñµã?3F¸&&Àåk}Üiæ jw{¿Æ/†àŒpú¯3gMT€MgÇ À8#Ø]ØÑ ÀxV< €`0 À` @€È€#0 À` @€‘·€#o€-À¶;WKÄRÁhàã|iý¬S8o°4²þéFÑ@ÏØØç»=éÔák}ÜY¼`²ú;1ójw{¿Æ/0K¸xÁ¼Z“pY©ûmÅiÿ“Ô¯d­³æ“IÞ)²¼›4"ù‹ü3 ðžEÙ›—JÿéÛ‘ãh“¢œ®nc— Ga¥—ݾPZÂ% äê+±Ëëûç꫚éï–-¶~{Ø_ÛZÇVj}@eyõP³x–~§}òQË÷ÃBü6`‘ù+²×k•'K€'rüË›T˜Ë¼+òP–w)ÓâŒ|¿’÷ô÷R,‘`ö}9'{JØ6èt?¼Ô×þQdhÞaNx"ér¹¶•§'ÀdýÓ ¾ñlðÅŸ6þ7:€ËwÇûZŸP.¯§ÙþÞÝùôwÚp§Z‡iææü8àpê®×(OšOäø—7&Vä¡<ïR`öPaª,Ï_ä˜D€Wݸ½lcT€W œŒÐšÅÿ#Y΄o+gºùpà"e{{S¾|y‰V s}}}-~ëþ/—Îú§•ÍÌþÏDm=ç™ óùk•_sN«¿ Pà¶!?‹3Xݯ²â„—,O¢OàøŸ–sS ÀdÞ¥(Àîƒ) 0‘¿È?0ƒçñY{ìúÎ˜ÛæÉ®B'®RÏÌ=À-~í LÈkýšJ˜¬~3á#·X&&ÀÕ ´ûV¾‘ý.¶G^fï\ü<à_ÏÉ#^²<ƒx*F€Ó:Wq²áùÓºX‘¿È?0…ÿùÁÒ¼ÊÁݼ‹Ë77i”`î½}A8iýf¦hâ~¥ØÞâ·v{ íïô xÐéþ?OÀ¬àu¿Ê=üLxÉ2F€Í$À mý4Vä/òÌ À9«¹Uà=+v×*(Àƒô¡è¡ ñLùÐ’8¹®{Wç-j<ïæSñ!Ç+Õêçñ {;îo6ðQŒ¯Ÿ÷¶E÷ÚÄö¹®EÃ^kàþ‹35Ul Ðt¾?†ýÂs²oR[^QÇZhX«õ¯lû%å‹´ uÞ\IUó¾™`QTAý4¾( °oKÖçdÂK–`ñøýð3Lä‹"¯”_?™O”íËÇ| Ü?ú@0÷È+Üës5"Ðöß:‹7éko*LäÞ¼’ pÍ3ºx‰\€eyÎ50'ø•…Ä/QÚ›¯’¿È?0…ç¨ ðº‘ÜÃZeÃØîrá…ÌÅ£OȪ‡üâ¹D06­ˆà¶QñQ–5¿Q©Ÿâ.†X†áï{×bP/Ÿ~îp86éÕ}bûÜ£fñëÐêO¦äÎ:™Û±ÙREkXò¡_E|0b™?ÁP.·Ÿõ»Ÿ×ߊþß¾pBÑÀŸììmþ ²¿(ÊÒâµâ8`– pÞÅåG/ùbÂK–àªQñµ7Wêà¸ã;™WмQÖO|ž¢6ŒKo5`EK pØcl‰ p²öj hŸŠëÉ+™/î Y…‡8.Ïí×èýÁÝ™n‹°î‡ÊüEþ€©Ø·%«w©FÙ°@^]Í'ñâ)ïg®Š: !³ :­·ÿÙ×5J¿Uà[£tMï7C^v¯GY?UbûÏ\Þzã|bP/!ð+“ÛßV.”Û—´¾¥ún„Šzš‰E¿Rpùþ`nòœ¢U¸ú -7ëtû›ì_bûü Ekó‹qº¸Õûê±SxnµÚþâ)¨KüÖ*˜<Eî™™ð’eÃøx=ÝßÛ3N'¼ •<þËŽïd^© p|ýäç©uµtð᥾­#¿Ä8â¿j#ÀIÛ›Šy§;¯d|@š”àø€±ªÎ°IH?8?yô­«Ø~‹S ÚDÃÑ–ùÏhÖÿeßRñ¾+:¢]ÙÌÜÌÙ~Ö)œ`(–—”‹W£êo²‰í—”;»ó¯–î>á¹Z*œ'¨í/Jœ‡=}ŸLF°oGvïV™ð’eÃØÚ)°+êèš¿$`;‘/d^‘y£¨Ÿüüê+tÍïÃàŠ°-â¿*#ÀÉÛ«…"ãóNw^ɸ¬Tx‹‚$ÀDž—…ø:æÕBü—âöýQ‘¿È?0µïY±ìïZecï>®¦­‘¼K¸B½96N€Yfa$ª”õ·D'$9 é‹?„Û+ÇP +¶ßât'‰öúY¶ÿxÔ°ãîiZ¹™(r‚AÞ¬|@XZýMö/±ý’r¶x nÀKÓ¡Õ÷—z»`V ðªYÇšrbÂK–`Vº¥†?^'zˆ“üø¯È—ÿ³wv±Q¿óŽÓù|>Ÿ… 6&.*Œp ÆXÄà„ÏRÔŒKåJ¶ÀPJhUÀD"¨H)#5 l$‚Q$¾S¥ô…Ç ¤ªU^xHó€¢*U¥îì×íÎÎ}˜»³üû½Ø{w;»;þÏogvÆ›WÒ3Àîò=û×7¹ÃF°î¿F$y{€ÓžoƼõ)&Ár p¶yåà2}eC€¥<_ÐÙ·Isè]ÚïóºD©rŽü(Bn{=Ø»$Íö pm§ÕqÙš°Ç(¹øsÍÍ[¬Îò#Ý~k*í“bŽŠ| ų dïñµð­¹ÿ״Ň–=ñ‡öÊîG¿1'e*Ör\܇Î[}Ëõ+¿vª(jü q×»¶Sk(¿/£¥ø-ÿA` ð²ªF1·¤-¼òvØè±Œ½ÚØ–Y€=ùâÍ+Y€]åËû;–õ3ÂHpÒ=ÀiÏ7wÎ6¯¬•Ø[§ °œçc»¢ëbk£_þÂßQ7~Åú)ªü%ÿŠU€[N•¸V<’·GR€ë7&Ÿé±sÍ=ÚÜ·µÉ2å,Þ¹°õû¼®ðúqù¯òƒHD~×{ü؉Iþpôfoº9.CeïjúÑ\o kùj^e Ž\ñ µÊ©¾¥ú•_;Umü }ð—hP(¿/ã,•CÃF‰[·–-á•· 1z¥šî¿úÊ¿ÿÞ|ñæ•$À®ò=û7œ“R·5±¦*鿊à ç›>o3 p¶yåà²Kþ5›t–ó¼¬;°­ú9ïoiŸðÖŸ&uÔ©ò—ü(V6ïB§ÜInmìù…²غ±Z¯à±]öÜ"Ч ÷¢:þŽÍƒþp¸æ~Úu™fÞ¶'qºvm¦öH€åú–êW>¾>ŒLûxQoP(¿/€/+ :9"opàÔ]Ž¿ÿŠ|‰Éy•b`½|ÏþŽ4К˜³Pà))8óùf ½g›WNýĺËy¹äï=¿µ5Ëë›´€C€^6¦á“r{„{€Oe/ÀïîqRéy[}üêk·šÂá ¯¥Wgk—w›,‰¤-Äh¹¾e–ŽïiP(¿¯œÚ3/£&oP€[›¬ßÒ °"_ä¼J!Àzùžý5MtOŠ,†@_j·—ú³Xû¼[€Õç›!o‡,À)óÊ%À¡Q´ IDAT‘+‰¾^U°ï£ÄONlÜjx#pr­˜ãƒ!Ð/ޝ®*uÏ€%mô3ÀÞ§*Ž(¸0Ï'ú3À:ñxó@SúuB[æEJlNÕñÃÎp!&Á’ë[ª_ùøž…òûRŸÀhàr1äYûáÙ.œ‹¾ÙŸf#Àž|‘óJ-ÀFùžýµ—]B¨?ÜЈî­sfLL•é|s8Û¼r °oÁÒÀÍ©Šg€5žp]{=v%±¾[†˜I°^Ö6ÜK IÛ#> ´wõ¢ì{€Õ³@÷üüƒ–€Q/Àö(+é™ßB®¼ÜÒÀÞxfV䋜W®¼‘Ê÷îßÚè¨sf·XiÇmt[Üð^ãØÚ+nVŸo6ìÎà œ*¯$Ž­MDŠY µ×<1&‡œ£ÿd$€F€[]SBËÛ#+Àb]Aófqä½Ò°ºX¹ð¡g߯s¸€ßý㻹ª×“rÈǯ¾lžZ™=XL]^™ý0‘ýÜQ$¤·'ÄÆÌÛþ}Ör×û¾†s~÷˜³Üê[®_éøž…òûÒ™¾Ã.ŒkBj ˜œ7®9 ¤|Qä•+oäò=ù4æK#lõ|ÒØ7ó±öªØ¿T÷Üé·£]C ç›2‡Õ“`¹ó0ƒ«òÊY?ÖâŠZއëû¦/ øÅÔÚ1Œö…7É?€¢`1àªE°5âJË^—ðÊÛÃ"À'+++CÎ@¶²/»”ˆÞ¼{f÷{ÛŸè‚+ÞÐøoæ2õûêûÃŽœÙ}퉿ÏZÏðÑÓŠŠŠMχ¾®¨øË+Y~X:þ¼®¾¯în8¿ûè@“}‡ÛYÞôÙ½gôË~·ßºs¹’ˆÞXuþè½ÿnÒ ŽÞ<|}ÐázÂZöÉù¾/öQ»´pcNõ-ׯt|oƒBþ¾ì†UþVa@€³àèçÎ/¹7h §*¿œÿå|Qä•+oäò½ù4±S¼rõâþiƒËmöµ­ð÷‰ÍênÍý‹Û_X³@{Îw¨í Wºó.«¼rÖ%ÀZ¬ê,çyù°¾Ž’²†ázó—ü(.?ë˜vc¾9çÕäî9°&·‹¶BräPíT½ñ`ÞHmh7úʸZàXè·š3õ‡ÊNô_óc³ü˜.Àß>ÿèã°ÞÏî³òñÎùͳO.ôë,Ok9Eï4þrá`r$ßØÛÆ>}Z ‡^5*çÂkõ­u ïë÷¨Ý³]çTßrýJÇ×gƒBÚß:dWú‘fpþ×øý}¿Ž§Ê/çßÿ¸”/ª¼ræ\~Ü“O¡‰Í"j,>7'™gÑãìu€=ç;TÜyèÊ»¬òÊY?–ëf/XÊs-\îóÖvsAaOþ’ðó™/òá­ƒ‰}wv«ó¹8ìàT=À>ßÌëƒMûî,J®:Ôóϧ©8VéB}=Ï**þžý„ÒîãWzü?Ñ=7{¿§*¯a ßHïšÉ®¾÷ðà¾=_éà[pëà>±smòQ'×û±-íÑ“uùªooýºŽï½£.íoPv)Qà p@€‡_€÷Ýysnêürÿý—òE‘Wμñ–ïɧPõ'E |ÿð¸¤ûb­ýƼÒmâóGâö2H=âÃoÎÍá¢]yèàLyå¬[€Å3¿ë¼y.>֯Ǿ\Oþ’Å À92Ë÷òy1ß¡U¨?0Û&)¦Í õ|ó¿\Ÿç‰ÄR”ŠØÿàÁƒ»g\¯ÌPŽ÷ã ÝþšÏãÃYß± û—}Ñd-j ð2 p`Í®ã“Ò ð0çm(ä‹å–_‘XºÃÈýqÅSÛ;ÚÃŽáÐ/­‹!¼á¢àçWI®<‚äX?rþ’pqÐó¬âßñ—øŸÁ‚Çþð…Ó«Šâ#¶úä?Œµ1E!ÀùÌ_ò.}=”Gx_Dv 4é¢ü™L_ꇋª?F€ Ÿ¿ä\$Äz¾©øî¥¤æíGÆ.ÝsóôùŸ 0<8™¿ãÉ?¸X8ô1ÿR†S‚cÔ Àð(`1ò.Š@ªä À0 € À2 € À0 €“·0LÞ À0 À0Œ ÀpzÊ\Þñ* 0$žðÊø¥0@‘ ðêiÁE3Œ_›W“lÝ ^úðz‰Øøó* ¯n¹v<ظÐà–O÷[l//6oÖÍ·åì¬Ò#0@þ¸EX¯-À[/^6¸JÞF€Åç]Wß.±xL¹ã®ô¯´m?+ýÌÜœO äO€—U•V%ØŽb9(˜ï¼;£ümoê–Ÿ˜¥yž8»ñ˜.À›`€< pÛ륽›`€áà1-"u¼zö;ÇŒtöêÓtL3z‚ d€|póâ’ÉG`€áà1jn»^Úk%rã]Í“O•LžA äI€ËOÌÚúûf`ä¸yqÉéä¼Ð½GOÍÚÊ2Hyà‰³KŒq p0ظóÁÅ0À0 pùY‡ï¶­, Î*ÙµŠ@È—7¯ ž^" °àÃKÈ[€aàÕ³“ü¶œ¨ K{— Àÿgïnc›¸8ŽÛ±ãx—GÇJ(á)ƒ­kˆ$!XBÉB»6VM‰Z k€@`jØ  „–åa)­Ð#ÚuSñ „„P×N  JЬ/&VíîüxöÙ>ÇgãFßÏr¶sgŸÃýþ¿»ót*ÀŽ“ë}/1ÙÚÜܼ«a‚Xwó‘#âY€¥ _yúnÙÎ⑟ORÍc€žàª\ùKü¾carYw‰Ùuò€¸`ÇÎ$ùÜ»¨G¶‰-8‰=ÒèT€Ý»š¿d°¦Ä¼´ž¼ nصWÚ=‘Ú&N2ŸL  C®r}â×¥Mqn´O “·Ä¸;/ËápÞU¶ÈüÃý ðŠ»R? oˆW®ÉõIÞ%îÝÒU¹¾;¨ dz\€3]¤S Å|ïj§¿,Nø|åo§öz«0 @TØs¢UÀg€“8ã €ø`1y}‹µ×y>tf{q*ŸI ¶¸¦Äüx!y @l °t–C*Àî3°Ä,öý@ð"çWgV•˜+¹*%1)ÀÃ6J)ëè.1Woå[ˆMÎÜãsŽñ®Ë^ùîzXb6/¿°k[ @Ì ð1n_ß6Ál®>—BÞðˆ pJëIç#;dbS€Ëþ:ANÛÔ§·Ä¨k¹öHsóÁã)™2Ú °ifã¶Á¡ °7lSrܪßÍ NØÌ2‘`‘Ѫ±‡Á ž˜@ âl¥@€LÞ@€LÞ@€ (ÀP€ S€ @& @& @`(ÀP€)ÀP€ “·P€ “·P€ €Xà̵Gš›OŽ] ·<¸ûm–â–ºoþs×f»û§r­æÜy¿ëÇŒYÉ’òß…xøö‡ßýï±Ó*Ò»/¯Ï^Óôµóõçœiú¥kýdlΖÆQi¯F÷ ”ó×}ýxŸoPe —×÷d0ôÒôçÝcÞŽËë+ŒÑ¾jˆ{î?¨ï”H °CJ܃oÆ"o‡ î÷lD¿06{Ô=ZÒ°Ò™ µçžy¦^zõfúU<óQçõ¡Ñ²#.lœn‰Óò¢ûûØœœ|Ó’~(¹\“ YòÛ?Fôë‹kO¼Õ»©\¾¶ñ €Ä)À5Oš+ ?–=oöZZ/ÞÒz2Iú¹±#VØ~í®í’b[_÷ÀæôFB®Õ±µÉŸç»~[€]¯åUKi5Es娶º†ÃJ­Ï=á¼GèE{½‡^ê£mþ¡„›_ßiÒ=kÄ­þ:Ÿ¡çù›ëÑùò¸P ¿)F«µßùÐ;„Ã:c a ðÀóxÏçîmÎø]•XС‡Ïƒ(E‘—ZÚ`Ã…Ñ‘àô1ò“ þ¸HòQçõ¡ÉðOJå•v`´þËëÙx!Ìhì3ƒð—dgñV._Óx@ÂಹæàX,Ç#?ÞÕk®Þ«#ÀÛïÙîçûvÆ[6ÛGoÙ²àẄ\«ã6•æî¯‚ÝnŸº$xûî¥ïþËf[¨>­š ‡³ûïè\¹b—3Ÿ™fZﻼ¡Ñ`ÿùG_€­V£ñÀÜ·ù>_õ„ýÒh=°{z–ü‚Ö63šN…<=u¨õÔÖN»ÝÂÿh½­K©ë.ÀŽÅæê »vm+ž×KŽ‡Íƒ(E“—ˆ¿þŠ®8¢|Ôy}„—Q5Øh:»üõË]3꿼BÿE/IÞ'‰÷þ‚GS€•Ë×4ž ØÑ½ÍûH…eG+¬!cli„£6øž`Çž$OÎÜSœ:Oã²½äp¸<ˆVTy©6¤‹Y9¬+øã"ÌGÝ×G¸¿ëÌÄÇ[ŠšŸÒy=/„&޾öK'";{lü °rùšÆ3£gŠÙÛxüÅ$wNÉôÙ+½Jº{éty²*7&§dÉ,YŠ6x-QÏ}¾ Z€oÉgw×=°Ý·¨N«n“+B9ÂúØ ŸÈŽ|þ‘ÌO 4ñ•‚˜ø3ò5Í­h®±ß‡ŠÄK¿XjšÑ'ÄXj~¿ÓügÐ pUnj®»,I79vÁzG€c-ê¼Ô¹Ë빫ǟÌG×GH9sŒÜ!ú/¯ã…0 6I£¯±µoõy4X¹|Mã‰R€—Ÿ+Ìô)Àž;Ú‹+ë¥ì æá#̉ÑU)où}$øû_€[nÛ^QŸV!üÁøÜ à°óïA–΃2j‹ÁŒÅioûEŸtÕ§!_ð§ügÐ ð¸©ó¹rÖ!Fq}Jâ`!ñ pôy©z^€ÕòQßõ¦ÍšVgÅny‘ÂyúTy‡,ù(ÿÑ`åò5g$FNqHeW¥×”Hú÷²¹Íu@x‘¹ºCÿ@n¹'_Jq¡>­öú_ó¼þyädú…%x +ׇ_a•[ºHcÓW£Õæ/*;y¬tÍÙÊw5`¡õÉc¥iMg¿jË÷ 4Ã.©¥<߀÷£`¶õµ€Ÿ3'TˆæÌ1­æó¿z_.›”ôø›î,X`9Î¯óæ‡¸qŸÜ'º†ï;.‹rvé9é –» KM/­3üÞ] 3fe÷Ÿ¨:?ÃZqcŸÖ´>[ÚÞ‡Ïßܾô¬öí}Ty©a}`ežª¯goVä«ÿó œ_`>*Þ¿ù…Ëwµõ!½?ÒëS[ÿEcŒ£~­¼I×åE>^'ýЉÒìf–§çµß¹zý‹-ž÷óÎÕZiÔö–ëéîò«ÿ¾±ÂîS€…YµÉmñèŒYÉÒ1ÝÀù ­7®×–_½~c¿EeùšÆ3¤§¨àq'SçÉyœÚæ9#Úýcl °Å"`‹Dõ7ŠŽVÈc‰ ÎÓ„¤ fúE颉VOöÖ¡/»#uÞri;g(ˆ°‹¯ïÒè[Þk^ùOȰÛ.Š]¤-ÐýÖ‡J^vF¾H㨟ªÌß ´wYM&im½`ÑR€纵WnôZÁli×±j ù¾›+ú] X‚eqiˆƒ¼Â\ee€^Q€3Û‹—v”¹ pMnuG¬ ðð3R±žr6˜<1?Lò“s¾g¾4:Ÿ¬\u]ØÕ½Øâ-ÀÊùI!Š5h“Ï-·ÅÁD/(ʼ ³>òÆX• ÈÓ ‰å)À~ùªV€•ùÊ÷Ão~áò=p}Èï5Øúð3¿k]ê¼¼ˆÇ áÿÖJE6oE¡Á]€‡’¿šãsç‘ݼ¿Ëí×[€Ç9ïN.—«®«»ú¯· Þ¬˜Ÿ°Ù9»r÷7(—¯i< ¡ pÙ¤¤Ý…Οçy¾¨AeßtôOÑ^W'–Âû~ßxXسÃïpvÚŸþÖÙzƸá´{ƒy±bCåÁó=Øw:ú¼ïPò‰/Vˆ›Ñ›y{o/ؤ½K‡|m¶K ƒNûyzŽ'o壞òòf‡tÿõX€?ßÉÄ52"æ¡6²Û2ÆGÅ8”4\b·<“©)8¤mJ›Ô`ƒIy(¼ vˆ]ž‰1mx((PPÚ(R„B‰•?ª þhø#E­ªÎcgwgvv÷ö`ÜùIÀÍùî·»3x¾ó™ßÌo&dlN'œµ-mõÒ¬MMSÇýdãØðkõòÁ=»¯Ëx›"ÐÏ)С·“1Ož.·\|§«öý¾éLø—¦ýOú.y·«ýR ìïõ€cÛ!/ˆíyÕK‡úà0¥§–õL˜ÑWöþLþLúÈ´ãÏIßMõÁ´kEËèºKöõ\bGJ€÷(·¯íÐz ÎpÁÀíöææ²«*OnUŽ\Ûœ¾iÖ€ ÿò"À´¿«»ŠM³n å\?¦ñŒ0aÂú3gís_ULóâe¯è‰*“»8$ø7N\´ ïÉ©—Q:ÌõË`BÁa€©r¬|ºÆà9íðß‚g:sz±eAÇ© ³õaàõ8t ¾,ÃsYÿ… ý3QÂÂâ…Ö‹vŒ¼2‚AÛGüA 6ÓürA£Ú#¤g'§]v[‚ó#£ &l pÙ³Þ=µ:%nß5BøNJ8ó-Ïé\Øñãµ®€x`Q mÁ}le‡<ñ‡j·%$üˉ3þô,F‚¢ï¶ÕŽA‰üfh|ìV/ê#„NQz)J"œ”žZk’æék À”>›ô‘iÖŸ“¾›êƒÓ>F[¡÷K'ûz®Ç nÿ¯ª¾R}+*5¶*hÉ^Õ"XÄ@ ·²Gù!nc4¤-þ½µU’x`Æß–EGð×íS„’µÅY˜0a€çLw’¼ò®ë®­ØÔ—v¦/5,Q싆ÉàðPι;y“°NÀèÑ p‡‰êëL•9“˺-`Ë<Vpzƒ-Ú7,Ý…ß{að”wÿ“Ë)súΗ™¿v‚nªû=/æbiSNù_©-¸²Y´C0µBK´@ƒ߉IШö°J¨e—¸ Ätª0aÂWÌJƒÇ êìýnSÚºîö²ÞQÞu³SÀ~ÏéÇ0àA©$› •Òbñ/– s˜ölGã¿5Ûè3‘éå>¾^']/ëôX×S;Â!ÌÓW€}fuiÖŸ“¾³×ãµÁ@­ÐIÒ’}=×ã׬(x·,‚£2À»Ù*1¼©ü Xç_N˜ö>ßù;Ã×-Íá$a„õWžü¤W;Š¡nšŽöîiöA°„&€­“`åÏ#U8 :LÜEO™ðx9§œ8ïÀ{Iª}´Ü=y'ûþ?²³ŸA¯V˜Ë‰ º©>Ì<º\}Sl£ÿ@ƒLb«PšÂÎyþ<ÿà‹âpÐRЌ햯kà ¬Òf7|±Ûo%L˜0a$× ÷v:À^DÄhÏÑk)`Ñ D{¥"ÂÝ® |@(®CY uþåD€iºY°>«æ K)`V/êƒÀºžþÚ²šI˜«¯ SúÌê#Ó&únº¯}l8é×s;^ˆ€1ðJE«à(­Ò pàÏI:ëüˉÓþ¤ü6¥óÚòX~Ñâ>K˜0a€+vbØpá·ãÇé®-~ò°mè¢eúž¤K Ã$ÑÁÄ)³æ3X `.›˜Àn\ m(TñïCà}ÃTNXÐMõa`òݪRU°þ+;üäue½ß*Ó”€CÛ dæÙ%o² *ÞbI“±=G-–Íà/Fè9õ„ø]&LØ`2Õl`DÄø@¤±©Xý#‚² —À£ª‚ |%Œ TEæÒù—¦ü•¬J…5Û곓^'Y/êà À&=å×3`®¾2K )¬>2íaòç ï¦ëñÚ‡âAz tÒ¯çv¼à€;Õ±Ø ¬¡ùŠ:jS#À•=Šñ¼"À0½ËP‰`}°Ñÿ´)Jõí]7b5ž&LXÿ`uVšÕêQ)I‚%¹‹WÕxÖÿ‚˜¦—l9ACÇ ÅÀàQÆ `ïå1_ÂÌ–w™r‚nª3ÿŠG°rê5¹R;€¥ÐšWŽË~ÿà‹ïÒ\©¹bÍØqpæÙrñ»,L˜°Àui^£mÎÚ—æ}Z|Þ–¶zv ÏF!* *æ©ÀgÜ_WE&N<€)†¦5'SŸÕK§ú,Ï.*!ÌÕW€)¬>2íaòç ïìõ¸íCË-UwI¿žÛñBü!yFn0à«Þ?àœVåã0ÀGÕXÔ÷ Y  þÐíÞüBQ”Îk{ 6ð§åà,ÖÊx»?D€™Cí]p£Ñ¢ærr¸ñ[üdwïd žj[N\ÐMõá€ÝG€¡5÷~RªîÔÑ \I]ìd'h®—@¢Í!`a„ l>™^7Þ;|,ùÙã©àªRð*Рn‰…ä¥F€é$Äp ô5xŸ¬€ÁçiÆþt~[Y“ vÒçDõÒ©>â`7`Ê«L{ð"²n€”Û>ÔX†>)é×s;^ˆ€«W:pè’-—©À{a8ÊðU€‘?ü£¼º¿/R”ËíÑÄhaÂAž3(ãd:/[ÇêÙýb0=±æ€ãØœƒ'»§>ÊjÍ–tS}Xp€+Xî÷# ‡ËŽ•ªÇBAÓ‡¶‚O¬’ù‘K˜0a €³TƒK Á?hç¯*ÅYÛÒž^š:†±Î¨ÿÇ¢7cž_ÝLïA…\ÒÉ|}(Mƒ€”(ÆþBG#-øë… å$p*öSzéT–°` †ŸI1ífü1:È´gO®­¾›®ÇkJj'ÉÆÇJúõ\â`ˆ¾[Õ¿Q+¶‘=ÀG–³\rN©þ ßÁ!²ä9¿`ݲ2Ív÷)’` ö(p—tÓ'ÿÀûTíÃçMbNëé÷Œž î´ÂTN€MõaÀ3¶®³@VÐ-¯A;ÂVÐâ9Iœ,L˜°ÀÚª+u0\~¥ªoÅ4ïðÚÔðŒyž×Ã0B‡S'ÖËøªJ=Ôù¶è¤5çåÌWà ±ö€whÆþàR¤"% ²¿?0«—Žõ!ISæby±F€®¾d¥¯v`“>2íaÎÊì*"ËmæÆQ;¦êz®Ç З¿ëjb Xð¦ \Up¹°G!Y •¯| Kkz”Îý>ãXn üüc&ºõÅÕÜÿ”â$aÂEÂkN¾‘^6-íùTœKÈ`Û0ܤ¢NFÖ¾)%´8À$Õð“`%Àß’GJþ{S9q6Õ‡[ÌØòÎ) lÿëýr Î;¬~5ˆ‹Aƒ'eìôÅ h%òLÎÒoÛƒã &ìÿ€³ö¥elNÇY Õ)`xh€ø ö»ÅçåŸáØge‡<ø3Üa|€¥âëà]XüÎtÄG…ç3ߢ–@‡ üPFÖ4DÖǽÚ¤?6úœ¨^:Ö‡a,¬^˜s¿ÿÄp0íÕG¦=8çòºŠÈòÚ‡ºÿœzy®a`’ìë¹/H8Zp/Æ1Ö²r 0 Ô÷(Õ€!ëPbÒX*lïâñâÜâžê+ÁÞ­Ók©]Œg„ ÖO.ÀB 'Ñ ,¬ÅFÞ„–a•ퟱ$=…nŒj} -?ˆdžýtoóÚàòá- IDAT¦¨~HI°¢Ñ(ÀQãºÝ½“ý ì·ÃˆïårS9VAß ÜûŒ‚®]­ø¬Ÿ«pœ±-zÏpóÞæÞrËçz{d_&Ó䌿Êú–¿}º´«¹ýX) A{ñĉîÝŽøµ™uê~Ùöm©¡&ŒmF ±çŠßfa„ l†y¡Ÿ‡¹6&÷ñæ¡“À™XÚU{Ḡ´€w2Ïnì;.ìSϵ2¾s¢û—£Ž?£°4ù{r ,æ5ȃ/v7÷¼p‰d¦üåM’=§66ÝÿؘËZøã]œ-!½t®)p4’yzvWû…¯àG€ ÷¸´æ† Õþáè«ñþLþLúÈ´ã/ƈ¬v=^ûÐõ=¬À?ñ9ØBy‡Ç$ÿz®Ç Êj¹\ŽÀûk›+ö ÍiUª?î½Ù¦ì˜HÔgxçðÚµ½7¯|¨°4¤U9£yç”Îkkgµ)·´$X”¿Êž#7׎6o¸ÊD’ÝŒg„ Ö?¦Ôì)uÖ𱆟güô»›¦z3–Ô¦€[bí#ÀRɱ<˜ðœó°X¶_`TæM6н|ÿ¿ÿ¼“}üù:—-Ç(èèqµ•Qù#Ñó«Åt}”th§rûƒ—ž¼Yv{xû'·À{“*¿½§Qïx ¿plvøóS>^ðŒ¡ ð‘®nß“®òzÝî;Mê:=ÏHÎy¸×ïîêirø˜Ü¾‹™ç:ÓŸ?eò½8¿Ë]ÌJÏYÔÓë.Χ“ Hñ @”„ú–ºººsóÅתSlñ{}|¥‘׫9äÕ¥ôþOÿûy²ÍûZwþrÿ {{˜Jïÿìþ‚[|«üÚØ2JôÉŸpbuCÔcžÓË“?ÆÎ ˆB6<½qï8iæÒPWw¢LÍxk2)?&S¡¦æ%ÙéIïpÅŸpÅKaùÅc½InymYuÛ–ÙB.žH|TcpåÏ1i˜¼ö&ªß£ê`R+?Eãòú­·W+êáRnŠl“äGaF¾/ø…úÈœôJ©Ù2V‰Í<Øx%, šXFpX¨ÃfÀeíar8ÔLžz¢ÊUÖ9ãê³fúzê@Яh€è`ê¢O³fžÍñÖÿÝè‰?a/¿ ¼Ö~•ŸUÁR¢_3"`{[UÍO1×ûÄ>Û‡ï=Àò :ž@€‡VÕL41ç+½ùX#s–œ¨¯jÓ¤ãÅ¢ñæ‹ÌïmlV,ÀQoUà¡^~v| ó8äð+L5U–Ço€qOXò+}˜xÏ¡íC©ËÏ À`¨R}6“¾5N§›B=##ØùnK+}cUüj´ Ìå÷ÇÇp éJû±ÆƒÍ›Z¦gŠ<ŠR§Íü®ßUrKxdXÖxC—¬ªNýá5—û´óí\z0 ôå÷ÆÇ0c«,÷^”¿b &)ßµô~âJ…L —5ž@€ý@ Âá0¡±`$Ê ¦êÏ;]µ5·?²EM~[wGpûP~01´ïo @€±ƒh3à¨ËÏê@¯ À` @€!À€# `Œ€ @€02 À @€0â-yN¨¯îÄ9ÿrVK]݉©ÈÞ{¦Éó«u¥‘"ÿÔ`«cVU§kôXê·]Fã7&ËIc~SdJŽü-vºìµ5ßMfúﳚ?Ø<ý·ËNSãWk§åƒ×—µý…‘Üâ3j¦¯6þòG’´uG>ÐV{p·§Q j.5Å1\U=vÔ ðÖ‡}ßÛ8)½ÿŸ¾Äľ/'‰Nq¶êèØ¶,5ñ0m\òÊvCû£›CÊ1=óéçuüx%`0îÏRõ >^Q†¼ösètèµ4J„*ÀEc ý‚Ûp!&–äÓeÌbö¹ÔâÒmó£T€SV®|Gå¡¥ñÞhªu®´{vÊ3òàgËéqœ§¶7uÄ_Œžý.£—ª4/ÿPÈ(¡5×ìФgš—ü’YChO€ƒ×—½ý©!ýLJwwEDÚm€X²~Ú`îö4|ÞlN¾:±2¢ZS´-ÀÉ« 0OâýçDÉÚ5QÞ{ž™V¯49å`˜ðòÅXÆö“Á‘ðuئ2N¬Lє˨/wû ¿@ö/}°¦X°=Õg Ì0à pöá¯g·Ôy©Š)œªJ¼ul­àïÞ¼•xüCiéPѧ—VОoy#5DfÇÃHÌóã? U÷g¢¹¹¹² þoäGhã ¾'SiQÚú‚ÔW²B©aÐöËÍTxÖ IN cïÆsOùcmÞ*Ft³ž¤C2ùcc™gbX!À”EË칞H¶g”è“?ñG )Û©Úå¿ÅœÿÝE]KI8¬ä¿©/ÿPÚ @z ¢-î}#,`M °Èöd¹–iX>B"$À “â’<<,5+üŠJñÖdã¸Æõ¡qí3c7/轓)4æÄÃHÌEíý9· `‹É_¸Ÿ2)Ù?!¬ýÒË“?Ʊ €Á À®LM` ð˜Y3öxf‚©\”»Ó{’šYŽn6èã·(`keAüþTÿòc'©!}Úº?3«4ðŸ—c ÷õKÆF÷»¾`0«=Ķ'ꦀ¿"\FH€ó¦Ç­}Ñ+À¾À¼ofaÙÀÄÛoy·fœ¿ÜOªP€¹ñ03À2 NÝýYR€ƒŽ/T`þx%$–n?r€€£%ƒ@€‡eSÁ–=<=v-3ãKÍ'Žyä ýœ¬Y3’˜àÇþ™Ó­#ÒÜÈɹṫ-ýùû SEØ·`}óïTá÷Oçä4霕7rn´Š^r:jG~“β°×íî¡sµ¾ýõÝ~0ÑpÙU[óÝ“7B/· «Ú ôtÀã/ëtYÚ2kÛ êD?O&7±-3¾¦ýöÎTÑõè×úѓي•?Qbx•ü‘vœJiÇ—Ûtº)¿5¯a,'ÏWPuüÆF/îâ °ó$õŒ†¿w3wò—Éò÷t¹ÓýŽøç©íswn@^þ¢´xÒãíݪ_ã»@j =25üÞX€9ý3j±a ýéšÛ³ýëÉÆ'S!3M?éÙê³®‹³uäÿDúWqy¥ë+Üþˆ²}óÝ]=M¦@Û[¯»˜úÖ[»Å?PO'à¦ûC¾°¿ˆ¢^w¾»Ç¿8÷Q¸Óó²CT¥úÿ4uƒª·vˆ?øšS~þþÈ/¿è ˜[>ÉôDÒç–_ŽSvu{[[Ð^<8éû¾HùésÛCt{þ\“B@5Κó‹7\4kén9Úz—~ÚÕ=W€ÃùÐ$¥ñLÈý[_úqÑôtõdcÅ׸ìT{ â•x{rögË5o(êû ˜ûy‘ñ/}I¶¼o70öê$;j³X|”O²BÜ¥‡ÃŸ£‡I€hàa<&•wÆÒ‹ßþ?{ç[Åqà]_ïõêâ{±|e„mŒPK|-\Œß±ê¤8n·”ppoq 5ˆ: µJÓÔl*9¡‚¢@C«BHI£ü0 XKV©"ÔÉú³óØÝ»sfvç> q¬=?—™={fvÎ|÷Ì#mA©µ"8£ªmÖ?ÚÒ4OO.CÄ[kp½¨{×OîÆd"K <¨3F†6Žåaνg+NÀ»¾ÄLЈǑJIÊ\v±‰Œúè´Ü!oàM u«ƒ¶_‡uŸ_ýØ/J ëªxçÄžìÅo gbb:(ûóCÔµ…ØûùqÚø` ¦üÏì%„^Æà·ÛøF€ËÐ;PXl¿×o£{f?ký>’åœáŒ{¾h¶Ü(CáðÀæše9³õƒx×(Ù¸£òGôÐ}Iaðz4øiïÅÿ’]B@ý&ª¯ä}¹öˆÒ-Éœvnλ5v-Û88`ÞÇõÁ•ëW)#å5j'Màn8clІÑ팓³¤õ¼¦½ne C¬ «?ü¡þ±ë'-+ŸÕŸ?/¡‰nÓãìÅ °,?ÿþ°|ha{ò÷TxAo€g–o?\¸áƒY3 ûý€ýX¦ä•ôg"ÿ…4|6^ÜþPâÿÙ¾«±Ù­H¸©ÓŸó÷?ÄÀºd”l+}¥š÷ÄÝ¡»Në„ô 6ø7Àþ³åá"/¨;0oOæ{Ž ‹7¬JÆ~ƒôÜø‚-Ÿ·'ˆ—W‰y†™¹÷eŸ'­Ÿ$Û£{Xoû ÝO<™9Œ×-ÝxðHùv| Ò’ïêENúhóŠôg~5-xâ«Ú±ûRbÅ0»õ`Yñ=Bº _ÌG€íùÿe]Ù‚Š™ð xèŒvi|7fNú O…Kö§ÀÁsÙ™W?=µoTíû›ÙAßhêûÁÛGº­Ú~¼ÙéþcÍ—ïª}Ÿ‰Ò?¿ßï-}gç3sÅü"ýÜpPìó{š[ "xC|àN€<’К;`"À“Ñó㻢&3×õQíþîÞŒ–ýÆÞ'öÓ, r·-·\"ªu–¿$¿â’ú[çÀ ~ÿ}T­<úöÅ ½½Þ÷]A?xQ¥¼³ûßwÔy;+þóÏl¼Ï(¬ßõ•½/×þžß¦Ý?ÑÛ[³o夀ƒ¨áâU·Ä|‰úþ‰æÞšR\ù°¾‚·´ÆÉÒÞ}_jçi;h@5;^Ú›±oåMKê¿x›ù¬œýÚ\ø=Býük×OZW>«<üîíþø›(ád–È^ppÉÚG–ŸÓ–ïdОrÖ9‘=y”<Ù¬þ ÀU+ÒŽ–>’cÃ]]‚-¶N@‚aád%Q&×0¿çó ?”ø?¥¤Sõ]©v`¶?çõ¥NõôÑ7î¨tÙ(›÷7Ï©}ןj~çƒÿÅØä_Q˜-¥ÔOo|ëÔÁMØ^œ¿r°§ý{ŽTèº#°%j?À =7¾`Ë—°RЪãÈoqÝù™{_öyÒúI¶=ºö‡F]yâ‰'3€3ÚÒÒËÓ~³Šn‰Eóüïn_…m_5-x¬öëcJøà‡µµã$4rl ÆÌknS,ŒÛòãðXí¹S¨ã¼Gïs¬}®}ˆFÊÁkVˆ-'ŽtÓ+9*éqQ‡¼§ûôw° 0;hûuÉ:ý²Aá’ut’Lß“M;z‡Á vPȹʲóüâaä»rŸÛ3Œ¼t`=.°`‰;Jlhã×3ÚÍèm‚2&Û¯Kök· w-7þ÷ ÆiJa0æQ…L ƒúA¼']‚[TrŽF¤Ûwu.ùå9›pß«‡²FŠV¿··p¤`ý&¨o<¯Ë´?Ä‘¤w)âMÉ꣓aâÄÅ\¼4oûZ¦|X¿ZW!Ä«ÀêŽQ¡0ÀŒ ÏÁj ²úÏ»fª,Пûú³ €ÕOV¼†úÃ÷ãÍÙx?  e¼$²Ÿƒòeù¹÷‡åËìa6¢sj’k=I €«ÚÒ6Cžýf9ïk§È߆;ÿqª8A&|,Ó ã{ç%þOñÏ¿«êùÖüâ9õîç±ï—íÏùûÀe¾J2”¬S+_áó+uÇÕÊ_ßXØà É¿‚0(od‘oÓcM{åÙÓþ=ï5àv*줇ã X>gOÀôÊØ‚< ó†…%jl3ó¾ày²úI¶=ºö‡ŽìÅ{âÉŒàÙƒsÒÍS)W­˜ÕO–OOÆüŠc»€Ñå×d¼øÊøC¾ØžGЉó}h懬Ñíc,€H€óž¢…U$‹“ðoš¸?¶uÐöëk‚1I¦ÇÇ ¸ñ*zJì„Ixþò5}›‘ϸډʮëÄ ¹lxAË €Qà˜Øë›;Û¯¢' ÛôDÏ Ò“º«*M©MHÖo˜ÜÖOn«î[;×´$jc± Àú¼'‘iõyt:4_¿‰ë+¦ýõvØ“ñ,Æs7). ê+–‡€rHa`ÖÈJë?bäGŠLfÉõ‡ß£HÐvýdåÁkNé`M£€=@ÒçIìË—å‡úÁûR{ˆµž<6®Y™†OYüÓeKÿšñˆXaØÎ2×gNÁAv‰ú3±¼¶ÉЃcÂw œ?”ø?$U£MH…—,ÂfÖ‚þœ»¿“â"N©Fé±×àòG*X炘ð/uA|˜-/H—º°Ðžv=ƒçS%a?ÀLz8¾àÊw<ØäV¼ xïEµr³âÀP?×úIº=ºõ‡uÇUoºŒ'žÌH®:R¾ôOmiäô_€Ë”âDÓ€Çð±ëÁµ-ÇàŽSÌyh.âµ°8Ë#À‡IþcB6fçÖG/mJ€ Ö˜ÈzúÓ&êi¿|ÙÕÜu`½j²B1¼GLà+ÿú§®ŒÂÝ[Hx~Ngæ/C™ŸlQÛ s[7,Ä~Ã}óÉ€é¾abâw°|!dÉ/ü|zH©IH"ª 0¬dõÅÕ†‡Ä?Ó=/-ÆEå¶bo^°™š«ßÄõ•¾.Óþ h'ÇI'²ÍÜYJ `(ûâÀel}ÅÒ÷Ðsà7{݈òú/3®àÀɳqè¿G‘þŒ±€~²òÀ5¯¿€)â' ñö¨Ç”/Ëïüþô¾Ì¶‘¼ï÷žÃ|ìÜ2‡œ­À0Þˆ²ùñ°B®S· V‚þL,!¿ùÓ— €y(ñú[Ï:é•(Пs÷¡?DÀJ#œ¡âaß>¿íØ%ª0àÿ "Àly1{:°ÐžÌ÷ìÎMÊ~ ‚ô`|Á—/`Œ—ºžÿ‰_ìŸyý\ë'éöèÜ_þ8ÛØÿÃO<™Y\³2mi?¢`JÀ€~ö“Ã8Qú4M×êú…Ì€ŽÉû‹×Ûò“] ‰8p£5 œ ŽtÇÖ°~uÈæ¯‹!…½&ÎÁZÍS×Iþ„éCƒEªžùÑÆ?¸pðœºz3`ðüàzߎ¼§*×Ó4ïÙ‘¢öB|fûÔHÑ`:| ‡»¸ëº3ÖJIãO˜>5 ‘¹P='¥&À°~Ûië+*_Áþ–LT‹0¶Vn+™<Ž˜«ßÄõ• ÓþBƒ4­ñþ‰SbûthF«uˆ@Ö[Hç°½¾"ÑØXœ×j¡3ÇQÿõQŒ¤H½ÛŽCø=Šôgº V?iyàš×_¾ ôYË C¼½ê1åËòC}á}™=lÃH÷¥ž< 6Q—àš#i‚Oé.Љø³¸Ž÷Û„xhö{áü¡ÄÿÑ2ƒ¿F‰^¨æ öç`AX\dúŠ‘"ä¸üeÇÁòÛúìÕsbü+ˆ3å!/“o<Ü€…öŒï{NÎ~véÁø‚/?ŽcZuæ^æ}yý\ë'éöèd?\ ÏmSmO<ñäÛ À-sfõ“8pZúÓÍx¬§‘?ÆRÖl®›`Û.Y°xhÅÀ'¦€ë›|{~kŠñ e{–(™ëœN«»Æ®u!Ÿ^ íÚúþ õú[ŽŒm§ ž8§¶ãÈoÁšüF*°hhçëœýÖ¿Œ¢HHŸš,²mŠ¡‹ç'À°~È1HÆ€ uPYÆ0É`:Oÿ'`®~×W&l8¤äL|Žég\4í Gö#àºD€¿`€Y»iÉûL‹8Žú§“’á7å¤?ø…ú3¥³úÉʃ׼þqŸ\LbÍÐ^Üo |Y~¨/¸/µ‡Àß(·¤¥Û¥ßþVF€ôg© ïÅÕÿ™²dT5!ËP°?€M~#!C.¿MAš¿>»òû€:0Sž- é ÀB{Æ÷='g?fz0;Þã QùR®ëTõµsü37¾q­Ÿ¤Û£g~Tíumžx2¸ª-þYµ5ý‰Ãä¤Ësð*%sCèéÀpX)Ž„¬%7‰QŠc, *–BÍ#„GCR¦¶I PxWU*$°âRÕ¨‰üÑ¢ªU¥Îììk»³{¾Ãg3ß?öí<ö›oßüö›ù¾hûRÕŒ¿stñöe«ý‰™ÞõðÎ÷43°åo¦0—0)7¨WŒ æe`ª3€©ñ'Çkn%}ÚÝO )3½@ƒ§Ú¨n²´›û«,‰ux‚¹Ø;éÿ&h6f—=ÿÄ|dòoï>òw:à÷µòð?R^äjưۖ'ù%Ò¹ò0íäÅè!Àn±†­Ø•>ãRÊLô –…¼ÃZÿ™ ŸGµÕb˜XÏ)€EÖðÛ¬ ðUÀ><ˆ<}¨ZŤ`À¨>Ï5U»ÀÎæszò#ïÇšöÄþ" p¨; 0授¥~–ðýmÿ¤=­ä'ÇZžN&`A‚F ?Eý _3&Oñ|õØÈ!Ç‚)yKós§ÀÝíƒrÁ\TG|h´_ ™w€é<©ì©`úñG Jš3Ï{z¢z˜pšøÐqð<Ü}¡A¹3ÃsŽãÎÒï8ÊÆÐÏ2<$äq½ŒÉGÀ²€UÔZ[``ªÝóËm.ËZZ ÃâЊÝí&¦¸_‹ž[¾ÑçÄ\´œßg¨ð»f€è¤ÿGoôýiï¦dšÒÌâ߆?^}À¦øw €Õ’¤¼,Û'kw€íË“üé\yXnjÝ '`Ýë°¾qHª¸Ð§>eœõä¹¾œé‹æŸœ›üKÖüqë£ë§¼@cí³Àµ:ÞgÝm¶”¯<É™ÞÄ“‡&i(°îyÒ¸Ì 7˜[à ê³A`–—a„o-ôŸ gU¨gNaë#×s2IÅoµuÞÕFùYÞ…Åæõ†Aj>ë ¾A éðÀ¨> ¯Q« ž€ŒÍçP‹åqæ´äLJÚ~ƒÜ_0êÇåIà9ó‹ÇÖÖöMµÀØþƶҶ롈,HÐÈÀö¢óW£¶çÁ­Êy¬GÞQÝu˜B%å,Öãƒe²”aD`e‰ƒ.4ŠPr‘ý—ÀY±ÃX‹_[)ý²e^õc°Üò:fÅ6§Ç«{™~K’õ—ÿLÅ0¬' ÆÆÞ_:Ï«DàJÕO–§4¨+ °ç´ÜˆKàñƒ™ôMʇ°Ãý ü]~Ö³~±§0Õ¿îùå6×<þBÇTøcŠSMà¿ÏÁp)?à«b`Õëpó_Ÿ£;ÀðÌ­ (cm°¾æ¾Î=è¬WmѲÃþy|Læü“ó‘Å?F¼úÈßÿDû¬påõ,2)/ºíXý¼òÔzD¤så¡Ï€ˆì‘xÚXŠ6—,À™ÔgÌͤ¯°ç´,JòôŸN!ýp±~OUöSë9•NêC °À«TG•Ÿ±ËSøÔ`¥¼€¥òäçxÜ<ç–ž ®ÃŽ@kõ½7!›ÕÜ]åê´y>p]xÂBæiÉÏ€Éüäþ‚Ú¯ò$0@ú“•ÂMË Gaæö2ø³íŸtÇ£íz(° A#ÃXÓ F'°€&VBª™’§¸¢¬£¸ƒ®üõ¬ƒY¹€ãw§OiÃÜæ’î/TO^žÜ:»^|m~ @É™½lvá °Ù§ïÕ;ViC‡¢ÁÓçßmmYqS¹“Ä[ ËºûÞx·õØMõ‘>cIû—ç_ÜÛúfO…¢YÕ OÁïÆpxs5ÀÄû»JÜ€‘mnöuînA4W’¸`ˆ8n·´æ×lôùd €,ð3roÞºËoÀzÿò!,Àð*Yðôú㽞}ÇáΆÀdÿºç—Kæñ7ãÀ‘–ɱֶ~Ê«¡µª“Ç6ùÞªO~Àß0,ïíÃó7úNv`XÎd…úA… CÛæ÷¡ù0~xp¸¥åØ€êäI9>›ÜZÙq»ÑYÿ‡ë“>+£*É?ÃJñO|]ÁøãÕGÍw’²}4Þ=·uZÇF»Ùò¢¾¹~^yŠ?"/µ4fÿ4tºÝÈ>ޤŒõÎ-Ψ>có§ë+.ú§ÿJ§,UV½Ð+]šeVîŽO=³÷Í ÿý)µžSé¤>,™<ñâÞ¹zU@K—_ŸÝ¿ö›½OéXšýCO;üoð^Ü¿¢×»ð²æ«/>ÓãýhýŠ›ž^1;Á2ô•…<Íó¹¬"°täáR~TŒÿQUd~jAîWHy6Eƒ'Ž"‚fjù2/F¯Fèö2ø³íŸtÇ£íz(7;A‚ K<ªÃä†ã»Èœ÷Ú¥ÛÈ{z«)ïµ;·å1Cæ–KïjQÔ³ÐЬøŸèÿ—c™ômŠƒ«`å·†áÂʆák«$zªÕ@xJdBÎímïBÙýÄÏHOÀ zJmF CåÙ/p¬hêçé÷tˆϪVè•>§zodS“Ï—D„Ì„q{ °›…¤£áª9XàÌ)¹·cq5ÏTê‡d\>¤Ø_‚2ï{´lYûÇËfLö¯{~¹Í5? n…ª|¾“[X¹K6 ¨±,Ùù>#}ŽRYÕÉ-f/Ðxœg¬¿äD¿y'‰L—áÙ•7øô££Už”úô?äá-«þ'ø§æ#ƒ^ãüqê£ê'ù'ÛG`5&ÑÍìLÊ‹ ¢~^yŠ?²~®<´ÕàG }™øÇyßÈ:†¤YÝZ€3ªÏXdÖW<"õ!OÿŽž{ìgOôa¤ÑgQxÛ–XÏ©tR–LP’<žöŸ+­ŠPåýão¨,š0Ì÷¸ñ¾à©±z`¬>iÎ<¥ôâo™½@›ô[žæù,— YÁ9·òKìÒ]6*¦n2?µ¿ ÷+¤<õ8À¨A%Tƒ-(ßþÝ^vý“æx´_G/Ñ?$h$àü ÇÑ“•ï¨G²Þ~þ|zýÜüá€Á’[ ðµu·èŸ¸gÔ_O¡ïÝ~­¡Åf[€‹2€ÁãŽÍ†A’7|².ºæÜÊ­Å’£ºüxoÅšsßÿ-;=~pÛ¿×Eƒ«N/ÕOð¤¾*(øóXÃ;9ÏÓï‡ ]W¤Ý¬ 7Uw[r¿Ùˆ;ÊÀ4À©èKvÞÖ¢ô0Ð (>Œ‚—IŒË‡´ƒÊ'ëÖ@a+£-Àdÿºç—K8@]0pëJ²êÊí=Q…gߺ¢$-g[Y+ûaº_r€d[pë xßu}¾Å;àƒ¾ø ³o@ÍÀíå™å½sœz>ÒüSÕcüÙ×ǘï8ÿtû¸³ïz£µ¼$›ùQ²¼ªïJÒº<ƒ?¢~¾<3I¡0ß§ØÎŒâ”ë”}F‘Y_9 BÚë¿DOBK…§Œ{±ñ Ÿ®[³êKÄ0¶ž3Òq}ˆëšsOêg„©òþøÛŸB<²~¬€¥ð¬.äÇx6ÌÿFDƒä]3õ5¦Kn0>ŸC—»<6.¡ÝÉÏÀè„‘Ÿ±¿Àë'å‰àRý ´4~bà¡©ŒöÒüÙõOz㑳Õ‰ã2‚̺ÜvxíÚýGóGéÚù ø=9{ yp$Gˆ¾KIþPÈüD«¸¾LI!Y‰yî§Ê‡aN=zÊ#1ËOæÒ/ÿZ` 'œð›²)ª»?õÿÙÜïã¼?’h°¾BäLk@ȲmÞxǞؠŸQQºïŸ°Ëò™á7¦ ÚÙaÙ¶ ¬Óü~ªÜî Káp Ÿ.þ”¹Y1ÖJá1äeQ¿rØ]y"Ý»þq?yëaÙ²ÂÅÚ&HЈÀ¥BB g¥Ö«w žÂFùcƒÂ“ÐcÇÅ{zç%–Åä½o)~ÆÆ|Ÿ‘8{ÔÜS°:ž/hði ä2¶&‹8åYÖgƒÔWœú"þA*ùlêC#p&çsYפgs¨?²Lƒíîz¨„h$HЈÀù÷)Ξûªàï‘aÌÿœžÀ¾ß<=)(û$×'?hb¸GXn[Ñëñ ü›]¦xdê³Ö‡ƒÀ¬ùœ˜éÝý“±õ°vbà#ë M‚ XFWÿææJS.RsO…r•Mô¥ ¬Re+ô_Ík,pvèÿìoLéÇgv68vÜ È987§²F\W‚X½íY° ©ñxp½µ'Òbˆ/Ò…özwrÔ­µñô¨6A¸½×X½ÆÚˆÖ3Q›¾ì®é›{图hšË½ê<ÏÌìÎ<3À.2¸ì~?/tpvžùí3îþøÌï™ç©"sì,ñøðòηË?Ÿ¥n>|Q¶ú<ïŸáÛ p}ïûÐ}ƒ>¸ìz_p@€ÁxÿgåwË>ÿ„zŸ B€ÍŸ•¿Ò)‹ÿ€ÐK$ÀR^SôÞÈ5ô38³òYªæÃ`ËÏó̻͡ÁµÀë“à÷aÑåoy0`0ÿ}@¿üóÇßûÇ¿þÈá ð%`Úét3ù,5>ï/þ °éó܇ëeÃ÷¡äóá>-`0'ú€9ìƒþpÊæ[ä3`d„`@€0†`Œ| @€0ò- À€`  ÀXGΟÿê³/–&!÷xÅè½ár\+i'À%« ›’à9òïòï‘ïæ÷-w‚¦låMG‘;O3¡Þgƒ%«9®²¢ø‡‹sþùH.¾¥'ßbRؽ7r d@º ðþµÎb?¹ípÊüeß,û[€e\sq±dž—mrnŸ=ÿ._…‹YʦtÈK\5ÅÝkæl¶z²‹vkò,-‚ÏŸý—¤»#ä¿0}Þ¹j!ä5ñ¢Xü¨e@: pè“|§NpËÖ:ßûêS9 ïÍ€ôàuÄrõà×Ê£HÊhv¿ Yêæ¬ÁÅYúãÕs1Ýeóoê pèöTpàÁNmj`éÌÚ© +úà«¥Á°Rx tÉ[:–…¶ÓôE¼=vE½DàªhG 'bªV´U<+‘_Â+‚WÕ.ê~[ ì¾õl0úèt, ûò‹åVÖ½©êÛ'Ï¢Ž?$ELæüš@{y‰iZ´—d|6÷Ÿéxc|Êû—ßîóóY&ÝVnÚÏÄ_ê_÷ÖÉÉÁ;[¸jùÏ×b§ÈëàÕf mx#¸9W5eï½ìxuƒÕ~r}‡üŒ‹ £8çBíñKñz¯UþM%–.Œ‹‚ ¿•ƒ?ÊRD²KdÉjfä@pìkñ@9¹É-ܫРpÜÝr´AqVÁt+ç ‚ç9Öå%ºqšè®6ÉSÑdX¤Íi¼c†W:Ÿú#sþ˜gÅØØž”\|v÷{<™!;L¢ÃÄ Lbö3ñŽÈïþ@ù³-6ì9«'èB æL>ªé»û=ž‰¼×ݧ£g¦ù¡/ Üì§+*1û™øeîíúf†/é >æ-‰}IýOÒ\€WlÛãpÖ:Ní[l`ÙwG Oüšóo pU»¸›N¼±]ÛãUDÊúñÒ@ðÈ­qR;l¹`w|üó„_8²^y©&yŒ`F»Æ^#jJ÷—úc+ã*ìîÀøÍ‘fºèœç¬— pýU~óO”×J>Ît~s˜i/Ùølî?Óñl|.å 鼞kL€¥›Ê‰˜ýLü¥þ¡Á‹¹§úË'*ˆy«H7°T% ý8çB¾Ó™ÝÙ¸d\´•ÇÝE%Àu{§Ž§¼ª3åßàžØduPlw®É…e;Ú!¶•»#‰‰˜SÿTêî¿î}nnÁ‰Ÿi‚Ù6,™XY! wW)Âëz—¬ª p ¨kŒ3ß\6¶—t|6÷{<Ÿü»”R·•ßñWM€=ÆiE›ÝÏÄ_êKÖs¥­dæ-2Z×oa,U Hw]®­ùõ‡sdÃR 07áwÝ}ZŽË cxW>ôY?ÚœSG€¥¯M-UØBgîe©øÑÓ9nÍ!Y9w´lþ(.Àqÿ”׿&ž]0åÛ±é•-ÔÛ ž) «RõTŽ 'Èj¹.e4™*ÀR·±r)1ç7W€í%ŸÍýg:ž/þs­øRm¼~?(îÞ™÷3ñ—úÉ[+l!UöÒV½WV`˜ ͸n¯£fXÎÂú l·sedž ¬ S˜ÌùÜh`«ü›:\U<¢ªc}Ýô\¨àE×½ÎÓ³ \³ßõaO¸m•&ÀóãþÉ5‹ßáæLmx˜‡£Ëá„¶²‚Ôi••’ý˜\Ž pu<`õ„Æó›+À†ö’ŽÏæþ3ÏÆP¦Ô¢’­,ÀÊ[®6e 4³_?ñc?éÝÂÅ¥õÜlzé'À»ò³‡é}h‡s{ãR ð¶/@æð.‡SϰuþMÎëˆéQK:ÕÉcSäöõ£k³\ÑVþÎŒë «¿! ¨:•® ;»`ê—ˆ0 °ök-QJÝZ…T`]ÀÊñÆó[/ƒo/éølî?öxS| a!úK/Uv <8~^ ’ÝÏį {.l!]d–‡ö8•ÍÐ1ç+—–H€ËZ]w/úðý SØ2ÿ¦r˜Jà­ûAõIR ãz™É8>úFXyÎÖà³=áDÓ<ZÖ† ¼%EÔG\IËjظ¼ñüq–_o`¥½dã³¹ÿØãMñº´ ¶B|hµAfÂŒ!Ѐtà²Mä6´R Ö¶lŸ+·àbÈÎQ!C å¿fÉ¿©ü 0%774´Z'‡ ÜŽ‘,Aàêˆ×Õ_N_pÓ;¤heU;Ÿ`š&ÁÒ¶›H"‰O±¶æVQ}Ø Ìsþf¿Úœ2ñÛž'éøìí?öxS|¥­ÆB-+Àìþ„“`Ò]€÷ÇÒn<c$`Q86ó¤ö °eþMåY U²ÜÝ´‚j%pîOŠ 0wršw ÚÕãUZ©ŽðbB‚iZIÖæV¡?—Tl•U…ª:x*À\CP8P®hãùK[•Ѝü/FVÚK>>Â៞β©ÿØãÙøŠ¶òáe˜ÝŸ¨c$@Ú °œvG•Åj³ÏC€€¥`Ëü›Âë}^ ™–6ø˜“~÷ïï¶èŽóøÈüP:æ6ÎðÅO²è?RA;ñFb\}•×iÖ„U^*h²ï’BêÆi~@©×_•Ϥ4 eq¦ó6QÏ­šv †@kí%ŸÌÇÿ]¹ò¹öô{¼)¾@— ·ÿÏÞ½Å6uß÷±lËvìØ^RJ¶4¡–Ñ È<‹5â2TE% µA$!…`¥ÂÙ–’H%T’f ""‘H4UQ;Bé¦1iãq /«ö¤®Õ4Mš´s±OŽ/ vðIƒýý¼ÇΉãƒôÓ×>çü§~™À‰÷§ÀEoÙYª@ް|VP\å,y+KzjÅúöäûÈ[Z39Š€Ü à¤ù»ÂØTyÍ~uðƒÓ· gÿ¤Ìî³ÿ˜m¿rúüÍMJI*ø•×ûût§Ò°©îuáìn¥¿ËÇý„#ïè/2uÑï^$10ÝçvèÎ}që“ö+¡{“Ñ -j\3ýÓ“ÂÕiõ™J÷Ëß™šˆTLî6%ÿþCBÉç“–½_Ä®·½ŒŸŸäác¯×ûSƒ^¿„ŸOz~ú\3³œ>ÕùH¾vR'ÜŸn¿÷y5ä@{Æu—áØª¾-vÝ6‹MC©î7` Žp0 Ÿ8qþ®´¶\¸¦þ)ûÞ pã¯õ¦ÅòñºècÞÿR À3‹pዯØ7ËÁ¶m—²ëôWYV6¨éœ˜¯¾eŸß¦¼n¯²Êpö]åQòveW¿_Ù¡ mUÖX”•è8öû­êã]·WiëÇm/óçgr+üoƒ^?GüÏ'?¿7wD·(o0)€îOÀŽä.jM8rr/€mÓêwÂ#¶e `Ó9Ÿþr ä_'Ìß÷ °É´azrSÏÝúèª=«¯ÿ§ÏçêžiÓŽ=ü×û×U‹°ÉýÚ5õºÇ'«úzfËLé¦ûø×Ųøî¹ûãƾ³í3i{Ò“ùÞþè‰UÖÕ—ôùzî–÷¯2¥øýuòã µe,Ýòƒç·—éó“þþ¿?^0€Ÿúõ39â>ùù9>“ÿ‚ðü"%püýé}\𡯄€äX§:#¸÷F$21eó¤ñØì<ÍÊÖä\[ö…‡×,ÀéÎßû‡:Üó_[ý¿'ž/jEëpdö› 7J>)Lܘ;Ã'lM}™*õ`ýö2}~R[›ÞûÒûƒ_?Ó‚ÏÏý„å$ÝÖ'ÿñ ¾ØdÙËÀr?€Õ)œÞòÀ\bs,RkO?€›¿ÏÆ_mõûܼ|«Ï :gj~à§ñð±÷Í•ûúeêäÍvíølÈõ¶-_ Ìq‰}@N°=Ó¶=Ël´ms‚ýêXsáJ àÃßxÿU˜#¯µ£·sR°Ð¿àläã>õ|š2v€&€uòæ&åÔê•À¿ŠÂûì{U¾Æ–aŸ·@~°¥Gw`x!Î}+3€Ý‡¿öþ7W‚ÑQ´«{fì7ü@g} ;bëé@Àß’l|üÞ¯rê%q¸ùo€f @#·Ÿ×``˜y  ̼€€000  Ì@€€f @kìé½™˜ÒݼÑßf N÷…çw¥ÀòøL…Œ™·Ç}öžî™Á2v €n©ë×k·¦Í¢äN³r«÷Þvå¦9hÜ;Òä_vš_: `iÊjo873oG…#/³oyÀ¶„öŒ›«g¥.¾lŽûÀWº]ÞL¥ö\¨91ÐØ9¨]+öe¶çí–VûÞBö €Ö°‰[jè_€¥pC±²è‚Àž†ZQ1v¾Ñ¨6ýö×í?–±w°ÀÁ=æêA©‚u¼pÿÀ,!€å5Bñ‹öÞ®©¾3ÚPeX›J绵€°À ÅÊÕ®äÞzRÿÀ,!€Ì¢žþ:“uF]Ëdª›óÀx>€¢úeà˜X®¬CXzpáþ%€xúþ(îàhçAó¶t¿ëöÈ¿•ý €µÕcXùJ¾Jåz @öØ%-ýu¬¸…³y¬CÂUì<À-űVSØsÁ\Þl#€ÈbkWž<–xÂoÝÁØ)H,ƒ€Ñ,eïXH] ©F>+ИjÀÀæ¦ €1,€”X=KšÄÊ)¿òr õíÊÑm§bB0†ð@{t-Bg›Qo8oiµï-dßò9€=ãºËplU?»î›Å¦!õH,MŠ·¤y‰ÈB×c0W IDAT{ÆÒôíÜ.¦¾òdVžTÁQáç`}Û¦Õï„Gl0ËÀêôíš wÊÑ9ßÞdßò9€SÜ{#™˜²yÒx,/1 °e_xxÍ⬟¾×#‘ÈTÈÈy[ÙÁÀ8åNïa¼Ä,ÀÁžn/Ǽ­ìp½Í¾Àßò@ 7ؾ‚¸``NØ|†} € `r9€û”ƒ²/–±o0 @ް¥g¦í9v €&€ÈévøýVv €&€Èù€&€ € €ÈÀÀ d```˜y  ̼€€00pÆìé½™˜ŠÝ J·¤›!£òûŸ®ýÂw~çoY»öÓ?ÿb‰Û.¸õ Ï×ÓýÏuÊ­¢ßu¿[øöÎ7¦‰4ãv2hK   ÐÃ?s¬B¨¬J<»"²®DAñN0gs D ñD„¨¹Åù“°¢ˆâ _Y4Aw7·¦œxžž¡oÏ5¹ä²¼9r¹÷ârÉ%7óÌLé´Sh1–ïç >}žŽ¦É¯Ÿþ~ÏïŒ_Y„ï%±¿ZÜzF§¦Ú˘%ÏÆQ÷úÚºtfíKOoßÇ'¢B€×¥¯=°v ó7 DV€fÒ…s·ã‰–æù¾ŒÈðÀ^šP:YÑ€lœG€Ý3œÈË¥­m;E4•Ýþ%¹ÌÊgmG KØœgùdÒàŽp °m¬†|OâåüŲ©Ï×à³ Ñ-À¦--³‘DXçMqKÛ‹ À@äØq3‰öà´Lº~ªŸ—àÂj!ï®úë’è˜Î–žå¸éædkËéKZ9nÄ’ú¼ï^ãÅþtrý³Ïu—­Ò˜!!!áàæÅ¬±8ƒÒMÔŸ=T~z)óÕŒúм]f&7tµî!¥ûf >¼°ºØyÄ.DÜn:¦ª DH€ãÝtîÞ9vœ%æë°ã5 “ZhSG&}®úe€[§9//¬f·™YÒÒ7ënðrið½UcµÙ|ý¢vC[~˜ŸÏØš¶,e¾ªÿÆúå| Æ\, ¢^€Ï=!‘¶8‰¾ÍÇbÇ~mÌc0~nxtDëà‚“bàu”Ð{”¯Ëíú@`y„Yšÿjj]Rís%°‰•TÛg˘Œí•úÂÈã^äëŽ%ãã Ñ-À…þÛ}ù°ÛP-bfC€€ pýd¶ÉO€ÓòDÑuœU°¾Øó¡x–ãN,caÃ×Ô¡ô° p­KwÙN6ÖºboX•Ï EÛÆÇV‘M¢ÉV#瀖VùÅÿ£@8Xx]%þ­(ÁâõO^EH€[g¦=œÇëëqÕ2ãå<Ó/?V¬bÜ=ÃyÌšßÍx=¡zdF¨/|Æ[É7ݯ™ÐkèxvíÊ¥t©æ›m»¨íÍó ´ãÉÃü+…÷}Ëe>̽4ñc§Uuý­ì›ß&±riU,„Ù>"Ây¹Ib †á`}€ µÎ“¼ßÒnôÿe:-“Þ¡¦³^οɳ{Ö#^M‹ÂÈ0³œ'ЉKK¨ °1!á /À <ï'À¶1ù®Ò¦,K–_{œýyò|lèbu:~~ùaò†5d9Jî<°¾ñ+Wê_‚Þ†©ÍE VK8ôŽ’˜É©-4ˆ¸ »r«®ß²Ÿ+“GMΫ7óÄÀ€ÜëᜰµbޱÄþþÕ½Ž?PmߪÜmVrnÐü¬!v°óNÑø;ªxíÁÍÂucöpݧkÔÖ·Ý¥m ~ŸÄŠ`ÑULÓ¹õSÒ¹¿9Ûˆõšzì·‹öÓŸVC€€•`}A‰–¶kÊæj¢CK¸ü€lìæ<'$¶×Ëyyós[y±]£"Àãä^?³õ½X”Ëy8«FÜ‘›XI©h­%x¿¯ÿü­ì!ÒÀ9§B,b®µˆ¢Ë„XýfÝ •bkÃ5ç.¿ÏØ‚“¤óFZæ¹2>(û~ƒ†‘`SOíw ¡(À|€ÎŽHxV®\XðYâà ? l¶5›I ´†aŒq•qbÈ ê,¸ä¿=À pÊ.jÙ|kü…å“àÖ[ü³©ŸÍ'Àµ¾‚f©ˆ¹Ö¢¨pZ·+ö—jwêyQ!Àúަ¦¦þº½|„N?ر?¦“l†+$ÀŽ[öÜö-‰Çr£¬«ÃuvÕèeßaë[Îk&B( °ïZÓ+Õ8kÜD€_/»åZèÖ7o›—/ÀëˉâÝ•è…ØpŠúdŸøpíçì13Ù4œúü•&Ôúü‚å§ÕîzC†î·ø@ô 0¡È1žGÇTÉÜcߘ-e0°Bì<¢Ííä-XaÀüÓ=Ij]°–}‡¼ö¾ñ= ™çF8î–J¡ùG! ^”gÕøJ¦Yµì%Ð;ï²ò{í¬$=û´ªE}}Ã×!Jª—Fˆ&&mŸésÕ¢œ,í‚2h`…¸8)¦“äµJáu^v@¶öÊÐRXh‰å•QàÀq± ô¼ï±(ÞíÒ]úŒJ¬š`%VúzWÙîJÇ]8ûbÙÔç÷ÕÖ‡À*`á‡ç.¡ Öç-­pþ‘ó,š`+$ÀŽZ|è8K‹'ÊkcºÂŸöm–2À½^9ï+H±ŠއY€³j‚3¼ Ùÿ¤à °@Êø³|–ü©Êú(€U.ÀB|}LŽAOj¨žk "/Àiyt§œ –I×v>@G.Üú–n.ØÆ‡ÛóÝ{iºtRîM×÷÷wÓtUX!Öw<Ÿiè›AÛÉUÌ÷ô`{ÆËy_&K] yÃ5·¼ñrœ÷m³Uà °b\³Ä ð|¬1t<»f¹2ÑÐe•ïðßññõ[!GèéÌ/0¸Cu~Γ‡ùW& ï‹W)ÃÝÿ¹f‰½ô´J®hXßX늽aUÞr܈% `ˆfvüqo@|uÞ$Ï”¶é!À@dXmGðÕѦ¦ô&)D7ñ—×Õ_žÛ4jˆÂšE‘u»ÝäÂìç¼ c7ÂûŸf ìIŸÿõ?Å~\Ã…Ñ©©ö²ÅÓk0†Zßl;E¥~§øƽÈ×Ã`øØXWÞÐr°“„[ÿí¾BÄ}Áx @€CXðû½,‚÷nd–7Fi¯¿²‡‘œ¨Ôç~æ c.V.¿ÿgïü¢Hï8¾Ãn–ɰ»®K@qA9Ps"9tƒ˜Ó­üÚó<¿ÂEÚŠÛj¬tµ­ÞåHx^<•;@0A/‚‰§^¬É©Ešžþô—^üÉÚûcç™/»ó<3Ãììή-y¿~0 3óÌçy>˜g^ûyfþXDy†ÏÏŒ(MÆ„œC6ÏrüØÈ¾ ùY¸Ð3Źá¿0?˜‡`@Ñ;‘s¹ª[9ž§êÁ À˜oð<¢¡çe?à’Öø“‘›€c¾ ÀóŸ@c`Ì·€ @€0€`LÈ€cB ÀC @€0†`Œù€`Ì·@xÁ…oÏߺ‡ ÈH€K—•´B€5øï¿ì‹¿Y-m=ÿ!(ï\ ‘aòýÚ©+•ìr’išøLièyÙ_ºÌ媬ï°Õxs¨þOiEU9ðÛÔãs¨ÿŽç'Û˜å?Ý|ÙÎ¯ÍøJZãOFnšoœàVy¶­Il >ÈóˆüeŸæˆ¥ë=ïC€‡xÁ7Òd+±¼Eù=Ï¿ù¶¶CÒ(^Q¿š¿{…¼Gp@°z{^4Ú`ÿF)óã4ñ™uh2&¥Ù¾ °u|éŸíüt…ÔÕY±d‡”«sñ7‹ÒÏ¿½| w9÷©8ÓøŠZ9žO/6Û8+À _{4îÒUžÓ/®‹¼íxò=0Mfæãù7ßúï„ÂÓWGû~w}™´½y—ûl¹*………;WfäNâéG`m|†¬åø±‘}A×Û©[Æ—ÁøÙÎONX„ã6¦››ùjºÁóÇÙà â.ôLqͶ ð†û_xê¶$'܆“’ùnøf]ÁPâ2KC€¸ûÖ·2÷Œæãy8ßî\é¾"ʇ nú޵ûËr+À.ÿèèh͸ùql|>åSkto£l_¦l+?]¡ð«Qov¸tõhßð_gCü¡Åéåßn¾&*‹RàLãz'£¼ÆxÙm€SL>>soo^bÂÝôqÁwJÑ7¡¼Û‹ Š!À€“L¹®n>ž‡ómWtnç˵Kb3žö³Âµ\ø{ÕßFØñÏ'¶•Q€wdñoGÌÝÒºÿ .ü*­üÛÍWàpˆç}Ÿ¤,À™Æ'ž;qï_d¾ pJ€O?_³@3á.]_wI©«Ê»é½‚Γ` ‹ÌÌÇóo¾îr²#ü °¾º¢¾+å®4X€§'ÀÞ+Ýg½iäßv¾–läâü¡r{œv|R+wBá¿Ï± pD€ó7YW[~ÏÓ)? ,W‚EþyÞòa0=ÖÍÇÏ·Ò 7y süµúˆ¢0øÃT”ƒ«Ú*ºMÕ‘ÞÉþ‡®ZñßwÕýýçâo.™¯hý÷OÛ‘úzWáw,±8´Z÷)¯¹`Q×—DŠú3ƒøƒî¯!nx09÷lÛMÃøôíU·ñÇ’¯ J!>—ÿ«—b‡žu?'E=U€…™ˆûÐg® U°‡C¥[ Ûs]øx*ê‹÷‡ˆëÆoÉF~PáRdözìø¥ WME|ñg¯‡Të¤úÏŒñø%óŸLWQ›"¥6óo™/ït_¹ÁW19žî_æñÉ'·Ó±ÛG8Ÿ™pŹî¹8_Ë[.ÿjÁåuÝW À@8?ûÜûTzQQý/dcšŒòn)8E€;Ä›õý}äßCÄPjÅý„IJQ–?ÿ´pá­)(,ÉVü7)¤Þ¤Óí 319@ÞT€ý31uÊ,_O7~ ° ÇÂs‰7IÓýgÆGß_:ÿ”`î/·ŸË|é{xt7¤tYw<Ó¿Œã“ñvE|Ú’/» ÈŠ“‡ë:‡¯­ëÞ§®‰.ʇÎ °ÇSwúÅ­59àïŸrõ#·&#òý¹ÿNÈýpèë¾áINàžØëY®´'òæeˆ|OŒ¸ß÷øÇÑÁ§ÜÀ#“6¿$|$µë7µ'îÿ¥¢¢V·Í!XÌõEù};76r}–“‹¤ã7 º¿&$¸fœúºåþ?¸W.}|ºöjopÚï ²ˆOdS+70}u´åöƒÿ$Xõ_£ 0Ýžx$?Öystx&JW7~D€g¢Ûn]‹¥QÖŸe~¬xçJ2ž}kn÷üL.û3ýgÆÇhü´ùO &y•š×~þ­ò¥ÃßÁí^V9®üõèŽgú—q| ¤~¬ ‡ÝdE€ó7íÉó¬Ë;³/áÃ#-`Àq&|øyKŽ8"ªå?Á¿uÕÄÜÉ{àbHàþ+Á‰ŠSgË'*ȲϚ˜üÄdQ;gvþË.\ø¯òôX¾¹ŸC°˜ëWVøú¥€«Û¸úOuña#KL Õ 3Õ_ó*u¢\ÝÆï–^¸»¶-±è´ˆ`züÊdÏI ÕÜñ¹šnpõ¿R.Z¨ °_õ_ƒ 0ÓÞÄJ÷1)Àä3ÀEŒÇccï’3Ó¨ëÆÏ2?,Ÿ]èU¶åNÔØþ3ãc0~Tþ‚)Ü•¶›«|ý9,jçå¿ÝñLÿ2ŽOýƒ¼ÃQφ³Û€ìð‚ËÅOA§<oØ›wæx>pR€óÏŸ?½g‹¨À#kr"À¼[þz–®ñÛ&õáBU€ùÒÕ®²ƒävŸ,‡&o*•÷U™Vy¿ürÿ/ƒ“ž œÐ<l%Xìõ++x÷ãEr¼ä(&~óŒe²¿s„*À]‰²ÉE§´ÓãçjŽR¯Œ²Œ¯&Bw–°ä¿‹i!MT€éöü\•Ü sæ‰ÿÚ`“g€Ó`õó Ù3É×"i÷3ýgLJí/›U0—Ç¥ ¬íü[äKOs$|”øgø¨áñLÿ2Ž/qY:NÝ6 Üpm]Ýç{ò”ùx{±ôRh0à¤K´4Ü_ï)èÌW5*wÔ䆻r\ù‚U€‰ž”ì"öRvP4޲ƒj¡¶9dº:à º²$ÀìõE‘+´QYOéâ7Œd?5µ,tpꫪÄ3÷˜?ÑË(Ï´ŠOì<]Ù#œô_ƒ 0Ý^r<ÌX6²Íëßi´!ÀÞ, pÙA><ý£6ùTÿÙñ1ﯜI0G[nÿáw7ºìçß*_F­¤Ýš˜ûlÐèx¦™Æ§ Œ~=4» È‚oØ›W7$Z°lÀäÐ-` ,òÑzO÷ñœ,Vd¦9"Þp‹> Üv«Lô¤d‘I€kbÉgS*Ay5BÆÌ^¿²BÝ7Q!žÅÆo TMQ¸é¯ÆÖÔ®þÈ,¦Úî2Bg_mò.Åë'ý× Lµ'×#-X]ªH!]Y¨ÓÏ.Wp¼ïI§òÎd¦ÿºñaûËæ_LùS¾C«Î·Ê¿E¾ôý©–ßeUÝ*WvuÇ3ýË4¾;é¥Úºm@x{qÁTÎó¼ß’¿]yBIa 8)ÀäƒçK¹y ´*Pâ ·RßÒ0YgZ²‹üRàæ¨;þG•GŽŒ‰-f¯ŸüV©$ÆÆo$ÚþšÆ¤pQ{B‰ª­0`ª=½PÍŸæò…›Cõ[D^a*ÀT{š¦¹+…ë”Ⱥ»½'§8žOKß+Åô_7>Luù—ÓwîY÷¥ áùVù·È—žš˜´^¼ú6æxº™Æà- pÃücÃIÏ;WYþ 8)ÀâT[ð_öÎ>&Š3ã;îf˜ŒË¾ÁkjE#¾¬©šâ^=Qôj‹ŠW5¹jÏ»’*X¹•æÀ+‚‰Zð¢žÔþ¡`ÒJŠgZy›†þÓ?/˜\ÎøO›ôüÃ4—^.¹yž™ÝyföÝvõûùGf™yæ÷<¿iŸýð¼Ìæ p²Ä’˜‘KßÿOùõjr`öþ!Ùä•~b㟩Ç3¬)O?¥6r|’V«7!–×ßòÉëJÕB*¯`¹¼¿5ø’*ÀÊý’/À ß<|ß«¬Lfê¯k¦¾ºü‡vY6¾>Zþ£äKÿ°’';VmòR5<¿ T¿DãS†)И-À‹Ö†y·Ï“~r*)ÐÒ?˜ $W€/š+Àb@€•­­ªk#^X×ÀS*Ökî2¶ö¶.þð‚!FàÀ@_ô5ÀLyºM•"Ç'}¬0º¸¬Îc?U¤ ^ÞˆŒ-ÏqÛÓ!_^z€K†³÷K…[,.Wù€—ý2õ×µS_]þõ‚gþ#çKGnh×r:!ÀøüPýŽ/ø‡l‚€é¼g^@€C*Œ5À@ЏâWdÆ•é#À·•×û–ä ¸`#§þBoº³÷ Hu-Ù•ˆŸPY«+‹u¸Á'¯­¾ 4Sžîµ:QâÛäµjÞGL_ƒÔ4ÊÙ¹d•ë.}¢`¹<É/å¨Êê8!f®ÿýGáþˆÁÞm¿ä°¤ˆ×ühë¯k¶¾lþYÁŒ;ÿ‘󥣤8$À¤½ÃÏ+õK8¾À…x æ °¤½=Ûä—!­›ÛR*À{ÖÛ–®2}˜ø¸Z=ʵ±*Óíoea?øÈ¸Lñ¯ÿüÙhÃa1 ›`±÷ˆ$ò[m´ñÓâËLEÞx„Í Þ _Äð`myeŸrê9½Qã{õS®ðÙœH|²[VOKŸ’ÃùUÔ«JGííš)Ðò>+¦‚ÕTçñÇ<úÌ3·û'W˜ç¹Û~±°¦=.èϘ«<˜©?Û>l}Ùü3‚þ£ä‹©š1}ÞGwõfÎgë—p|Áÿ&´kÅÙc@2˜Lp® ®<ÃY2]ú `çöõ¶­G À@*xuc'ée+†×ÛÞê2èS<,}çì÷:G¹Þbbzξå±ßÿ¤ùƒÆGáFPÏüèvû0±‘_ÊÉÉáÕ‚E–]Êb¡½I±ýî‘ëÛ&adâ§åßöØ¿¬¹~aâ¿GGØTñŠ{ÈÞEŽíÅ¿XÙ'ôžû¤yø×ñ­A|ºòç}êÁFoQ-ùdèfëòÁA¶lÞÉuÂ:®ðÁÍÆAëÞ‡] 5ålä¬w:qï¼§Þ+Ô~üÝ÷n·;Ì;Ùû±í5?lþ<ö»C2¤e_=ØñdüÈõæ ^eÄT[¶}Øú²ùg3îüGÉSI—ÏêÂZ²&œ=Ÿ­_ÂñÅ90ÿÀøv^Smsõš<l;1yµ{ŽÒ!C€€¤ ðæ5Rw{¼{‹ÍöÖ¸aœâ`¾d ¬÷å•ïvü]`Q-À–²Ÿòb×;+ŒË¤óžÿœˆ«ÞK‹ÞO¨ÓÞ_™’ÊqRriã—ËåègGGØTñŠ·8û؉ãS>aÃô>ûbŸ2áu7oŸ¾¼ÒÝ¢e¡Š¿hZO(T 09oÃÑ`>ì_æß¬)ÏRYE¯Þ¿B½ ´ªýôì ü“q2Øû±í=?Lþƒï–/ R9 <>Úú3ÏŸ¾¾LþYÁŒ?ÿ‘óÅÔ§zYpºrA'ÀžÏÖ/ñøÞÌzt<+3ଳ#ò''¯dA€€”pùÄÚÛÎýj(ËTŽ8UÞoo¹w8l7tÚ`Xúj~ö~»§eìdWQ˜2럹ÝÿÈO•kï_Rlõ“£_çÜjâW\ebª½Åÿ„\`4¦Š×qñ‘G–«—ƒá¬ô¶Œm½aŸ¾ïþO»Çî'M`1¨?Ó>úújóÏ füù–/u}ç=!+•®;­?Ÿ©_âñÑRny Õ¾ì1 )l´"¸­¿µõæP–3†sÑÄ@¶î;Ù]v pínWeÅ„9Q;´GÑvæëŸþ/…ëú] yž‰‘%‡5^WyëädgkÆá¹Êê¸Â»Úë£Æg”qÄúú/¬ŽœÎ3Ýq½Ì*rûÍ$ÿbŒía_Ç ^!þèùŠ^}~EGÛ7û¡×º7/ü1 e,[p:uÈ@ 0™2)D} R÷·|NŽY· ½‡5™ñ:ŠŸì`õ ?ëKV¿ùON}Ì—‰ÏéG IDAT³M>A½Ü€=¤X€³ À@¢,<×l"fRÜTNsBoO£È)®oý3÷¿]ȯùJ±­qS-·g` ž]š¼t©wzÖ÷»ãY¢ý"ä7uùJ”R²Ç™j|š=@€0xÖ)oœjOOvÔ?uÿœüš“¯s«ü÷zn„=@€0õÄ™‘ß BLl-€ƒGÚ†`Œ€`tÈ€ @€0ú[ @€Ñß`Œþ€`$*ÀζþÖ›CèÁLiꟜì¬á3$ÚÜQÿ‡®8¯)oœj_°8ýê2¿Ê¯çž@ÒF€,ž_»;Û>o ×ÿ¢E€¤ ðžå¶­«‚GgGæØ$¾ª¡½ñ5z@Yº-;äÏŠ­Çð „%»Ž|/³¶ø8œ—¬2Wß÷’Bí½¿HË|65Nj[év½WeïÒïµé—ŸÜ*N äãÉ ýX׿¶Ml¡ŸÌ9y ¤N€Ë/ϳ©xÑrۉɫ’o=~AX‚î$ÇWÛ‹9ëØ‰ãS}ûަe>«—Ù«ù r§õTQ\†?Í ½=5®tÌØÖ8ÈYï¬À£ @Æ °óMÛ\©û½Úm³-¹R$ÀÃݶµ[B\þGj¾×ÖÍ='wÐïßì—Ê‚?‡¼`Åõæ ÓaRÆ KŠ…}»%7äËZW¤e>uìh>—ËJþk7kŒuù›| €Œ`Mÿê¼ÜIÿî\ñÞ:Ûk` 5Lþ}rèÍ9AÞ|hîT„M;`é÷ªÙÑàçP€wÑ™¼Ùà ¿MB¹¹Ž³Ì Ž“²:®ð®+ó“ýÐkÝ›‡‡€ô`¦u:³”þ× ãE‹Éà㫜*^´~m—2 ~˜¯^f=•„m«|ÖS®´Îg‚ìhðÙ/¥w~²oy ¿Áà @Æ ph&ÖR0Ϊ ½°zxí°¼˜Ž›!ÀÙ—§Ú[ücïç¿)”Ç¿ñ,x]ú·`£°×%ž=4èãä5¥âÙåƒ^»ìÉ9ÅBÊG½-c[•­oÙóõ¤=Ÿ¹ÿ¬ÇK.¸/ÿCoø{ýùzž>þzGŒ®,Kîa‘r©péÛÚ+±ÕGoÙFnÃ_4¾:ëùÔÖ‡àRúÍÔzŒSÞüÖÓ–ìá©vÿe néá—L{ òÃÔWW=ù ·k1þÿ@† ðæC¶?a 4"ÎbXê‘׎K^ü·9ôÐ&på4'; ê^%Ùɦ»QþYà”{Å‹}‚ÕJ~»›·œÏ {>sÿY×R6à£tÈÓlÙßëÎ×sæ±Ûí~}¼—(Tt+à•XêÃÄ»°VØŸg$À³–OM}¤ÒB@uÌ”G¸iÔCNÚðÛó>f*rJŸ]~ØóuùïÞàµc€ `©ÿ]RL`²øhíá ÿgïÜc£8î8¾{w9¯–ã|BÁŽyH ¶ì‚ƒÀ4à–ž€Rp NÅ•€‘…b¨ã“âP0"TmU@±-Y±Šy¦#µüU¥ U©ÒJ®øƒ¢(U¥îÎî=vfï|>¼·ëòýü·çÙÙ™ý=÷¹Ù™9³hOt¤…ïÜéÊ7J€W”ñ-·OŸ^ìÿfmœ´»¥-«»ÎTX7Wè8rvþÅÚ5ò:åã`ß¾åsN'=-Ltzêú¦—wâ%ŸëÚgçò-Wõ~NëpRà-£àusí§œäúö¤ëæs“©UÞ¼jÍhªùñÔÖ‡`·ßï_°5¦ÌT~’<È/nïê(ßžãµ{&Û˜øÐééüTäñbÌÀò¬×¿f¶)kPB€€ôpÆŠ 6Ç"Û¾ í6 å­AC:äeçøÅ?S^Š~ý´úêŽåÕ…‰@Ôø1TeÁVa=Yð¶`«òÐ'ž&&=u}ÓË›W­Ì0TŃ¡NëúÒë}8c”,^ñ… öšlRÜ$êC—·ÆçÚÎé °Yñ¤ê# ¯ü´_&*‹Q¦ó“¸Þ÷Ú!²Ù÷7GñÐtµ:>Lz:*â%~ñüÀúÌô¯™k&OØ„éà̶ÉG¤ÿ=ÜØØøAí*©‹nÏ7¢CÎ h† õFÐÙ¢¡yµ&ò@ªúÐ'ž&&=u}³Ë›µ\ñ,9¿Ü-:ùÑǺœ|S-" ޲%®»Û:ÉÂÁË“Gc8Q}¨òJÿj$7;žL}tÁŠ`:¿©¯veû¡%%¥Ú³ nt|˜ôT~èr,'ÀºýkæšUúþ  àâ3‹¶n°i|7XÜW$9±²¸[;R¥7‚¦|Â_Y4S^†(§RȾýYäôm¼ª5²ªlò°éµÂĤ§¯ovys*Ãký–(C{t~ô±n§‡Ÿ^¼ÖËÙÍît½ÇQœ >tyã °Iñdê“X€™ü¤s—¨F)g,UPó½ƒÑ튛žŠG´â³ìïá Ö`½þucQÿ…ÆðÒÛ‘,ØFøn,rìÙ9örá9á)z3‚žÌH¶•u·Íâ×ÀŽCä­eÑÓ—U‘—tz­01ééë›]Þ¼êèUâit~ôñH8cE=VÖPr½6_©…ºˆsŒ'¨SÞ¸@›O¶>‰˜Éoê+៕äœÄ+Zá5º=PñaÓSñˆ°ŽzT`QÖö¯ñý #Àk&“Õ7d~9¶–ÅøøØwÈ“ªâ¬¬ÙF&ö wÝ[=¼ dß¾ =]V‘9lz­01ééë›]Þ’R{ý/Ã\ÕÉÉÿé ‚åj¸±ç8ÉTÜaŒàõaÊ›Ô"Xé‹'[ŸÄÌäG¶ART_W€nT|Øô§‰€q(ÀÑþ5ÿB€C¸xƒCyYü–cæéØDkldcà±Ö,ªÉ4ô‚ãœûû®”™’º#h „IoÄP»¨¯Éåͫ֎ >­‡bñë p솱â6uJ¯\ŸÜ‘ëÔ7î6HæÄ“­ÏèG€5L?mt{ âæ'm"x€ñ#À‘þuÚëñý "ÀÓŠGÂCÁÍF m†°¸›×EÄN$“Š'çñwdÓÒC™@˜ôæŒÆ_KÊ„òæTRwO'ÀI/‚s”Dë*…Ü‘ëÔ·p9¯>sl‰x²õõ`Õ#EE€éÅ¥Œn:ñѦ§â‹`0ŽXí_‹7è®6  àíª°òˆÖ¨á1êKv²¿jD•źA>@Hùåmi~¨¿ŠnabÓS×'„Þ<ä4©¼YËùXáI»_Q·^PÅ'àh}˜òÖ”ÚßõX&žl}8ne%56šxhí0³ ’Á튛^Å©æ©8¶A`ü°Ú¿f¶ÙfVd@€€´ °¤½íAe3¤EŽkéx!ߘ}€³ÿ¨|â%±˜ZFüiÁ «I÷Ò¬ËSÂf@nÕÛG5‘0éí{}™æÇ^ïIåÍ«¶¿«•¸÷Pj,žüÛ·z D‹ú‹`iKò9y ±`oàÃÛ0%ªSÞIUüæ«ÑôfÇ“©OÌ<Úp¼GØX;\xŽ×<ólp{ ãC§gâ‘úÑÍm˜(ÀjÿZ¼A»ô0B€3%–Ê,ý¯Lý%S2×9VïÌÈ(¨m•צ\ÚWä(?nH‡<­’w ´öv5¾Ô³Vú@¿Ï¾ÝUÛcßtWw¡eU-_ÝÜy~ÿ±î€2Ò˜×)t9»¿ïßò9§/L×z*œ:é©ëËÜöz½»L*ïÄK>×Àͳû÷Ö> •‚7íõ~:=¹»O –ä¯Ríú{øŽ~B©>ty¹Ù³„ůzˆëÍ7?žL}äQaŸëZÅùc·þ³+"À§ü~¿âÀT~̰ûh©vã]cÛ:=“_䋌Øña€˜î_W¼îر7L "À™Ú^V†€ïÜùà„Mé ¾'¾}b•ÃQ~Ó˜o¤=Óîóʧyù çì¹dGkSôWÑ-<§&¶Û?"#aÛ:•76¿êÔ&Á®nÓC†ÄèôœSs}Yqˆ?1©¼\aw©z9!&Ï=HM€=J}„Žóª[®&Qº¼œØ&ÝñåouÊ“cÍŽ§“®,¼ƒJ‚–ˆ“(O6Sù1#ÀòìsÒÖèø8©ôl<Âßc¬ŸŽ¿X[€WPý«$ÀÊOC€€ôpÆá~å}¤û-¾µŠMø¤×¨G²œYï5ùnÌl%Os®¸ÞÔ0pÄo™¬‹'¾iò¹êv„Ÿð,èï 4ÜX}A_%ab…‰NÏ\ŸãB_'`ƒË+©ÖáëröûŽÏàRàÐc¯÷‹$77fF¹•r}¤Âæ(SeG®¶¼rå5‰%%ëXb…xRõ!yÞjj¨ÿ*\`Sù1#Àœ»¦ÔujFºÚ:=“G†å³1 €Õ˜î_!À@XoFðÁË]½™ä c)9Ì·Øœ$Ñm\Þn®ùïÞí¦•×ý´Û·:CþûÝôݶ¼bÝå;wZ+œ‰§þÔï±§pŸý±ÇbíW“ßÄ»û&ÌÀ lß¼ïĬ¸s€ö¯` M¬Xð3Ý!ßÖY0yüàôûM.€ûÿù·ªà>Ÿ}{ŠuËW×]*Ä> 0O€%Ôy BÆS‚; )ÀÏt‡zì}èAëqXyŸ:Ú+,ÙDăµ=¼þ €uX€`Ksï뤧Ђg’ºî™Úg½’-(ãÁÒãÓ@€Ñß` á=ò~ )®j² ‹“ÊêÚ/ >@€08Iš–Æ-¢÷0†'‰Û† @€Ñ!` 0 Àèo0F @€0 À` @€!ÀxÖ8óàåÆ®Þ˜7–Jo4v3ªCn~<üÄ£y'ôèßÃ^ïðóŸÉ¨Múçï~£¾tÿä9™ïÿ6ñYï5•ò®íhñ`)~~úÔ²¤Xél{ƒà¤ûËÁú_xÆæ|÷QŸ¦‘{ÒâÚ¡¦ç§§»”ؼklî—|篞7ï÷úUÊhã1±o¨É×Pÿ¯ù)Çw,Û×Xæ7µ¬~ ýÂhïGúH®|ø¼ ¬$À_r¬Îî·9$>©÷È}'„ŸÓ!ûï {?Õü®†{ŒÏÛ.ÿ¡3õÓK~úÜ_f¨¯“`ñR Ö˜)À™Úa^ÊÇ«”ƒò›Ap²j'¬Ÿ=¬«½³$~âœÊÈp|îæ|19A*ì®&aŸLݯQQãË^¾‘cöy#ôy H1*ÿcï܃›8î8~gÉg°eYc;6àXæ!¨m<¼ª© Z’º€m‚Õød\SóªÍ ƒ `ŠÂä¤ãb:@ÇÐ $Lëcžáå?Ú)º…!aø'Â0™îã$Ýí­t’%'Rf¿غ»Ý½½Ý½ûý>·›PjQ—·a€õë³}ùrÄVð•ÉÀ³ÃB2t™… Û+ ϳy.µ'9\Û§_ùcþ*Spþ¾¡Z ¥]}±@ð¬jÌ¿r´¯_loÿMNÕ dçW OÒ¥ÏÃû Ï'µµ-{±%2‹äÿ¿ADŸ¶}úß\ON½ÙlžûKž›©;½»ÃlX‹gbbbŠT.ø1´¾íõCµCv0öO3Š5éRk»4 –ÆGö6SÅ5×>ø†1Â^¸˜(¯XW™j~lµÞjJJß²¼t`ñ‰úˆ;nJíÙß±nMûˆÕ¯ïöåC†Å&@€|5›! ÿÁ¶Wæfjö‚ Ô{iªõ«Ò¾|OV)WÁëJŒ\Ü/§ ¨½RsÕ »Îa~ÇíS5Ì_e †o ×þiA”€§-òg„»óµ¯¢á‰CªŠyQAþäëØ±Ï¢‡rt‡*­I=Ã;S¤°dö‘lönÀ‘:«p– M¨àS?2F\±x8ÇÝxì³00þUÔG­=oCõT!×€BN,×ÁnòÄò–3Á·Wšâ.åjJ’·}úÎóW™Â €W_/àá±áÍp8Tý-¯JyŸ˜‰¼4„É©0x¢_e‰‰‰é{À1¯å aŽl¨µÇîM¼b køØÍ¡àÇVkéÀc+êCßÅÏñðÜÌÔ9\JñúܬR.e¶o8£AÞWø}úÊóW™Â€c  –öÿP[…§£žàiKhk_…Î 7…V»’­Øçޏ}Ÿn]ßp¾îBÇÍGSÀé…àÿKSt%F}ë’“v?ðõ­£NæÆ6œ¸C|*äx2wýùYâRuŠð Â' ù/w ÔãÊðTöwˆGÊ;ÓÇŽöÅÍ›ÿ\†¶·£E¯Þ€ËU:Sd0ØÿòµÁààíÛÓ—çøoo¤ö¶ÖžºÔƒ¢Ø ö‘}2l–Ndä-%F2cPÁjV ÞBž>À”ø$ ÉãscÊt•ž%ŒRŠ5›Ñ¢Ã 'yƒÌÁ9Æ… ©¬ÒU'¶žžÄMGÒÒ ¬¾(ç“ùC ÿh(¬Ø†­&ZyÑÒƒáaþiõ_kÊûŒO-`Ùù,ÐCéõø½€ `7°6ß¶ÞÀ¿œVkÇ9îZo Ü–»·¥KDÃ5£»oß½òв>`yç+“^¿rQâ+ÊWê΃6méüË–3à4pª`Û+ý~L,÷ ùÊòPko½ÏûlýÏÎàãŽÙ9'Øe³]¡lƒŠùærŸ­ï‰øÎBy\%Ì_e 'Ž!޽º—¾ŠúÜõ³¡¯ïÿvX Pô(3îñØ›€VÑ À‚€ã.ÁEðð\ëxqäŽ~O§N£G*p”ðÀRÙQ„<Œ„<®¯”ãi`¼ê ȼÓ?€íCQ¾r´+Ó¬eÔS¦ ²`Û+ý~js½t¢’å¡ÚÞߨ°î]@ëxчö\¡l{ûÄð7Zxµü1•)¬NþÍ®Úu §~)~OT]u|kýÌì_m7(C Í€`7»¿€”àíSBÓfó-=û;ŠŽ}ø¿9^z€/Ù[f9Pƒs3u‡w¼·nܱú¡{nl'Ü.:õßr£„;nŠ={­£õgpÇÉmê+µ„ç8~ÿÑücÍßߌþ·€žßæ1Û`×µ31111EkµÙ«/ñX×øø‚Mû&j©3‚ÏQì›°¿ƒVåöVßÅç5¹YR­ïüÔÚ罂³¾ r ‘?d/Ë|™>ÀD|% ‘ñ'â¥ß°ùÑ9>ïà‘¹íAxÍiàpì:Ác®¯yxO«ÏýòSüdzÖ—â|„?¤ðoô—ru‡«~ß±ë’R^ÊôˆðD×Ϙ8à_—ó‡¶ßã}Nuã—ë H@ÝFz0r£éónkw’‹o5%'þüÖ e}L.wW?ªç€ë—t­Èødùo+iA;½G.LµÃ ¶½z¹ËD^Væ@VªíÍqßf{6²ÍùµÈ¯h_€]Ô^¡lþí_º¥íé@À´ðjùcþ*SxpÌ´ùQÚœ¨µo¸ŽF_i¨Û?(o¤ œ€›Õxò!>ïâ‹$3½¸¡æðHø(FŒZ¾ñ=˜æ¡êÆ—áAdø±5xEb9î`ò8¹MCzÀþN·Iy'úfôÇmàAõW÷Ç`ýbS{ 0111E£ïþn+rÏA’n†ÚÞoßÐWdß-ô!ÒÞÂz±)®ÍES$}çÇÒ‰&S6t‚°ú ˜!Jþ%€b@_¹Yjvõ*Ò‡lÆ8e| )â»/À ¦E[€ý^Á£í±5š³ðM»a§ pËֽƣ??Ù˜~4r#™^€õ¥8á)ü›£™šJ´Ã38QÀòô(áI¶dÀU–1“ç³dÄnE0¦ÌýªÃ‡Œ^ØÓ Ⱦmg ÃÌ`le}pýR$¯(_«àK:@;íâ+“£ÏÛ^½Üúã<}†1Qª×€ö8nlþÚö( m_ÿrNp^v°t»ùŽ­\¶þ(¥„WËóW™Â€ã÷ ÕjÅ¥ŸãhgÕG­=²+ÿÔ(-fâÐ/Ê!`ÁltÍ6¦›i!Wö4£õëàóÂÀ²!µî"â "üKS°€é¡ï’é‘Û4y–±F/Ë=*¥pôÇÈ™ØýnÛA¿Œ)SØpLëÆÛëgæ=8NÀ€€eÄUHìç\&øißH{WÁãΟ S4¸ƒÌg~f,lY|ø³å îär€’?pds*é{¾ìþ‹/@RÄŸj—ÍØL)Öi%»Ð˜ÈE.Ö¥†çLÃá•éX_äùHˆRÞY8_,Ož`ÀÊn0“ç³dè4g“ðõû³Ô”`4:àhN qzj°µ»P܆€å솓‚="êâÕ b€m@õ«À ÿ‘87 ß겴©ö´Âãš•\ðíÕÛý¨¼nê~µëƒüŠw?µÙšð[?<‘€¥Û½bÇ/"ç»”ðjùcþ*Sxpþœìmó£°=¬Z[—‡¦,‡rÆ/˜½/‚¥_!³Dëƨ_ž„ ^jÏ5É:×»'ðè/1*Â{FHMÅ]Çdzä6EæÞÿÀヌÏ÷&š8ÇmúÄäšÄÄÄÄÄ)ŒT”j¢vH•{aÊMÇês¨c ƒÎP(ìgŒœöMioÇÖ`n°t¶ÌQÏObyì[†Å±WWð%é)ÅpqJþ|…"ý˜Œ8BÆ!લ&‰?tL,xd7äSŠa¯Ü°…€@ù °¾Èóþ¢¼=ñ½°,=Zx€a7~c:`ÅùãRƒ¥ Bìò•=ÀVÜ‘,~,é±{.0'r™Í€ØŸúÄW¶gB;MiÇV&´ZÎl{õv?Z24›i·&Qj×°õžËÛëžó‹×ùüY“rð²Ø¯ï¼c{”® ¯’?æ¯2…7,ˆú?{×Åu…gØíxµ¶—ÝuxÔæaì ±eL`18*,4¡b«xÅÃ¡Ô µ\Ç OÇ lQ )•À Q05ŽÀÂ#PQdó«"XTY¨4ü (¢ êܹ3³3÷Þyygíu9ßðØ÷Þ¹¯™s¾9÷œóR³È‚1FXÎGXúsF¸Žd`Ó(ÐŇ|º·Ë¬|] Hÿ´äñ¾Œ®µ;äoOjõÙ>9H¶|aUÜ'Cz/‘í‘×,ˆXU ü~VÈn? 0ê°GŽ/ €!F€Q´ÉÞ·5Œ·´e$+ VÂrE¾ia.ßhy;»B¢'™2K±èOf¥gcöœ™›«£cÏ“}*Éþ™ ª}F$3‚DÖGNÌ›uX©[™¹i!òÁ_…£Ò?| ‘‰Sýq¸^ÄýH}ˆšï²ˆvÛð´®=VyŠgžä—¯—0u¿ü½Ÿ ”E§¨*,ÌxïB`Ú¯FÏãb⨳Á|XO i%…µÂ„·OVŒÓ_£«=˜¨û‘,oÑ?ÐW©M€Lo–ìÀÃF.Jóe¹Ü2ìíeƒnQ¡w®g§AÒ}«ÝÐÎû|9WÔ¬·² IDATè«#Ñ1‘*_õÔ¿¯ “ÑÕ>›ÛuqÒ %J€3ºfÁ^€!J€Ñ‡gmâßÒW¼’àräŠ|³ZÞvG1Pr YôÇ’/G–ß±+r~ZÙ'0ûgF(¨ö`²>ƒoS£H Eò%[ŒÏ-£_J˜êÃõ"îGêCä|k ‚†XÛ³/ÿiѰ ËRÁ¬ r§`ñÉg`ŽêÎ\Œ`O ¦X[¾°Šð\é‚ÐúÕuåÓ"+ ØŸUwâRx |R€!J€Eñšþgâú£dX€]oZýØ\¾1,fs_ó”çªq`­ú³;¼²¥ê]¡xŽgß*%f‡BAµïج¯OÖàJÙåé#,LõÇáz‘˜Ð‡ÈùÖð¡ê¨ Ì,O`ÿ§á¦µ`…—Erlæ‚d[€ÿ©É¬#À“˜¬çØl½N,ÀcW伟ó&úpsv’‘>êh¿=6@[ïö½¢[œ_ŠkN€Ù`ý}ÒxÜ o³b F?‰×òŸ†· ›¿.|€u¸úöÀ& ^Á`É©úvÇôÖ–å£þÃU?ë¿¥PaKà 0þ*À—CP¢¸…$ÀÓõ×®ù',ß4pîŒãÃÊ9¬ûSX5¦CTំ×T6u:'Tû 0UŸ ‚%ëù~…Ë¡†¯ð±0ÕžÃõ"îGêCä|‹óÖ„ÿ\ðoJ€q{Ìò4ææ.ôtMbû+‰_ñSl¦ÊÒàn™Ëο¤˜ôN4–õpâ,>èô ¡:Ú• ĉîW£çÑf,«ñµ^/Bó0 ¼Ô5˸ÏIÿ@_¤2~}¤B€1.Yâ•>—¾â}þÕA·seΧ<àøeV{‰7!À"Faþ°£@kËgÏ!¢Û÷GAð«)†C^5ºyޯϴtR¢@'.ßì`VÔÜü<ÏÆêˆì*kÕŸ±“–÷âàR3{m)»Bc{¢ÚwH€éúT$‚ŠC TðLLµ'‘Œ·v6׋´“ú9ßÕa<ëÅ•¼Ï†˜YžA€E##ÌŽ-àÅâ<­ÇÇ ÀÝR”‰aFÿ]e¼œó(ÐŒlFÆûÃ" ´”†iŒ”Êç[™ëÂ~5xí¦A²_< ´=ÌŠÝ礠¯R™‹´÷ N€Ô2Áåpz³üù‡Aµ£ê9ÁOœ_@_ܤ÷lÁ¥ŒFæèìOä¦2ñá$V`Ý ¦°Êó.(þwvôSAÈje¾ðýì X@€àY&À¯ÏP}”lHtÌIWò',ßü­2˦|cäMuò¶^^amý)Xèá‘%M$ lyM ÕOéTû 0U¿ø¯=sLZdE>Šn>õ߀, ¦ÚCúÏ£PèqÐÞz‘÷#õ!r¾çùе•áú¨ 0³¼v½eŒîãcçÆ¸ "N€Lǧ'À”÷7Wâ¿=l 0bÆÝòÞèõpJ€YëaF€Yy€µó“Yé“v²Kû,Ñýjð<ŽxÃÇÎ$DÎ‡ÕøDÛ7JþL1ÞŠbJú_N“¸ÏIÿ@_¤.B ð,þ]¥ÀÃÍÀyDA¼ù"½Ü‘,à`LÍKonæÆ­à3ºvž>¶å…vñU›]Éç\=VÓî)ÿ‚kvEÓ×—×®Ûs*‚¿Hõµ5\w¦—oú+Kàgž gt]þ¸îš^IÀô‡?¼zlóœ“Vò¢+8Kû Jó¨ä׌†ÝC‰O­Ù‰¤l陲˜½vZúþ¼u‰>&–kòÖù&ÊëÐõñ6ïGÊ[$é"šDý)>„‰„Èj°FÍ&û²ÔCWÈA5ãܲÃ{®üg=£}6ÖÖ—’*oÉúݪ PG Õ TH;éhçÛ:Â(Y.E€Éö8)ff(´ÞÞzQ`B"ç;{ï9»³¦—_ókm,u¼d{¬òÚõV°8 &ï—Ÿ—ñٺï^ii¹2¡¦ãÓ`ŽCüvûúîÉØ>À’xrÏÍÏmÿ¦û½Øþú² p¼>c?kçG¼¹´¿Ð¾Ýȹ°_ žÇ2¦ý™1Vã‹ý£¨¨¯sLJo=½£¤A2³#ÂüàÍ>¼#›‚ °aÿ@_¤FažUü›€½¿½v`ï0,¥OÓéçÔŒônZ––4Œ XÍ-À\pÜ]>žÈOÈŸ$Ex?7šºø\Øã9+}¹Ël9бügóU|**×*ôGAx25þŸâXºþ[®Q##*âÇ0$ðËÓDqû›½ó¼Þ¥—¥sV/x—^;€ÄíKÍiI‘·‰Ë7éÜóf{wHy+Ÿ&€®yDBÍueQ‹šT]=ù9â–ùØ›•hŸI€õõÇJ*…\Ѝl€šè¿¤UÖG|m?*¬jêÄد%ÀT{\@"ˆí­eæ>Dé7sJ]ù¢6 t|¼T{ÚòÛéõV°ÄôWÓ÷CGEð|Ó{Ê*YŒOO€cˆßŠèÏŽJ|9YÆ-z=ØØÉú²°¦>c?kçÇ¿*,M¨ºoݯìçQœ}ƒ“Í9ãbO•,H¶Û À15q¶ñ° °qÿ@_¤4NÛÕ³Iö@¾ë—èéç×¥ ¶°sBöïo6†>Ÿ°S*õòÅÆ†®æ Q¤ì{¿k gÔw­UNdLíh4|þ“#Fß¿ë"j~ÓG¹\ÿ‘Á‡¾µù€»B€•œ2¸äÊœïÕ Üþ——êäàUᶃûט!BK%@€“?>w×#•Q€b”YÚwo>lõôU`€{ÈþÃw<¼P0 É(©¹ÙèÁĆž¤JR˜`u•rhå‹ýi€&À3>÷Ö#¥á±°¾ëà‘”›ý}àî»'+K€Yü½sŠâ>àøî=w8Ž»‰¯‹ pžŒch-£­1Vƒ„JÀê ±b­€1Pˆ/ê3>5Õ¤ ‰Ö ZcM™Žšj3c2Ú‰ÍÄþ‘v:þÑýíÞÞíîíqÜ Vòýü!÷øý~{{ÎÍw?¿× ÀàA¡±U=ŸÅPó§« ëëît[‚ª¯4¬¦ó zæñùp½ Àyû]ĬªÅ #üü`0 Àd @€È€ @€0ò€`ä- À€`'À±o8üY½$n¿ÚP@Gÿ,òGª²2ëëε¤â;€ xì¸1sG7~{ïßÉ+eÿü×½¸¸{}Ä«l‡ãã@á\sµaì8Šš0>éÉŠ™·ZÉ×*MÂøÞº_Z‚<·1sëÎu¾Þ﫦ë³Ï~q@y`1ÏLÑÍ™ê~ÖtJ£cùã"òĹ@硺â»'ÀoŒ×¾ ¶öMÇb_ôvÑýKðI\¿Š?Ò6 ~LVŽÝ­qÇëÄB÷Ë)9ºÇÕ*À¶OïÅýyœÄ¿ãyY^658ÎÎã^u”K¬X½lk\!I—Ž/ãî³/”•)œ!Pfý7Zƒõ'À„d™›k+,!œž©æD6®*uÛFQ5×ïÿ]0y 2Lz ×½µÐã¶ùÏEu»:¡¹ŽõJ¿h„«¡ýŒÊ¤w|¥÷u½þ«ï-ÀŠõÙã!  ììÝ­\”• b¦ ‰é~ª0÷YL€œ¹œNzWˆŸ`8db.;´‹G…¥©øfýV.çWëϤžç"&Àk/N pJNV›k$XÁ±íÙs* ÀªhŸMü}e÷õ¯öÙLÁ °bý­VL€!àüG£ÊW©Y€e|![𛫠¢·¹UqX˜Š9fMú0,-å•ÎÚÇçª^¿ÿíÀó@„ؘKRX<ü¨®œ_ Ì»7ÊÊyºM¥³ýjÃúº ÕGŸt¦ùYëØï³c[LMÏ, üsò†©iÊIGtÝ…;ÂÖÆÎS'ë/Ìqmý(/ï-¨Òò²ã‡A€ý·_¢}É×z¡ZöX©¾i%ýü$ü˜ Òìüfâ«jàÆo¸Ý®î¤<:®¿Ûoïéû€k¢ì¦½Ï@ÕßìïéÛÄ¿›¾Œ¥ 'À1]Wê.ÕF€Ó¹/Zû‚ðùÆ<¥ý5_ÿÎ !Κγqú6ƒðTv=À',=\¾.6Iÿ°‚<ÈXýI©~¯Rå9€ °Q&Àl"g]d½x§f¢8˜óOE•«´Gzö-š¿º êë`ƒH€c.“M%NhMüc†víij?ÄhµäÝ(…òrA•——?dLû¦åôó£ŽÝPv–ןYÂ,¶àÇàØöìêÎG€ "À ¸ìë~gè>"œ¬—Ýå_ø€¼mÞZô%ài½\žžá_€ÍJ\ÓËm²<ý‡Ü+™' ¸ÿŠÍü4kùõ€ë¤ªa”Í<¢¿AN³æÈþãÍ>CÙ_ž&k}³Ê_Ý™]½HÚ+Ý9Uœ?—Þ|iÇ£§þó¤àË›çÞYÉ í¼ÉÌÁ–ßÖN=ºæ{\æd"Ï »þFoþˆR(/TyyÙñCàÁ´/œÛ 0׋ ܬÓe­}ÿ°ç. 9Q-FÕ 0e++c%Ø-Àî; ɇ…} ð×v{ÿ¦Qõwù{#‘`òpÓÝ{™á”¹‡›(+À/–Ð;wÝ¢}.Ë›m6[ú2‰¿ÛKOï<|ÂÁ,&*sÌ}öʦ^z3·îV~=à"~³4 «€Ó›õ·É”ªë¥·c\.¬€¿<0lÌ_ ÑekÖ-’Ä´Ô‡UÈ3÷ÐÓêò6›òp]åÁ‡I”rB[eåƒÏ•é˘ù܆Ӗñ“žäåe‚êU^vüPxPí³™€_€ O¿R(ô?wªX€)3%àÆ@¸£ßÞÏšeYê]{Ïhn˜5ßb^Œ‹9›Õn3ˆ8ºA{v4 Fzú&ÿÌË«D€ë¬KëYñ]I“û “;p+ŒãKhÎpe×î<–[ÿ%oÑ_#g±åµl¨¾ÖêS€Ó À0ìÛž ÓE•JguÕÈOÚQÊ#À ñY‹gXQUî W®IOòò2Aõ*/;¾ŒŽ/ãÄûàAµÏJñ€ÝÓaàÄÇ襣ðc€° °±iÆ »Ö<Á*07Å*w¡f]…QÍLIØ`³k€-©6ÿL¦;s)h`…w7lïá䯃ŸW ¹åÞ„ñŒö, óV«¯­®`F»”Ú­²¦sYÆy0'ÅÞ×ndŸÃGžûË÷äÕdáïÌ#³ö‘û+ø`y`(ع3;ë•hÊ3Ù«PlZ)í©UænvÎCdQò&éÒwõå´Ð÷ÊF%Yé*//T¯òòã‡(Àƒi?fãy«k=¦A °ÿúoLŽ>{%?'§s:»rtÜ&E ÜM&NÈ&X7íý|´tps Ùçv~`·£‡0ë¥ü^•ægD™'òu»?œ6Ãe´$H“—ûMåñC¿Òëñ•¦=%Àæ­¥gR)Ÿ#Àò<0,œ»P“ÕÂZ°È€swjŽ@ÎÜÃHöPzWÍÜ?íãi&ú\9¿§äLOõ™%ÜCyy© z•—ß·^²˜LþØû• £=ý°R$#ÀƒªŸBöá½€ ìÌM²ÒUW¸»š$hèÆ~»‡¹à~ƒH€M¿—vøN/È- JC,<Îs)Ôü–WܯôzÀÃ<éTlŸy>p¾sS “·¼ù6åK€åy`˜¸(!ª…Öè/^Êj3ªT€ãK¤›U(ßI\Ã\³ê$ѹK¯K«Õœä]^*¨^ååǘ@Û'}γ!fcˆ<¨úù·¬`ˆŒ“Žç6c‘kE°‹–7ÌjnO¿?Ü7° ±£<$;vÕÊà8¯@[÷+w¼®ü p`¤7ë¯%¾7k¯Á³!´‚‹óÀð°sŽè\¥{h‡h[õŽ‹7ô0›¤ÊLQ†Ú®ó~¥¯âð¬4B; {ä–‰±ù`ÿí›lµG{­iÞ]Æ&OÿsPk€¨Ÿ²$úìŽ6ܪ" À¬ûFu˸{Äwô‹6Ä2 ÷ö°÷hA€ó¾z`àŒJ…•Äžëñ}[žç~òµÞ3U¥7ZÉ'Ù¢Ê>óÀ pJŽÐí\”àzôLBT·Q­lZIKsÞd×ÞÉK_LQ‹ó„ƒ ­*®@€•ÖèJŽ/#k€½ÛÏÜC/ÊM°–ÓKq›Bˆ”·ŽuA¦@³Fàà>aʳë¹L€½7Á:´WÒiÅ!ðÿÙ»ئ®;Žã×Ém9qâÄ‚B2hZ^B"i–ÙÊ ¥@;¡¡-cX+Zx‰25¬0 40ñ¦¬ °A¥«(´…6Vm‚HT¬)š¦lÒPµ•P•ªÒ¤Ý{®_±;ñ…Äþ~¤ Ç×N}#ýýóÿÜsF2OÖØ ~IÍ"XùrÿõðQÀèëaIuWÏæàÚ@ì Faí‡ò-úFAž0.[ìï(†óŒb¸êKœ¬•<}[„§ä«@Ç ÀÑxý^àÄž?Ù}€}K7%Õ½e$¸Øót`Æ•-¯®Gà¨müøÙyÙ/ºz€ýàâÇÝc}–@ù6HÉ`e‚œœú/ãø$çðéoûÀƒÀZì5æ;ìªÊmöWæ°=’úâ>À%WÊèÐ Íàé¢NŽù o³t tñ ­v.û!ÈöŽ€eûô†¿~Ü\Ùý"X‰=’Xß«ñ+úd®­V‡—÷ËœT8°°~ܦ§$àŠW-á×üð˜ºP uüêïß|'Ùìï+eK²_,5îY³)úó@è9º_Û#²žËêû€m9¿_*êù6é%À`xPXŸpåѰ1ãJ«¼b à‚Õª±ô³vO_ÀÊÐy–¼ó[[[FŸzFL×-ù¨¥þTöÜ+ÒE°Æ×mùç¥ÅGÖí89ÎøF¹ì ûëM×ù̲E´^%ø\«aŽUòøˆ×OZOžß±Ðséi·Æ©Ø#ú ã®ë· ’àï#9~|´‰èE[¿U¯²ž3Õêì°•&û|vùBKB$€]côù¨Q×þ4â¡MÿúT¼QàüíSÂ7æöhÞ;‹Lûð”¥äª5ôú…|¸kU+€wkCk—ìï+ÎãEyç/\·¦þ3}õyÀo‚1ÿª÷V,xâ†ö›­ºcÌ„–&é8õ`N.8¶ìÆ$£¬®¾¼gV  ×Ì ÿjºïd—Ø£G§Wl6Rìppn|èŠWýÎÎ~[tZ»Þ0îxîûVy¶gû·M_ G>^±vyýàäŸ@]Ìé`ÎOrBnˆ»òEþ*±_Gr¼â\F}€Tà‰iåög;'«êìK¶þ€u.p"`A49­¥Ÿ¿çßé½—%`}Ô #°xß,–-?V<1ïø¥ð,lø(ðb&uTX©89Åÿ @ÿõy Paå3¥“缓óï[·¶å>êõ“ÀI?ÿy1¿žvH¬ï·À²ã•íE±Öô0×|8YTÛÜw[mý4+I`Eqmú˵Q£®}úr©"é+ù+¦äí.í€7\|2lγïË¿ J2:ÀZ‰k|_/§k›KåŸÑ&.¹š¢wË9ÿ§z-ýík±/ŠSÏ&`ÙÁO44´´Ú xl¿}gùýø´–-)ùCÌÿ±è»òõÍ~œè#=ö€äpöskw>ó`(·å¶„ôÑbêˆûcŸÏêò¹¬NG`èÓc°J´5tp¾ˆˆ®Š…–’w\]žÁjUÂêµÕ÷Åÿz±hr¾µ»ÏÎ+ã²S·,³ÏvææÍ5GzXÏ÷-)¸?ät{KÄ®ß.ìM ·l‰lK@7XŸÂkïv¤L«·jÊ£cÛ-%Åë)[Ýn3³U'§ØÃçC÷½z0-ÛÀ}׳#ío¸ãû²ð¿®¿š³±]º% ûl''á»íûëæ¸Äk;6ÖŸ²dß×ü›t=€3’óx‘Xé+ÁiÍ×ÿÓå’©¤¬(2®§¢<Ø|«NŽ—NßÿW3Ýb·Çï??ðz g¨â}_o¶$X0ó}_~Óã‚®àì ]€ÍSSÿñ怦¯?à7}¹žÀÍáv[{ä/_¹¯ ÷ïÂú`ÖǤÒ©”ïæ=0`0`ÀdÀ€)È€ @&@€L½€ ˜z 0 @€œl.Øx¢¡¥5ö8•yE‘}ÃúóM¥œ+@Úào=€kG«S˃£Æ³YªæÝ91Æ©Àš¼Ý.N ƒpÁ!Q\…áӢǀ‡ÌŸ¿€é¸fÏ@5,­®¾¼_ ½SKÇ©-È·{Ý›í–o¿ÄÉ€39ßñzOó—09{ÎìT+'‡pÍr‘t=‡ªr›dc3 rÙ’¼q²™€W¶œ0´JÆ`LÀú7Îk[geðÄæ¾%‚ïLu’llN.y†“ȬvõQô˜ €)xõ¥ò‚°<´º²Ùßù$›Q·1@&ó—07Ûé’w€Ã×ðíÐY3´§¹eåÐãl›83K­ÊZ;'Ö˜ @*°nöÖi²q:à6ï݃ŠûŽ€ïy½7õà:¸CX‡Û­g^· È;ÀaÇëà6ïñ#Šâì0~@pÁ®ªš»(X=ã”äâÇ-Ï?ÄÉdP¶5644쯟¬EÞå²q`=¿ê½]=kû¯è÷;:ý7º¿8üøcz§Xß{ãèÑè}U•[gfëo䨄‚|ldÞ¹?—rº™€…i5gªÕÜE1ÇiÖ>ªýë»saÙApoŠû÷ñ†`y8t¼ÞqXÏ2X€`Ϭ¬Ê&-õo䨔‚<´Ýb·³0 ð¦¶Z]¹8Î8®×êZEÌ€9€åׇ/VÀ€Þàs›Dß7K4M26¥ Ol/"21ë_47ǧÕ6H!Zn ŠÀòU C‡;À€”àš™ªq³f¹úÈÞè±)yè¼¼s{¸YÄqØ6#+÷­xã´ À§#!ï;¤˜0 5xhµÚhë·"Ǧ,‚µÐòü N #ð®ˆÀ»+°O ¸¯%€µt€¦àÚ`Ì5¢oä˜mHaö<Ýu†Uä8M;ÀÁU CB™ÖȽGÅg„/`€yX‹¹Œ wUå6G À¤0×V«ÃËãŒÓ4÷Vg­HÂ}G`—¸¥ý7Äøñ±ûot¦ èa.Ðxô¬ýk\ê+¶ü-˜Q­N]=6£ ¯³Ïuq²€ÇÖoÕ«ªçLµ:»Y2Nû\Üéõ.kœ¶jØï:ü֩ݳ¼|ÝÄùþ}‚ÿÏÞÝÄ´yßÇæ/laÌ“ÚY–Œ¤ ­J6Åöð¼tò&âmH14aY¢$Rl´D 7S̤­±DΔGìP¥–š 5–—’KHé¡;5W:MÚm=­Ó¤÷¼ØŒy`Ÿ|?‡”ç±1ÂOÅÏ_ûyyuªS®ä¿q 0@Ç–&­¢ ]ûX ΧSÖÜ.^Þì¼b¹Ì1À€×)€ƒ­òx½ž ™Sƒ¸hÙô\ýƒoóWAÊí ­|¬R¯ ü/íë«nÎ ØÉ¶%g´5ñÜHÅË;0︎žec^£ö¿ ©ÓÕ1›-¹lú®¨p6É üêß*\âØ/_Ékžh+>ùöǯ.UÕ–þø{0`+\êˆà‘©D"“µI«,ë?›ú¸0Àt\y:ž:¼ê1Àu¼æ÷•Š–MÀÕ{ÜE+Ü—*ªœÎ¥kª+*.åä[ÕµÊWU+¾¿V¹gm~éÿ¿¶À¹A¼ö²Î\s0YË,öu/ƒ´!<£Ejy zð.dgrÁÒv‹0_Û `à‚k.u'±{‡ØX˜Àä\9ü<º—m € `LÀÕnwÛ @À˜>€ €™·ÀÀÌ[``@@@ÀÀÀ d``200 € € €™·0€¥‘©D&»l1‘ 3ØT8¸ïÄf8 LÜ̘çí5—}øæóÑClZ@ùpO£èh.,%g¬B6ÛË_m1>A SK“êtU5ho2¦SÚò c°¬æÞ¶- ÌØ¿N, àúF18Ÿ–«·#–‹cÏlz NDÆ `v*€¼"2ŸN§¼QÎÛj·ûƒÇ –¶[l[@Y°ò~³'´Àþ«jù&½ŽQy1ت…°ÜÁñ €^ÜŸ™Òdµ^GTÆþ!£ÎÛ¦¾š l[@9°òt<{ÊZààyÇ3uüv‰vm>k»BwÖr—,Ê4€—}¤ì€åsDÃ? VSßþ“l[@yðà\³´$€ë}žñÜ'Áíù V´´Šå“š@¯È£8fô³@ßq± 4 ¼ØP¦ðÒO€[ET;Xù$8x^ŒvŽLÀìDË÷†Ñ/ƒT}Årù-¶- ¬ØVÀòDöÌ)§â°64«;Dç¸Kä¿$€Ð7€{ê"FàcçìïïaÛLÀJéz¢c¼ê±¿-¹Ïƒ•]£K]—§€­°žÁùŒ6¥»Öþ˜”y®ßn6jïûyåïÙ´“°-Øe^k\=÷Uà¸ÐN‚å¿*êvÝX¹ÎMâ±^uMÿ À®°t·Nˆü©({|"ž ’3ÖÙ}Ø–L$é¼›•I,:¬ñ̘ºQhïAoÞ~÷=Kï^¶-À\ìàõÜóX¹ü‘O{‡úáX €N¬ û§}ÂUXXã±Ü1GF=äè“·k>ûò[`¢œ²zFå .ðÈË”×3› ·´ÀèÀÚ®Vý15€µËªDj6è¼­_°Øí\ `¢î¬SOö¬p{x邜 Xyãy\=$øg¹ëßµöwtÞ\0ÀLìïÚ—þ«âÍegáËxœ@×¶uZÏä½¢¡9¿âMƒpý™šÏ&>rW±}f àzŸÍ¯]þu7 €¾|W `yþv®ˆ”ÿ,Øh'ÁºhéýÛ`¦î)dïb k{@Ÿ/Ú%š`Û8®îqåïÊ]wPYÑæ2HìFËÙû0¬½í]ºË³ÿ¸5bÔëP¶ÜãS÷}–&­ŽÑÜ$vŒr`v&€%™zÂIù¿Ú¡¿ê%€¥NŸèPöÀJÆr×FR®Ò@ G· ÜVl`Ú'"êûÍÁVí‹àŒXyhcÌÛcçìïïaÛÊ9€•ÓN´k‹ÁùtʪÍaiÒ!/ „´,&€Ð#€åÞƒ×S!!"s…‚³é:ï6è¼u^±\æ`€¹Ø–œÑÖÄ'–Þ>8Wª `¶Àþ—!u¾:f³ù‰œuíâÏ[ïù¨yáIùžºxù è«ò·jö.îÔ(56ö÷¥·¹åÜÿm~:ˆPªn#aVy¾6üýö£_JW,[ß àžFÑÑ\Èß«Å'r+÷CÊräv¸løÝ‹ÚsÝv«ôíúÇù[Þý³ò´ß|Ñÿ£?Þz €Ë"€?lP^(lùÍâ°ÿIŸWŸç^($?ýéªyö¼ÛÖôq>€•wпøxíW9ëá‡7l’ò3µgâô`êk}Ý£Û ÒÞ°íÈÛ;÷óVþ¾ËŸâׄæÔ×q_Ûl›åžMz’«°Á¥ëߣRóac_xÓ6éQT™’Ôsbýß¿T }ÿÚo ®Àv=Ø¿N,°ÞÙô@ˆŒk}Ê+çÓ)ሆË4€O\û?Ÿøèƒ¡ôÁ•7þñïï(|¶â«ÿ¼•`m»žÜèã­ùø¸\øÑ39p»ž*…[YÀ¿“’/æçÂÿcï|c¢8ó8¾ÃlöÙàÖÙ]sjºZðäÎ;6V¶›C‰-«©[@Á?9ÉQÈ•zÍÕ×ÜQO×MÀR …šóE $WÔK)%h@µé‘¾ôÏÍõ…’^_™¦¦É=fvfžyvvfšžï q`æ7ó<›ýÍï3ßçy„’&˜žv{¦VCÛ—#ê‡Uȃ¢èôÊ dïù6`2tdú¤ú¢…;Ÿ¡½TP«×o °kŸ›‹‹+ãÇ{`îSòY4¥¾óšX÷kùE`ÛùÈž ŒûÆß”ó™·ßÐ_êñ¥3v€YŸ‡Mú¾sËö—nv¾ÒD8ø(ú v­tžƒ Ud-]¤üê±×ëpd³þ¶ü¡Ï÷´çÑ­µûžº0W~üÑ_:ZÝ•k-Æ3‹ÏÅÅÅÅÅx±pñ(+ž­päfA=ËÚú–/OÀV1y»YþÛBgTFdYÄœÆRášji¤ÜíäB¨ò§ÀîØÅò<Ÿ¡½Tªõ]‹3‘°yåÈÅÅe Juƒ- ¸*€­å€mç#[k¥WQüÀ“–ÚOõ>Þ·ð„.•Ìø<ì°ô,çÑK¯eÉ 7ߪ2ø¶!{¸JáÍ0éEÀ­å&c“¿úÆç»ûØw×ç»õƒ—0r‚³ëý±Ï-Æ3ÏÅÅÅÅÅxqp  „†• J< r•JW"¤{Aè<Ëè æþ¸€Ýž™ˆÔÚlò˜ßkå.Àq±r~+¶ÈžÏÐ^¹?‘½wè°ü£žOæâÊØòDÃlµO“~lØZ~ѰÝ|dG{GA¨„ô…`±ýºÔã+Ïç6¦`w†üçkA)ÀwËÈ6mÆÀ[³ÒÙ‰bÊ[º%g•ÉÇ&{]ßB†üÃ:´™à¼AÏï­Å3ÏÅÅÅÅÅxQ°8A£,¿€uÝk»!ãñ Í"À"´{XHÓzõSÚ~zðžn/݆ ?˜ˆuJ€¹¸æã`¼ ´%®Ê€­Ü(´¼p‚w‡úrÓüd¨Æã3u€ÝFœàÒC„w! ·8_ïCŽð x5¬šÂ-+oÌ;÷LæßVþê^þüŸ÷¾žÌG:7¾ÎŸôÊ¿ú" £E˜Ûgþ޶ÿ©ñòzÿš—   «M®áÔÃÇ€¿Ç‡'ØQÐøÜZ<Ãv|êd´ýI· o=_â®õfÇ],öþ‘W„\\\\K€Ñ"¾ WŠÓ§É;^•Ç{žœ>îøäú¿›5ì™JgsÕI<†÷gÄ3hà2‰/Hp3Š·$[𹏍|%&`ȸl0–ã? Ò¾AÞ¾4EË5«wĉKñ«þtÛk~~º¿±#á¹û»ÿÜ[¨€T¯›<=92?£½t,“¿|ÍÅ•ùã*idÈÙ™¶Lå#C¾2|ÿ5ùœÊ/†ü£Äw¼ƒã[Ý_½qì:<~}ÊʼáÀCÞ×·îÐå§ß€§PÚ«Ýq¼i0®_¼rúR¸ãƒûë¨þ³¸ ´€—)\å„ÿ…Œ§‡ª–]«r®Ì7Cè”áq]O~þ~D¼s„~µ<7‹·“𦫸/Æ^Ò«KÖå+V¼ uÓÎíÁø‹ôýÇZxDÀ‡§Œgˆÿ¢¼e¯?PFî—ê4€5ýg€7mVlÞœB§³ m£û³tvë¹H…sGÓ¼žK`mýüY/ùÍäk×­žS"ñ‚ÙÜvP8÷á}A¬]etl«Ìe<¾á¡ÏwwîÑ­¹o|¾ÿyUÎÛà9âr¤Žgˆÿܰß3ñåGñ«™CŒøfyì•¡þfÀ\\\\K€§FA|ó–n@¦ÅzÆ#RÍÔÉèt™ À} TñÝÉöéËC*+üËr€õñ@ r¦.FÛg"¸ © xºö'}‰©\†GüÚJ­-…âÊÕ itzG“tvƒÛ:# x[sl¬S­Rêý¸àtÁ¹¦VG;Ól¯}x¸ ”V=[Ä£"E1-îV Nôñ!çw¬Úd%Vëç3´—ê†Ð$»‘ö²äþ3—eyŽÃ„ØØºÊ“00`:ÑùÊøý×ås:¿Ðù‡Žowt1èöÓ>}ôm+̃š\Çj?üMëL$ÔòlGÀÔñ)ûÃpýd üèt§ºÀÚþ³ Àá 'Y+Ô’åt–¡!Ñex¬·ªáNòúXó À§îìÅ£¡!àºzfóg×ʆ° Àù³“ð/§’°ç¤8±ÚáøÍ¡ø„ѱMÀŽžGO×õ<ò½Üððñ~Å^ñb®gb•Ñ:ž!~A³§wüù³ƒBÝ*ÀíÍ¿DWʘ‹‹‹k)pøéµA¼H¶vë‚Rë ê0" ±ô<*·èW¬ÅüËp€©x†ŠªZ~™#VÜ jðæ@7{PòšjXùå× I¡ ¿ÖšÝv8éé@¸—>QМ3A뀊ã‘ðHQšíµ ¤ªÜÙbJ¼âQmQ‰» RhÁ†Œ†\Ëç£Û»7%£—XIg@ÍNÎ.\\ "X½~ŸUR \¬|d–Ýä™§š¯ ßc>׿CþaÄ·³?ö·á¿ßoe„PK½s×Ð~ÀÁš¨% 6oÈßFXýð~‰ç˜ÀO¤µ™Àºþ³ À ¦Ðyt(Ž–uù³‡*–uâéÀóÀ ²Ñ‹ÞU„w÷2U“o2v‹U—wù=`ÏÙ}Ót° A°ï¯Žåx³òàÀl%žnûùaÍZyÿû »ÿræâââZâ ¤®Aò$ª‹uʆ«2çÞà‡µ`ƒXå_†¬Çð/d+<µ‰½ê˜ûã_k‹“éíÛÿtcV2§Ë Ћm0×úh‘ŽÆ˜s×~ÑŸn{­IN8À@Ú†?ŠÚ„k¡`ôaÝ,f/¹Ü˜Á`‘Ù»ú1—’V”\\\6„šXP’$™Î¦ó¯Œßc>׿:ÿ°âÛÚ¿Ödª#áÃã2ä+ò¹ÞÑÕçV|[û| Bý¬"°ÀàJ€ QܸnTx<ÎoB¯fްY˜\DM°¶ÿÒ`Ðqýý­[.E6mV¸ôÐë}h'ç ÆÎ/²LçȞĜ`€åe².€‘ïKPU<âJÃF‘¿ý×{*WþbEä“©r±nupfd~`<…à!×`å¹>$ÂA<¦X…²ãéI¿x¼ï¨ò/ÃÖÅcù%nµàAáe0вŒD±—=TY¨kÒ Eäs&]‹Àh-Uÿ¼°©ãb·½–T<"˜0ž,u™ŒŸ‡U µ‰²¦ Ž8ŽÁŠʘ..®´UÜçžÂ‰¥L`êûH壔bås½£«Ï?¬ø¶ö¯@¨ zÈb²` a68Àò\hQdì /êØd° uýAÍ ŒJX¬ë¿t8ñùýŠ,‚UîÏB c…[b,Ç*ò&$ô$ˆ·^eD´Öžô&?\}p^®ø®#=Îöz]Z†¿É¾Yäy÷ÿìmlÇǽìÕ{ÂÖ¹¨´ÉY˜€…“œ()øzjM N›ÃMŒ 6ƨ®DTãH J©‹]Œrjm Y†ÈÜDUe[¤‘lYÈ ø #ŠUá¯|„JýÐVHùÒ™}ß¹Ù·»3qÍÿ'$ß.³³;cß3ÏŸ™g²À•UbûG:Zø`1&1£o~¤ `6Y÷R¦˜áe¶Ô—á@[ux,¹¨bûyy‚£˜ÆR¬#Êå'—Àå¹F€³o¯×ýyY  ‡¬‘/€é:]×Ìåù‹‹ÃºÃ-+uÒ™t =UÀ‡1 €ÜhŠ+“9"×øI—Øï'ó}{äCgÚsfM¯ÕþpëT^ëÕ}~.úH8I¬j2·ArTÈæõìT#ǰÝÞ6¤ät«ÆenØÖ¹àêõEýÊ6HãëÛšÕÑùß©@Ý é[ºðýR_\ðe@\¹“~2«¾‘lÁØøûŸ M›}ÕÇD€õˆ´0@Ûhå]¼Ø©§Rô°}; m ´¶×!`°ùnŸÐmHùŠ;ú%ì»ÿƒ–Uq>pþ¦@»E€·×DvÀÜØ]Gn*‘TåðffÒì[¥š ßÕý@.(¥ƒ@”—þÏ5,ºÚ£Ø´çLD×&hyõ,/„#7zR’¿´ñ%™Û•Àæõâ)¿`›½mŠg¾Vú/œT’>ËŸ¬ysÏ›Zz¬£Ò2`e'$}¤FøÌÝ`˜ÈPštêÝ-ÊÄcº7œ“ö_»x  €0][¶7lF€kjõ5Àv¿€ àX§$ßÚ©¿nW¯¾cÀj}oìôê*ú%_kb“U1«¿®àvÀnI°ò'€ƒ·×K2ýí#ÜO8ë²éR ûy `Ç÷ Äc?&@ÁõTûȲ²×­>«#@,ÖùÀ{îÑåÕ´|X"íCq—}êlú–!H×g¦‰?Xí¿0]ý›"²w}(4D¿T ÷/‹>³T¶¤î¤ªa%å•’ü9ˆ~÷xª˜ P5ûë!7üÜ¡üG¾êc³@×l€`'LFr¢ÇˆC¢Î¬£Ófõ,жµµÊÒ§úýRòJ•u°×Ë3õeÆmSj:¦îNuÍ•gVdf$Ó±é´{>4– ;0‘Ɖ&^sž1p ´硽÷gûÛ;<<(UŽ ×ò^Gdq?N{]pS\/ÜÔ  @nÔXV}òöµ j| `‹=w‰èrëZ^1*s1N†=·2•’W9i}`åúhö`# ´—Vû/{œxg Í|%%ö…^TñöPæ4é¼`ªu•iТ„¿Ø¬é_EûÀ¯7 ¿%?_Ü«èà×g"]¹MÞTª$¯òQŸýøÄSÕ󭿇`û]qVÛ8%Óí ÉaRUvŸ•ÇTÃܘîj˜8U^Ü+þGÅÙd«Mëõ©laþ¾önŸ8pÕ[¨¿#·¦Þ²î‹[á°/nÌ>[ô€/!(4""D¬%®T¹K´ú —C°°±¤ÍC{=îÏö·g˜xh/ÿ,,NÅåÊ‘pî÷ã´×+¬¾?Ñó­²E0“å¦}Ëœi,׳öˆ±WŽØjÏ]"ºaký>"Àaöyb!Í”DŒÅ8™öœ1äÛ>ÂŽí÷À¤´²Ï½5ìÒö¶!®ç6ˆÌ–: `­ÿ|`™¤X‹ó÷*¯<¿[tT[ ŠÓkŠ&¤eÀTôjq_Uù.Ý~¸TVöЧŽ\kþSj~LˆýN=~鈻9Ú2&¼ÍVoxá…¾‹[„îŸø«Ï~üÜ•häÆÜ~ÓÚr¿âwÀ¬ÐÉÏÇ:Ú j‚–øòùõ×›¥—¬e®”’ãÓO¾*Z¸úCÓ¸¢w| IDAT2©–&ù=þU[­üË›zh[}ª$"–è1¹zGÛ Tý ®9RÝtWûã~‡IºÄѳn YѳFbšUO°äÒ¢â~ O˜×7¤$3,ÒsD¢•’QÛ¼`ç0I0,HɤͯŒ6桽÷gûÛ)Lµv_äš”t\à~œöº àîýÄy%¿¡Èãff19 –%wôTpQë&8Ø#Û÷™µGŒ½ÊÀ<{nÚNDÔ¨[c°òô¸ûlz~zlgÇì«q#²k³çL’‘`†–Œ=>Äi¿ƒ6ûK¹>j»õG†½%†U>¿ØÕ>Û6ª¿!°ÖÏôŸ·–?Y2ØM‹N,\jÙ*:šÒׇN\ºt.:šZ\¨„{UqY°ñã»4ö[öÅmucàužX›ÅÞÝ©”*T{22õíÃÙ `ýO÷Z±¿ú˜ãï|®ý‰“¯A°mJ,'´abRÝZ°î^S\ - ++È`›°`‘”ÛÕ(èå“FŒ}€Õ õú‡@2ô6Y–Ïjа©f奷œ8ÂMÊ,Vô[S“#9ýÙ(}®”'u§/´,ÌÅ-ZyÝM>”dýͺÅ":ß1þt ´@§SÞjÌC{½îÏô7?¬´W™Ù7Ù¬(ê]–¸§½®˜^/÷Î/ŒJ®«Þ”Ô©û"'u#Cí 1(ºf˰G62ìc¯øS®e«=·Ú— ûc©[c°òŠÞo(Ac_w«=çňAímYèiÖ“hÙÚÏÀÖþÒ®?7g™RîÜ™ö6¶[Y‘KþÕ½—Y?3f#€ÕãsºÜM^ØCOü´‡£ó¦{ÝÕ3GŸy¸T¶ô÷Í4 =éKw̾ý=]Nïšîê¸q¶x].Xì˜}Ð_è³¾ŒãÞé®hÇl[ÿÆ`€¶:8ÉÞã÷ucøh_büç#Ô×Ò†úØã¹>9}y~zÄÀa±!®æé$åÓã3UÆ>Àrº/e­85'%ËüÛú¡æÄøÌNsßÚöëWã‰Þãcla3lIaÜ=×¼€{‚:µLqääù{Æ #7ã†Ã$~hñ OˆÆCe•Š+øbFû’éùŪ|´×ëþöþ.wÀäcµæ×ÔJuïå|?N{ÝpxrNuìêf Èb MevPÏ*W¢¾³|c‹ƒ=bæ ³öˆ±W<l·çVA›iŒú¹Ø¥¼ú<±×GûRrz|úž=ç)àa%*Ëu{9íwÀ–þÒ®7£ÌnýÁ±·âž>9q¹ÅènkýÌxès °M§/Ÿ>}úªUí&È™Ñ8·t¾…µkM9¼±`òá}ídñZï r¡vÍJ`]!?€¦#v}Û9çma"Ì9e.¾5Î –¢È®y²OÜ+š±ÕFËŸÒò!‹ô@ToÂuúb'mKÀ"³E¿Úºh<¿ØÚsnë¢=KJù}–À«6rS]éìDý‰­¥=SÛ­rj¯×ýíýíð+äbÞî':u-¯oÈ/ìÜŽûUÐ/ä&€E§oyؾá‘Û÷“±GöÊÁë{᫼þ<êÌdÑ6Þ˜öœo ºÉ°~,Ì>‰öëw< gÙêcsÌx˜…\À)ìå’äW‡9ÁÎX§TéÏË+&¤ä­ÆmLýMq—œÇáîÝ)©úÐòzª9´w5"„EÈV v{$®°ç ±çš`ÎiL##ˆõý`ÞúX9xÙpÀ†cñxZÛSi*ž¼â×ež¤Þ}cQ¿Ñ}RþkÔ1ÙVkY_¶Ü!›¬Ú «ßù{7{¾Â €ÅSrï¾']³CqËš5oEû*]ñ´É§jœ“+¦ôVÐŒ^‰[O+›e{`ÕÛ#_ÏãfÏ!€!€€•.€ÃSzîù| ¬ØÞ6ÚçS ‰wnEz|ûØÓ }dÙ^XõöÈÇó¸Ús```Å àpý+=­©d¯CŠRg lÇμU•;Y¶V½=òñ<öøz°­ †EAXÆ ìÊÊ·´üí€ÿO{äý<«(0ðl `à™Xã-°ü@À00 @c@ €` È0 @CÀ0Æ[`Å`9ýÙéÓWSOo@>óïGÿ-¶yÿ_ÿyôüóþñÚŠìÕoþó/Ô>®;ü Êÿì~ÅK»ª„È/ð Ï”.ÙðâÞ¯K\Vv; ÚTû{gÛÄyÇñsì\,㘳%e¹·åE¼”Q!tÍ ‹`*+ee@9uÖj[E!Ø€%1 mË€¾0¡Ž©ÔRµZ`†RFÍ6cóÇÖ)ۤɕ¨Uø£ª¦©•vÏóÜÏöÙçįßOÕÖŸÇç,}ý¹çy~Ïw­GÕ¿û§>ßÎíŸMÉÒªA€¹Û±dº®¿'k ݇Ìû‹/ÀâµÞšRü÷ /cWi~-Êåoò#ùÂuµKêã¼Ø5è#ß~ ÀÎI1ó¶}´tœãÙÌþÒåà…Ó½V²µÏ*Šs5²¥Oëj<#@€sà(vÞV@€ÒžºÖ9½ To¹8}´¸ó–;`3fÑ»Ðiýe©JùIÚ–àRðãåà‰-î+ø08§9oË/À®7mËšr´€Ê p]+I]=pÛÖ:žWw"=åXq(£¿èøœV»J©I¥ðø\Û¾oçöÏö³—LXà\)¸úמh·=úc“vÎwIòÏ-ú W¿ôÓ«Wÿ¹‰¶÷Ñ¢WG‡%Àcרw`ÿ/@€s.η˜?Âqc†b¡èbíMⱟУ>¡tûC±¡p³Q€!h±éø@<ÊÚª)÷Æ•—GÃ|V&E·_¢:^ׄØó˜ïNvy× íGÆüÎhçÌo ,\—&À£4^îЖW€yž0O0}ÅÌ·Úéw‰½ï Z€Ö_î"Ïèll› ßž?ó)qÞEïž:v­Oý œ;MXŽS] 1•Ì^u¡ iíˆA€uÿÍŸˆiípJTÊ"À3.ØÈŸåÔ|£óª{DñI%íD&Æim«ü* À3f96°ç¾1ÇA7—X€9Q– ÖX?ɛ娢úAŸûÌõ_°í=­èåö½KŽîÒØØ6ãš×›ïž\E€þ®ö¿mýëºÚñ4ÏÅ©û†)ÀÉ»ã À#à¸?éQ,7Doª&”á&i|BZâ¿Ñžñ SâQƒ'çÓÇ•žH“Ô Œ«ý±ž†Þ8óg~víµ—ŽüÝf_Ù9|ÿ§]õ”Þ¶Ìo ÜúƒÁš÷TCÛ\Ræ<œQ€V<­Ë}PâȲbÛX€nïzí.Íš§´Í”[à|³öŽçj¯Ö¾? ÷ïõÃÇW€]ùî¸0Vþiâdyˆ™m69N 2¡%ÓÃäyYùWÐX÷_!m<ˆúC«UÖ®§äª,ÅUÁN`wŸýŒÒ1õ‡¶¹=™3À–l•ß@¸î‘9Žîã­ý'kΞ,s)Ì‹‚¶XÄ̱Øî¤5<ŠT®fê$ùià”¶É2ÓÁë^#«Í¸ö}*¾/Ô¾80B¾ÿUö À#`¨™cfe¾ËRK`Åc£ÆÇT€“ó¿ãÚÖ_&Àz?ŸHî)N`§ý É2Ï >÷æ{€ŸLÛókl[æ7P ®[:ÇAyéWË+!À=¦7{¬‰«´zR }t • &íoÍùæ|“¶ âµÃà_fï·Žn¾×ï9ï›û=|\ 3AU)¦ËþË%g€iUŸ‰ÿUë0§ ùcR&Àz›¼0b*À˦Ðñ“kÚd°…[æ7P®Ûóá{gŸ=Þ1cV…8gèi]ú’*§ûq Ú쪇3i›¡°®º¼—ËD€5Ù¡“Ø~ê.|\è`55£þo8åˆ -™ÁmJࡤÿfŒW´W½ž*ÀzE,&ó¾„×'ÙŸæ‡=l™ß@eX£íî²Áâ†7¼°Ý¾ýêä•‚a@z;‹ç»%—ƒT¨»ÏáÞ6@€‹%Àt.W_¨,éÌVH8¢/€æ„´ñ‚.Äê 0)‰Ó0`íàÉ“ì¿þhËü*+ÀKÇÚ_3ÀÚè,›O ¯¤Ý—ˆ%`—¸í ¾{0 àâ0›»Mh[zU&3À‹Ó˜.nÖG¥ŒO®˜V8f˜÷õäà…÷‘Gº»Þ´å7l‘ß@E¸õáVºêöä `Wð«¯4¶Ü\ f¾j[‰"X.ŠSMÎ袬ª³Ù`™mDHJŸœ°ªÒlauvtV´–±êl¡ ™I0;óÜœ;¿€Š pÛZÇÕ7÷¹‹/oÛºùs{F(À·nx¿äóûÙ² °(Š),ŸÈ0ëõ©_ @€­ò8S€£M ‰¨ŸgDL6ŽÇü~ÕˆéÚæXxטžx4¢ 05àŸ9ž´CáxÔJ0&sıpsCo|(u-µ&Àî÷6ëøð„­ñ yÿ ëm—Žo>a_y9¯"X–ù ”C€G¿RãÐy€´GmùÑÍ:Fmè0ë/‘´YàÜ3ÀÜÌ·ÚÙ— û©)#àÀÞœï`-Àµ„£I¦í¥l»†Þ&Îê¿y›<¦(BãF Fi+ÖGËÄu“Ç©,qáÌñuth5+‚Å ²bÂŒP™Ó?‡Í¶÷ç4ÑøÉ-ôD£3ãó«m™ß@e˜µ·\쨫çžVµÿ|ŸoçÝû%nd¬¶÷fž¡[ž¸Ê}F€&À!ÅCÑŽ%˜ˆÇü±H3™ù²•Ç ‘QÑpsR€9>£øôñ2i‡Ô*ÐÊ¡7¢ õHŸE€w~ðmí`?¾í|ßÎsÏ ž|Ø"¿€rpfïyc÷îÝowä·[©4?¶É^\¾ÀË‹ò­¯‹P”YÌ{ä´®ÆÓø0Àm'ÀöG»LÎày0SÄ XR+’õØ”9Îà®.ÚÍK<{d2ޣȴÔêCË2y½ äÐRž7;")O Îo ø<Ì@þŸ„Ëû~ìÈD·™“%ÃÎ’ p±ˆëgÃS@€ÿ¸Üt¶8O±_n/vV©KÁ[ %cÚ j .õƒ>Z)ä ü*\3À (Ò3^ê‡ü!œÔ ."ÿ»ÏpÕpP¯*MŽ Àà¢âE,1p•°0Š…ü¡hXÂß0¨æ¸ê/›Á ¬j4†U&À€ð_öÎæ¥­¥ãr"$$ âÑ'<Ä  Í øÈ…JQŸl¬Úl³)ØM©æZ‚B —>Õ…/½Ïæº(Ýz]¹ëåù»ž™ó’œó›9g’ZéÉé÷³h=3gæ7#ã'gf@€ À @€0&d @€1!` 0 À˜o€èðÈñ§££Ï«žôÇ££Ÿ#?!Š¥Gaé@æ¯Ú%œ¯ë㤞öûœ9ý¯ÁÞö‚ûåq¥—A˜<Ý]2Jí­×éûëïñbêy?eõ{=?½ÿsêÿî]€e¾\€y¶üKgú½±pÿÒNÿ½d¥ ÷Q“ÛFíAHÚÕâ&sÙ™rÕ•-OŽÏúp71ò¶÷>ÊZ{d}zàì•aÈYuúŽþöÕ¿¡½Óõ@€ûà™o€Áà… “u˜Ëpéöz×d›o-Þ`©öÿ®¯Ï›¾Œô„\­ø}¦ƒ¸PLŒ:?ÎŒÍå#-ÀßV=o<í½ò£ÖÞYŸ^8¹m”¶^üùê͇Ieú®þöÕ¿¡½Óõ@€{àÁ™o€AàÅ/g¬´Ôàj‘-¿°=x_ü?rqbý·¸]fµ(OÈó­Ôz"$íàƒ1ç[‰ì¾+ 1`Ú^ð`^ o¨Òô]!ý  ª<(ó-0È<ò_ƒíÞ0æÉgÖWu“Øß±Ÿ¯uŸGqB®—N†¥½¬±‹¸ pœ…s¸Qö¯U éû¬?€èððÈð`Ì·À` pûë/#æÓ®ó¹ó\Ñ?/´ØÃOÈãÅÒÓ°4QΜ¾-Ù4 àgà™o€àáEkã‘+ÀÕ&û½3oz7!ñŸDwIV'`ÉükÑÚ¼"ÀÉãë¥r{ë}àÍÎÌT{Épx'K·'¯òg®Åå/O.G•"Ï#y¼»ÔÍ@ËÓ †÷~Éó²³ÿ3}eˆçŠr{¥ú|wIŸîò Ýž\ŠI¯°Ã„¸g™9ñ¢ñµÈy2£¬®?|ùñ ‹W!¿3•½Xz÷~ˆÿ{ûÞXÑ€öÉ„"þsyÖY­\h*Üæï qÝpÚ—Ù6NxëOÒþöÊñó^ºa îª}“jå0¡߲ІÇGw½®¿tãÛ›_Š/¿ÄN€£=ß.ÀÃD€S®¯1÷KË“¯ŒégÑ{<ë[8+Îãl¼J&ùÅtÕ²„$sZ±S›Oªüɺ{¹r‰¶„yû`»þò¸þtQ+¾ÿ~ÙÃzž©›ÖÿR{¾úôP~ß,ìÚg3ËÊ„À¥íÙG㛾p+Ä…HQ]øòËñ —0quÍ:ˆ™ßPëžÝ€å×r|’Û†{”ÚP½¼<£`O~Y€3™¤Þ ÇÖA’¦í•âçsÿsñ¹O¦nXÜ(¯Óþ–⩉O×ëúK3¾ýù¥øëû€ø pÄç[ Vöêͧ“ µÿ¾bì|}Áÿì?•ÊÓ *½_¡h™Æã<³ŸÓöúësÌ+°y"Î4>:søKyóäÙG;^´¾éí2[æZ½0”B¥ë~9žáñâvdníO>ZÿÎa}»Æö¿~¼1Äý¥ú<®¸#m~M5æH~I€ç[Ýòĸ¤i9>$~~bç{úб‡܆Å0ö÷·Jh;ñ©ö$Àþëuý¥ßþüRüµý @ü8âó-/^\aö!X -ñ¦sAÝLm­FwB®›þ]“4MØôЯaœ3S룶ß(…›¥ý(×ÞÌeÃzÒ—Ø»±_¾Jó7Ê›Â[Æü|¼ÈÚåw“Ö"n‘Ÿ–—ÉdÄÓL÷ !Þ/‘3¹ ñbºœ}íµë“L8õÑ–ß÷g+âI¥Uz&a+UÛ\¶ÛúòêÖ&º‚(ÕG×$?§&^…üæÒÁè\eúì`ôqegŠ×2õÒØšàñ_cÓ³r}ÒWÆ“×CÝþ—ØŸ?D€SD€­´Ô^??UQçñb[,€Hn8xû›Öߟ'3ŠÏwt×ëúK3¾I~)þÚþ võùˆ—7*lÿÃêâéq{Óà‘ú’z>ŽÈ„Üó XCÞ÷wN' `î¶7eøèSrqÉ ÃYÊi P£ìÞºaŠ-¡RþîAìA£,þþ§å iöXÊõMž—ÙÁ¶±Óµ%"À´>ßw°˜„¿‚lvÈ8ZßlË5õÎY_}týAóÓxjâUÈ‹ÏÆ›Örܦ%ÀnþzÙ©†?>ÕJjÝ*A£ù{€“tϯ7-µ—ÄŸ&ÿfÕÜZãc%Ûrn@Æ7ÙÓë‰zxèõºþÒo’_Š¿®¿ˆG}¾â&ÀÃugËé»7¡¹ÌÇј{?«vÝÊÅ9?·8¹á$þÔ?LÈùù7J!úð\ûößù¹òô¬\žN0õÍ®¼±žó™|í•ë£àŒK@Zê¿wr£±{ïZœëEëË}òùPˆPéúƒæ'ñÔÅ‹ غÐH±šÙ`çÁqµÂT@d¹¡ÍX)?Ø ùû`©½$~„ùµÔS±ôyÛ8/:Åh84>ºëuý¥ß4¿];Žø| ÄO€‡ÿ>+—n?¬Î]ž£1!Wɳ°j%ä…DŠ× … pÎìîiTý]Ÿ3]¡±X<ÐräeÞúRÊŸ9Íó¯nßþ'PÖ;åÍÊåéCU_áü‡Ú+×'\€» ØbKÓO:ì~:!œ™Ö·n¸¦*]Ðü$žºxòâ"ë9ª+Àî]æòNI$>9ëÉ|¦®^aMó÷+ÀR{Iüü§ëé•éÙFyyf®RíE€C㣻^×_šñMóKñ×õ7qà¨Ï·@ ¸ó†¼sVÈ|‰ ¹°¾A€Ë¬ýÒåQ˜pYìÙ©h«Ÿœ?±÷éL<ÞšT ‚ûDÌ©<­+ê+ö•Ö&‚Ú+Õç» °´uÓzO`}=;Q8¼?¤ü$žºx‰Sˆ…€‰¬Ž»Âú¸¢^‚Îû…«¯ú¬!)ßLÛKâGÈl‡âÉo¡Yz”3… px|4×kûK3¾i~)þºß?b&ÀQŸo€8 pÝL½µŽƒnÏÇ‘˜¦ÿõ34}76Ãןw¸QV?–ó'ÓÇ»¦³“S#ßðXú¡ØÌì“„Ôí%õù®{€»{<Ø_ßî’oYõàÐÊIùµì//D€;Ïúi|¬ÍÙ9S½ƒ–æï¤»íÓ<&í `®‘µSó ‘]aëuãùн °®¿tLòËlâ¹/ø™8òó-c^Ü0¬ó ÖØ»_†#îŒ/2¾}õïA€C¯×ö—f|“üRüuý @œ8òó- á,þŸ½³ýŠâºãøÎî°îÙe—=lˆ4nQô€ '-Q8"šDÓúü€­Tƒ&€bO ˆJZ‰UL[mÑŠ4cµÝ#£ñÀ›¾Á¼È y£ÿAßôÎÓîòV­ IDAT<íÎ ³ ßÏ à²3÷þæ7sæîgîF€Éo¦ÜVÉöÃgs\ü@pÅÒèë“W sêe½ìóùìb&egˆï[]]ë{N\i߯:‚h¥K.^i§ÒǾØ2»ˆ.éèihä'ËÖOÞ»®¢£Ûç+=“¨¨BHäõ±£œ®Šn_ißšéËÚs®æ†‰UßÇ+Þ^•x"×o˜Ì´««£²r ³]í!N²xIl®®¦tº¾Ix/­4ý!__Sð¤õ©°ëX·¯¡¯ÎÞe“Ïê:Ü §|}÷Y:ûXå•tzÙu},ß^ &Ç?{|“J‚SìeÇ·$~qyÍý¥}G¼¾R€5ö7fàhïoSpÜ9kè=#˘²«n¨³¦ˆæŸÄQ¸#ô1;$eò†Ä’C‘Ê:˜!øߌt¶Ìé%ŸÉá6ߪV³³v{mò¡Ùœ;Ûø)¯{Ç®²>!áªc† !QÔÇMŸePê–¶W¸„÷Ú¬ÐmÀâíU‹'rýF±çZE/^Vœ,¿…\òë& ï}•ÆyØeëk ž¤>þ£Vä'a±ú#°Y–­¿œÏU‘¤ï)ЊíÕ`æB;}aCNð8—ßâøupäå5÷—Æñ-]_)Àû“ pT÷·€I˜+×Ý- rí tèM^ž¶8›;‹¬Ù·®÷ª‡ðuQöí¯…`ò¦öD²xðÛºt}wsÓPQŽ«îöÉT‹VÖGêè«)Ê® ¸½ÚµÁÍÈLç'SK·W-žÈõ†©ÎšýáÑõ)¼²ü0ù<™ÄÜ-»K5ý!]_‡à‰ëSຢÒøyKøü»ÃO;àX¼~>¼è`ùñY€IµÜ@©(}òã[¿޼¼æþÒ8¾%ë«°Öþ ¬4âæ¾¦¦¦Òiºø‘·Ï™Qâ‘òr4b÷9_¼Æì?`<X¿E¨ÊçÑ·¬ýŶ×nŸÈ½zQ„x–/10X>±ñ½¤ã'Âòѵ¿ˆ6¶mªoŸ«ÿ=ÀQÜßfàÉÕ!{|NO¤2?„KpŸµJt ‚(€`v®NÄã™aØ À˜A€K{|'¾NtíÂ4^€`fö¼Ï<2NxQ-@€0fàÏ]u1þ €`LQ_§i@€0†`Œþ€`ô·€ @€0€`tÈ€£C À€8®ùJSÓ@©P,`ŠCåèêÓæ¦¼©½”ûú£ÖøƒÏDÉ~‘ÇórãKîë:º>Uïò wë5ØF~Í£Ö™s˜¿ŽÇÄ|ow_ŠyãOÜ'ò²ñ|‰âñg¾WƾÝÇŸ~f¬jüW×yœ¹0Ïœ3c•nÖîoËǽ/yCBçsÀ,¼!.YÔßA+M¨ïàŠçŠh–uwK'­gíd5É‘÷‰áú«ã…úgmÖ£YYŸ“vl¿Ú¿TS[3´4â sk^3ÔFVû½ùû翊ù¯Åùï˜?ÿûL^ÖS4?¢xœQ(ÀšùŒx±aÙ9)÷¦ãÜÀ`ýí©€ß¿mÂBô”=h1p>Ì!ÀùgéξÝY“H¯;É^^I× u2e×±É*ÀîËñ)÷:ztïÁ«ã»§`–MoêÇP|/*À¤1Ç‘§ý/xAÿ­y±¿Ž¸Êò·m§i#ó å¸ÐµÞËí¡}1´x¶Æüí=‹jYWÈ’üˆãqú|>²Ñ$ÀšùŒˆ³¹æe»±'/¦œëéoúG’&.ÆÃÃþÇÎ瀸àz;]àÂÅtI%çÁõÌ︾v ¸-Þ_9Iø­y¶Ó¤óvާ~"À)ïà5ç{š÷öGX5ã1߬Ͷ{8|öqlÐÂ*Û–C¬VžHÕ)lž†JCß}È÷¥ØÐæ¬}Ìdçã1_t«—µ‘çGÏ,3 0ÙÎÚþb €©'Àzú[{ùœÔ‰ ÑsJK€¥çsÀwÎJ×üÒÊ 0)î_Ïþµ:‘þHº\öÉI*ÀÕÅã˜û`~¾­~fwn?ò¥Îx Åǰ}üùrî¡„Æìö‰6E vR)7C†šñ»7þÂL|þC’zYÇ>ÐÈÉØbqßϵmLÂé €)&À?B«5,;Ÿ¦ົ‹â‚œ¿–^Æ}¹84-šâ×$`çUj͸ï^ªŽ·Q±ŸŽC€ÓzÃi<cñ½ 'ÿLq?ï °§º8ötj¨üú%ænß´}ÿLU/k;»V~L'Àì¤ïopú`Š ðKìo5F€åçsÀ<­€±à î ù[ò÷Òë:$W¤_}­Ì<$¸ñ;±÷¯‚x¶ÆÏ\Á Ôej÷¸'pUÇç}GÍ\ `gÛZ6>;I4Ô¹“µÑ´^¶Ñ´Þ^A€- «R¾Ô¢ªŸ3Ö^gÛŽkÅԦߨ°üs‹%ðZîÁ¯JÎsn8[m륖,òó§D­vRò[‡¥Â&«/ƒýæfû@hrÆÛ¶O¹|? >é©™Y˜¹ë™m?c»c·X±Ý—¾x™øü½W½¬-ÀâüÈãQ`IþTâ%ŸsñÎ_¡Ì"ÿ?Y>ùÙÛü¥‹ØoÔ–Wk/¡ì.Ú&·‡ëoËGý #üÉâð°¿¥üùH ÅÂý´alö•Ñ‘Àð >†ò±á€dt›Pò¼–ßüþB‘X~>Ì ÀÓd,<{#­ä1³Óée¥Q*À™wØÇßX*N;/œŸo(a|<¿³7–ñ-A€³ú‹Ùï2Gnz-ž«Ô–TÆmm§í̲„F€¯Rlóšñ(â×Ï ©û~•CL5îü¬×a³1¥_Ø9ÞI²³±ù¹%‰yoíV’xí[’”õ© píöÑËyïpþF¶9‚í{ާ|+Π§ùÙ²ä‹Â”5åG[€¥ùSÆ›L>çâåX’Eþò|º÷¯7pW@äÇk˜öìÕ¹±`Š p¸þVE€Ÿ“r`:÷“`⸠öÙÎÞS#~öÕIV€Ÿsÿ{`áå¨y®â|˜O€3Ó|¼„æoŽ‹+h>³„{6V4 ðGeÔ…®Î'{Û¤|Äõõ² Œkê0àmÜ )/ÀîËñ±·ö´Ý¡˜{|«‹‰îº‰<ížNœ—, pÚ<Öš5ãQÄ'­ŸÒûÅGJ.ž­ +ÀâÏö:.ë.½þuä[V€kªž>¡fÖä>{Ï<÷9£Œ²ÝXY€Eõÿôel—ðÍ;T^×Åþ\ÇÆTÖs*Î÷œ¸_̶Ÿõ9¥úÎ$ŸF9 òüÈãQ°,ŠxÉç¶ǺNôSœËò#Ï¿±ãO‘Ï´¹B¬ eÌUùñ®½„íÜîL)Öèo½^"Á"&R<e^ŽÄ0QÜÇ-£¬[,Ä^K}…”Ùq^F€ŸüÃ-D‚eìW;o‡;Ÿ&à‚•4÷¬ü½Vš»¸p1ûZ£j¯%ŒŽmµÝšÎÌÔ¢òZ”#®!ÀDbN{^XÅÝ•PFCIë%nò~Hï’[lê`Ù³õyÝ3tM15 €+£™ÝSÚâþMÀ¬ÿX{×è‹kufüÃùßáóÇù3ÜCEŽ¢aóù)û˼A Ð àÉæ[sS/¡\ôzc­–Ê|¬¬lý?¨/ðE¯F5&ä«pº5À0@ h6pÎúådþÍ>uØØ:guÛ…¦e–9Yn`ñô— Ò°Xs›Æ[í QNd Wâ:GùuÞwüÛ¼ww‰5¡yë,wݶŒ§`¦}¤i«–¿¸Ê€“~¯Äpb¯Žª5jQ)Ưyñ_ñ‹¶h„%­ŸeQi–`S<o•ö͇(öRF§sóS`Ö¬½%=tOo€ÿpþwøüYøSßC¸•=?e%Åž0€@³ €ÓÏ·fF/qHXM0å%ôjTÓ°@âÆƒ! À„w;ï·Ó'à'0žƒ@ äBÎi»õɲ¥Wϯ«Xb0ž“εڕìÃO/Ymé0ŒSÀRkö]kwR7$Öx"ÎñÅ<¿›¿vÅÑoôׄ´èu®GiàÑi˜iŸ©žë7ž¬I©³k>“õ¶ÖÔ¡—E¥˜úæmÄ_b $ßîbQÞ´*›úãX]Á)¼•‘BíÀ•¾Œ§î:`ÖŒ½è.¬h70ãÎÿŸ?Ο8Çù¹ý$òÌžŸ²¿ zÍJN3ßšx„°íˆ`ðý$6Æ+~µÝäñËEôMãZ%øIø Œç ¹€T¬—µM°rôÕÁ/nvs`2s ÀiDguÖP®ŒzZþ¨ë ¼T³G~‹¶¾Ž bÑ6ÁR”;jt ÀLûZY¢dÚ³*ƒ”`Ã:_‘%È®RšŒÃ FªÒ6a²,ƒdöÀtà¥@©ÜÀ¬ÿ{Q[Z„\`Æ?œÿ><¯ù ']ç×Q›ÙóSö ÍZžl¾5¯n'o´óŒ#Àõj\ö†^)YÇ€A lhýÜÜ#¦ã,ó±Û¸2‚_À©×!JÀñdS°ÒèÚ¡E€M5g…?¶mØ'-^ë9¾ ®1ê#îÁåmØÃD€Íí;`>œ€ñ¦KZ,t:œôý¨1ê†hÖ¬½1mÉ-¶Ü €9ÿ;|þx¦‹±Ã ÔOìù)ûƒhhöð$ómܓտÖ`óø)Ð NüúgY¿5ýzý²ÜÏ] À ­¸jÐÝØ(Û[,UÕžk¥Ú`S`.ܰ`½ïï ÔÇðN€íÛî6þœ0¿8KͲ©°B#À}.úý¨|»è†M°Xÿ1ö*1½.ï†-²sþwøüYp~¸iQL$E˜¸óSö›Ï€@³€Sηö˜¬þU­Ö›X‚M°@ œÈ€þ…y Òœ3Yo}êbÞ€ÀP5ªõ ÍCb&Ø¿-àÕwíÝ´Ð_µts¥ä§À>Š¢6ìaw6µï€ù] S°Žhßlª¶$ÇE€‘pÜ]wL”§\)þë¥ 0ë?>bM·š*¯-˜óûØçó'N¶Žz÷”j§±ç[ö'@$hVð$ó­-FG˜w­vf#Àãx·¬T‚2H šM¼òGÌüûê’l>MÚ=Œ€†Ã¼jÂåCÞÑé`%Õ&X©¯+ku€=ûBôô÷>DïW{Hª3‚2B2—“Õ¸6ì13í;`®p*öÉËÆºaEœF€…Þb_vs,ÐBR ‘ÄM‹œÝÃÎoòò¾U3À¬ÿX{+#|±Cb+ŽÀòþaýŸÆ>öùãüIï‰Gw w¾Eôe;eˆA гÀ“Ì·6ÖãÅ¿´Ø‘VþWáç¸ÀØËjŒ›Âx@ ûxÒj Àè'>>HŠ/¬<±,w™‡‡/“8pÛ³–qe$ï—¿:»îV¿Xx—ð[L,¼}¾©ßSs'ƒ›`‘0`ßÅ€÷ÚÍÓ{ßkšÀ@…€—)ê„ à…Áàá¥b×›öì13í;`!Ü#w:½÷Ïb×_ðÊ—ï8MºÙÝã©ÑÀ«/ིùìá[ÿÛ™€ƒAÉÀZxþZÑó—š&ÄúÝ´0r”ƒ¶«òòòv:`ÀÑqÐʬ½ùu¢÷ÚGýb÷@wæ˜õûØçó§@Òd­D¾EX•Y@³€íÌ·8RU5.,(xÎ}wødÓkÙ¹;ÈDüÂËÙo Ÿ<Ù47{é¡wÖÆÅ®?i]*)%e®<¿-£LȉÄ_ŠÒ/3¤’.jœ¾]%`jÔ– ¿TíÙÛÛw À’ïhýxíO% €•$ÀCßļ7–þþµþD$!H¤¥‹S01‡d±U¡ªš|xë+áH-¸òír½£Ÿæ·N8a`b¾¨0ûµW»r÷Kᆮ/xÿpþOcûüqþ$5¨–õà;>ןö´YfFƒ@ g€íÌ·€±pØ €5Ý‘“CÇôwîãÇ"Lö‡&²`Éñx@ ÐL`züîMºxåo–‘ãÜ«—sÜ À­7^×iNzõzGëµCª?ÃŒ×ÔÒD[åàõŽ@ë=Gð7 \Xö L1iQö´ÞxxD²iwœÜ¾ã° T ôGZoüðeC&¹øR¥³Â+çmܺ×ÑÚòth€µˆª 4/Á×*£Ôþƨ÷xȉã”A6û·· ßô†Y¥ˆ›ýŸÆ>îùcýI>oîÜ‚[O:Ÿë„… ! Í6¶3ߦàAD¼ƒ£ízB³ÚùÑÁ²±q}ù ÀBüñ8úØèˆå¨í|<@ ÈõÌqÛ…ýû÷_Öö¿šƒ¦d||þ°õÙ.¹NIüßYg~i¦µŸìIiÛ?<<|ó´éŒàÔûÔNÓºº¸ƒÅ1‘F¿í›×ùuÞ;OÑþÉ?ŸÞ>îù3û³¤Ø(%B×›Îçíõ݉èEšA ÐÌ`OížOŠí°ýùVU}]’ ­*ª6Ž%vN Ðè,¿ª£ŒÊít¡øÕÔc¦êx<@ hæ°3‹gžTéIü±Àë5f«øJ,¼í(æþà‘ÅÜ.Òôì›Ó—['×N­æKQ9)Í`ÆKdÇeÒη–ÌjÀD~ÿÔ}çã9@À gWþ3ïS´“¼FñŸª¯D¹ûÔfÕn+ñoòþ­ºø*§gŸr'²â'‚]VÚšúEð/ôì°üXpÀ²î(€€€·ºO}zvï…%‘ä­Rš/EÈÒ7{<øWÞßÜ`˜†}‡Oïm»ñìp9ÞS â- 00@À ×ImlГü¶š‚–+›îuØ`ü?yÿu1ðMÇ>\K–“÷IÀJ~u˵SçàÁ€Ÿ:Oa<@ ôŒKj¾u¯#ÚÚòðsuÊmt~ìîkœº}J_ÄÛòÏdרX¬øá±€]À ƒ žv þ »¯pöIð|€@ÀßÇáÿìÝmLwÀñv2À°Ã´{Å’zk…øX‘p¸^/­õb£ÕkiìèÆ'R#&J¯M*w¶ (Å*­m¥€/Jµõ…Úª±JîÒË…ô.!!D„"ö‘•ÞåBÊåæawÙew-­Î2[¿Ÿ&Â2ãnëNòë×™ý € `~ùLý@ðP0 € € € ```200ÀÀÀÌ[œÀ9u_8p¶"1ywnÆ›ºz¸pªû*¦­÷÷ªÞþõöïR²¶r„l àG{ä÷Ž àFQrgwˆ¾6ãÑìšösö?9òùpnoxÂõlq(/¤ºtû‡ï1k™k•]¬Ëzß3µÝý$€¥Ï­W%€Î `Ûæ­ ”kâ5Aê­ðB/èï¯þÙÏ€S¸äƒ|×Dë1¼äÊG¯å»Ö Ûe­Ë¦,©jíé)Ëßšâþ~ݱïî3€×Ì͸x䤪º9BN `ûæ­‘¼b« W‰gª§Àkj|ŸýìçÀ™\úå{®%O‡xåb׳;¬Þ¿#´OKª}YW´3kóT÷UeᾸü©_=ÇÁpZÛ:o jŒ‹“Åó'ì àÈçÀ‘œ£ÏÚýg×¥X¸çó»Ê|×ëÁ*ó3óí àŸ¤÷Àúë]âà8-€m· Œž-ךgØÀ‘Ï€Cxï7Å9¡.Y¼ ‡ÎܾËÎnÌò%Ð$€ æf}ËÁpXÛ;oË:Œä]s¹ÐžŽ|~Àé¥F‡xå+®Ã!ír­·ÖÁ*Yúëwí `éÕ”msî±½çæˆ226Ï'€{nèÛÆÆ ãì%oSÚ|þଶyÞfwœ¯6.Tò„Ø{t¸»·¿IxÊøo@Á!k!çÉ,mîöu»YªìÕ|ݽƒmîÏ€38}Rg­Ãa}›stéžã%vpÙ¦Œñ§¥ÿ–µð³òO¬ö޶w͉³tqoIÙ6“£श{ÞÊuûTAðî+‚¼°C4\?¡×k§Øøì®T%67y­M–Ëæ@]Ô§éßûÄv+x¥FÍÜè¯Æx~’!€.vm݈Áúð¬ezÛÀü!íÏ÷ìß;+šŽÝU”çb°þ k¼Éß4úÃØÌ8ûGÿ?@Uî£ÏptœÀöÏ[“*ø‹ñv‹^ºCã±/p 8¯Al¢8»Oô ×Ö‹gÌsÀkjÄÛmµµ¥õë† cS ûZÝúÏõ>2N7jVû™·¬œ¾a™kÿ§¥õR¯\0fpéºTãvH¶dïoR^Š×ªþ»Jדæw£ŠòrT ²À[dÿ(eŸÄA¦!€1o#X‡Œ´•5ó£¾Fà²;Í3¤Ï{;Äëf ËUV)ïÖÚÍ­ä/ ™8½r™Ëôñ»æ‚ЕùKޤÛ=OÍÍúúŸ…17½órÇü¬‘uª7*€ÊØŠð²¢öŸ$»î¯¹ËŸçà8(€2o#Ø Z¡ÈºøyQ p µ²7"€ WÉÚmúÕÐÞ_Í; HòN¯ûû{K—\ù´báb}¯\ìÚ^aÿ@žu#%##æ½€{‚K\6Ç`¡G/`e`ü`üý#íÌÈH»8cà Nм à°Î5Ó¶\3–«‚aÀEÚÄg‚Íß)=$оÛm'y³IÀ¡Yü„ëõôÊTW¸Ã6 ä•7rã°¢Œ ˜ô/±Î î¦[F+cãíÀYWWplÀ š·|.øu×£2óp^Cp1¬È.×ÄÞëwæ v5Ü‚·ð àÊüÌ#“òWö äY/f}}ü¤ëÖ¼=#zìª~ýYð«±Xp{<£7G¥k^œý#Hjíé¿åÎç0À¹üU¸\ |·Û8\®¯ŽÀEZäMÍ‘ê­ÖD±™ ¡ÉÀ¥ëR÷¼ž`\’¥±i¬-)/Å»-¯õ™^Ù\cCb°1‚O»5¢t͈½”EŸ¤ld,€s8!ó6fK¡åŸóÄë';­5ž£¸ t›¤H%}ZpÑ,’8€W¾âZU1qs†éº ’±ªócÑQ¼uò~žÂ[ŠòLìý£p`€³8!ó6f¯9$^³’WnÔÚûC 6–¹²®6…VžÌhþRì\²:uýñôéàà}}íœÃº IDATÛs0:€Ý£Öeÿ-«|cìOàØ\Ò!†®y^Ð ŠâõÂà.Æ=æ@5~)Ò|PVëôÎþ2ÃÙa¿§°qÁU©À+®êw˜øÃ¥™Û5Ë6elôÄÛ¨‡­20ÞÔTýÃ÷Ê_&Øï·>áë¿«ŒW7ùK ïxbïEÚ’²m&Gà¡àÖŠÚÒ–±½-ø3¹JÃNúJ}¢ïZqmIËm£³õG—÷զ׭ë5й¬ãÌà¾bµ¶®O žAÀùœÓ¶ìÆ*ãqæÞ|ôÚÓ®Ìí‰ÈÙ¯Þ«G¢µt­°ùpŽÀ¡ÍOÆÙ?JÞ¦´7<€‡9€÷4:Ó6‘¯¿m›ÃÖtÎëìb°´¨/p'$͸zQ`›O/7ñ~’7€­Ç{¿©HÜ@nÌÿrü­îcç±ç …èü£ÿû÷ˆqï£ññöRðbÖ·€‡>€Û{û×pövDžÍõ¶ w·÷î \ú¼a¸Û×}»ßzèÝ08Ü­éÛTÞn@Òpt×>pàÀÙŠ© êó¯Y´3æ=€$I2×söOþqà‹ªO^·$y~dÿÈ×»ÄÁ°9€Óþ¸ÿ½ÇZÛ;o'“Ýþˆ[&Ì®93é–Fª$¸ýUdYµ꿪‚žÂ²Ä» H➎\´3ks"ÿXÊŸºWpð@X—’áäžÄÛÉÇy°Ý9»þFÊò·ùDzfnÆÅ#…»8#‰XªÒÎWóÖ`òî\ó"±÷š£ÙŸ›¯šµ•#@ëJjõœ}­¼sØæN{óêö‰ýsñ~øß·S`l’;Í[¹@Û:%U¦i;}¯ €ÀRŸæë<'° €žöE9 €íæWÉ_Lð00˜   ÿgïÜ›8òîÅX®íúœ à<À_Q8JàÈHÁ ¤wJƒØv„¾ýyGømEŠ pa=Ñ}ñ.îøn«?§&)<ŠòQïl»»¹4üòp—¯°¿œWŽç,ÒW½A¿1ÄAÀZšïÉW~95ÞáI«ˆþ¾íÈÛªñÊx ¯Œù¨0Yƒcù²Ohý'çrÕ[â»?+ú×Å™HÚ†I€ã¼Y§}¬2|«=qUü ïçŸ7°)~²GRB€K>­ñ 0’á©wzšGk–)lº`Í"¹ß$^‚ëÃÐÒïÒóS‚ë˜SyÛ†Q€ã|¼Íi“•»µ_}L€Uiùº(â@ò 0¾â¼íÊâQ¢ããïRòׂњíôïàpªÞìvE0÷Ù'Àâ|Öð'7¬´vÜ ³<œå+¬²nY8¶·s凨ïôÊÐ(H…×é"+OìëÃt¿H]=&ü>’Øúæ…³=`8ÎÇÛ¼.ì»3ë *ÀÑÅ€Tà÷¿ÏÝ+À%ïhÞ¢_L!£Â#@€ —„ªñ‘Æw[Õ‚ñƒØá vÚΖ‡³|f·ËxØÎ³}2 pæ Þû}QxRóƒp èúæ7`žö€,ÀegðÝºŽ¦¯í±`E<H~N›Žº^žõ®fŸxgð&Í’#)"ÀxÞ¶Áèù¸xm^iÍžKå‚°6âEpÜÖâgBöd6t~×¾³íÕA¤q†zbsÉÔá©¶H¬r6dÝ ¯<Š÷¾øÔ«-†Îw/»„åñy«õkeÊ(Û>œi©·è‚ ˜<~H¿Q”_&$aä7¤›êºVx~· K?›)\ê#£6ü‹Qåg«T@ûï‹Rß÷¨±ñ=ÒsѦ“Vµô·ß(U¢×7ùÛÔñgÛñ²lß ÐžŠþeèœx¹ÈØv÷Å>ñŒ¶äúå¢wçRìJ–§gòçÝŸ™þ&Î6»†ñA ôѶ'Œ`6ùjž¸üÜH€m]ZDù1ik:t åÉ£oPÑW²-{ù¨üÑ/WƒÅ€à4F€_“ø þ3¸`€,#|£Ô_Øtâ ¾9=½ J:"2žÔã1bŸ‘¸°ßEÎe:nYTæK ;v õaN»Å7|I Ù‡,¢|þñEá0Ýo Ëjaá0w»²~ô+ ³}hcâ‡+À´ü¨4>VŒ‰^€ÍÝVõm?1²…`¦üñ©»ÈÞa”ùan KEÿ&ú^M £\zwu+~ÅÎÙ~ˆÙOé*ÛzÜ’ŠþÈö/ý[/ˆr Ÿzôj\žåÔꬳé™ü9÷g¶¿™½×K¨Á³ûÔí #Z€Í{¶¢ÿT™[óÉÞ|Fëãk žÐÌ pþ·èƒç…äïÑ…Oêp í‰ã:e<HI.˜¢YG?7Mƒo&«RN}ÿ^_~Ò ðöZátoÏSÜæÇŽp•ÕzÏŸeâÅ!À«è ¥(À¦ VãíNvø_· i‚ ÉÉÚ±ÈyQI€ˆ5‡,¢|òøT8î»:æõ}Þ@„£ð¨à?g˜ÝùXzÞê!Œ®› pÈüB°*¯VPß(õ÷IY<üÓ2C0£a˧úÈXMu3Ñæ‡øÖ€PÜÛ×_¤¯¶ÓïÕ7ö}Ѻ¿_ ÜÜðâ©Ý\ôꡯ“ÍÙ~*Õ¬ùBÇà‘“ç¯ÿga`ÿþµh‚þ4Ê>ÿ|óïɰ¶ÓƒßW\{&tü¨?²̦gòçÜŸýÍ‘+ÕmF-¾êÄîÿѶ' ³'æxK/·*˜üÿêòàÅ'~y©=ÔM^Ñdz¢-žßÚùR{ñª2¤¤O[CÁ*Ù„Äoù=–aÉÇÉ)ÀÆvõí±x&¬Pü¡r„+ŒNº[$v6Ð;N3jtFíð 7¶ÍoóïRê‘N/ü<×HÇ5C•GQ>y|"m §ßÄ¿”GÎ2ÚLÀèÉþÆÆW€Åò›ÉS}ÞK÷QÏà`QjÉ6ºqÏ}–wþj x¢ÒG ±åS}.a­Lm~H€Û¬xlS£@žål Ýîh?l9—»üòû¹\|†³ýTeG…â?‰¿)=𰬹ÅÅÛ|Ì«ÈÊ«é$b6=#ÀŠôLþ¼û3Ûß2ë…ì_‹‰ógÓGÛž0ìœÀã-Ú¿Î5•ŸõÿnÈðàŽéM»£×CÐþUW~ _èËè¢Â08mÙ4Ͷ¾Šé×GݹNÖÃêܽ{wOótHîÍOJÖÓ)´H°fºç¶1ø=ÀÿׯüYP€Md¥b*À™3¨§àøÈGf×tlDçÜ·k ¡¤çKë‡QÙ{&>Š}CŽ™.vI ÅïJÀñÃà ÷G À’0J3bK\è'n·!À†î"õб˯úPÆ FTùÙ*õâ/s“÷eÒͪ’ë³'ã:˦ӡ¹ÛOå,’&а¬¹­²ånïsq1›ž`Ez&ÞýYÑßœÏT/Ðayú´' ›ÃñV±–L€µÈzsö’W,À™g´? ûþ F†§-˜F¯@÷î÷.–VQrmšæµuÉ)ÀtÄÉìðà3qÅW /½³ÃNØ7ƒw&*˨5®1¯4>hªí¶ÊµoxXß1p8”3ñ‰pP¥˜=í7¥ä¼ž®U¡€)â‡+Àº¸ °J·à™ WûžÔª`§GleEùãVŽ\õq`6?$À“JECÃC”]sÜ+À8”­rçÔ ®ÈÛ~~™ ÜžLÿÂí“5øƒwóz<›€JZmQ¦— °"=›?ïþ¬èoØpéšt¤lú´' Ÿ'þxË °|ÿiÛ‹_‰#¿s×Éç@@* pÚž~ò»©wú* ¦ø˜ o^Ÿ”S ×H§öØ8G€ñÓg} ØtAX¾‘ °³ÁwO":/7Õ«·dÎ(ÞåveÏ=—[m§@WœÿÎEöxG€™øD8¤©œfY\‹ .SÄW€c7¬œŠ®3ý9WÐW•àq5Ò QŠòÇ­>ɧVÇJ€ÙüÛŠigˆk™Läõ 0þu¶J,ùD€yÛõGYáû÷/•ùSÔÆoÖÑ9ée¾ÍËjÉŸlz¹+Ò³ùóîÏÊþælÀ—PñÉH9›>í Ã,À =Þ=Œ—x¶í½¸A`gïžág¡€#À³&âE°|KZÏÚûmÑAÒIq¤/³8„³Z€çôyfÜotpqnø»‡úøÄ€±àøÕ·¡[€‰ú pnE)Ú}L`.Z“$ÎígOb 0/®*¿-‹…`}c€9nIYòài °¢!)·æ¹óÂ~çÏO+ɘCù…_~Ôc€• UjáˆrÒ'MúáÞŽ´ù7F€­›<þd•é&€fæß¨ò0h,õùdæý,MÝ´ú5K€#Ÿ9Åšx2˜ã\®Â>iºeŽ!À¬1Àê("zž÷[ÚzÓ÷^¨4ÑE£4ÇÇ ž$€ï+Ù, &ª—¼n^¼CÑKæç÷%°¯ÄÊ%¯ÖËm¾bŠ¥;×yìþYfÅYze¹yõ¡8yÑBú¯,ÀQtäG= ´"}pdjºrF: ±2}BÕÆw­#úª&ÿK^Sµ¥ÅD€¹\S€ùMþ  Ùù7¢<8Õ²9!Ê'Úó©[€ÏHË׿¬718ÒøqžäW²BÄ“)À‚“e™¾Ãž:„kW?\~ÕÏ3ã~sv,³7Ì‘S¯+ž$ºÇ¯¾ÍÜ^zHù÷U 0ñErÞü+¦fÒ¢©àHãGºâgüEüÄKOÕý•~Jº‡Ø›µp(f­|þpùU?ϬûM8G²?èšãuÄ€„àøÕ·dE_ú4Jµ_È`Òúžøz[aÂÆ­O(",üK×!¤SOîŸ/®Â_÷ÙQtn¹|wB.ƒd¿°áÈŠkýÂk6©—Ó+M×OÔõ'¿r3†“`Ñf'Q€ð؇ºëëa„— pú†Mx–×»sÿSû*}ùQn«ÒׇÅB­lÛëõz­Á&l{™é Üyêv»Gè§ÊÊÿýÒÚ#;¯ý÷ ç#Âó¹ÛýçYlÎYPÝE³õ‹ãrKapz%ßµµ¿= 1‘ʃP,¶_†-Ÿhϧj&ý{íƒïï7;ï!‹Ok8Âø‘)´IŠ'Z^ì_ň§êþZ´¾ýñІ#;û|b lîqÛ±]çJ‘1ø’žµVÆñªó‡Ë¯úyfÝoÎu›ßº5Çëˆ'‰*ÀcPßò÷-¥÷ò »?Ûè7âÒéÏ•¦˜sG_¬oLi]s÷ìFÀ8à)ÝIf™Åd;uËuKͩմtþ\³yË›KÍæò¡”Ä\˜`2µ¿M_Ë­Ùsè (—¦¯‹©S3¡íx}ËÄ—ºr­¸¸»x;y`[Æ—¾ü¨¶•ék»¨æü˜ÎÆRÀè×åžÁ™4 þɈTé >A…äKöõ3òÏ¥]1‰K¿¡ç|R?ówØ,¼Ú¯Î{si¿b¤@z¹¾À²6â`Ruþc^R´åž´!Ë'Úó©[€¥ò¶ûznMûŸDæƒ8ÂøqœkæS`áeMž³*Î.¿êç™u¿‘›Fz*LJ' +À%cPߦõŠË–˜nöp¬`¾à¾´RúB˜@,no^[J·S?HI\n¾úm¿=YK.·5îp9c,ÀdL£Ø‘•ÿÍå6OóÕ†ÝYm»^fŠ#ENn¾úx·Ug~4ÛÁé3–©Ù´Ì¾7kô¦J_ êOG(mþ…<]»ÕÖÜôØŸ@8®zævÿu:[€ úŽ‹v“qé(§K€Õù}yÐfÄŒ@ƒaÈò‰ò|ê`Ž[BÊ»zšxCi[€#ŸÁô÷o‘/<ÿÍ¥*žêû+ýdçÚ<ö&’‘üóý¾æ«Ë¥ð0Ø,Àêã5ç_Íó̸߄<øÇ“É›HâALJ'‰*ÀcRߦw?¹}øî£z+[€-ÀÂóõò“Û¥·?®ÇªbÆ­k¸õdKKË€þ«))E­§ZZNä±Në´Z9gÜNæ´Žiú.º,+†é;¹­ÿtG´¸ç¤ ¬U_üoÄñ—V¾µåÆC]£Íì˃sÜôÉ‹®|b{ÿ8c¿°ð>Oáó«yž•÷[ölyfiq °âø°ñ žœüjCçlÝc€Ç¤¾õòÂ_é*Õ¾Àå¿UÂÃætz«^G€#E<ä?0e\ŸËËoze`béÀ:À‘Ä€ø 0é¢o‹x$Ô·XòÀd;öÁZW¬Ò«zæþ· åAá[ëúMÉJ_Jôòoñ‹.¿üMßÂpz˜Oâ+À60Q±¹ÏG‡–Å&µ;Ÿ†èNöòÈ)3ÙlªöäD/Ÿñ¿(ò»³«±µÏgÿµU¯³â €£BgÖÝj‹;«¾p5åAáÓÊš?8:žÊg¼Å/šü’ÅÆl¶à9ÏÂ0#ž@€0*äIÍÖ](ƒ`ir޳òoñ}~ù3>{Ó¿>rqz˜O ÀòäÅéEŒçòoñ‹"¿Xx@€ À€`  À2 À¨0 À` @€Qß`Œúˆ§Oi=ÕÒ2°"hOÙsb§ò&­¹ipGb` ðs³f”E"ÀÖ·×a±üÝêèµ”Òû´'§E~žâŠÓC¹ýðïu¦åØóävéá»wÑ-¾µ{øTÞè¯XäåÍËódý=ŸdhØ' ñ¹N3å-cXÀ¾×…`˜Ülh}KÄÔrã?±þ0‚o|šsv|"ˆ+냜áaÕ7?±PÎÒý3Ù­°X.níõ€ ߟj° Ãó>>P7Õ\¾[ú9zÿ|sù:çWP!ó^oãɦ…ï X&µ[ßr\v­¥‡s®³„iŸ ¢d[é=kÌ8·ö,ûóÕµ¥Ê–aÇ}Ëáá¼#­Ã»Hþ·[î>zÔk±|ºk”×,Ô·ç:Íó–Ê\2×¼|ƒèÁ èïÑÝóS«é‡…õFIÊ­±ÿÁ0™Øðú6³–tî°œíÒý•”¼Èý7œ/ê-íáô °°£GÈ€“nôZHOhÇý ©Ïó(®xJw’¹a`M’$ÀÂæ/×Òÿ­œ*vÁš¹ µz…Á“räÖd¬B°Lf6¼¾ÍÙFü²¸âwÓŒ½ôÐìì¨Zpà 𦊠¹Í¬Gçl³Ü‹ëõ˜`¼e(oŠ,À…ß7/?ÈŸK[…‹„6=+e‡] Lj6¾¾]ÔKFËf×^ÌK.Ù6â XjæïAή=ý¡x–ž¸^€ %À)EÄ‚e.yݼCüssù>ºã-£—eàfú?{÷óÛDzÇqÜ13™È&6bDZ¢’n˶Õå§jY41  ÜHR Û.Õâi“®Šj ÉJ•šôÔ$„_¢ °hãâeE,llšË®zéÁ—ˆ4ŠÄ ù¿è<ÏüðŒ3 õ «øý:ÇãÈ‘¾|üÌó}>ø  †pðõ¶± æÞî~¶ËÌ“Vó¨ÄiUŒ¡îÉÌGûž‹2¾îݧ¼w+7‹žÌÅÒÆ¨.wS«¹|i½õ\],—;`U\S>äzÀžó«æ?¹¸¢ÞüÐï÷àupú¸b6ÁJ‹*ÊayäÊ­.¹,á…90à0ê­Ûæ`µh¶Y^qfÞîq`#‘>”gœÕºŒ£f3«#ÖÆþayº3ø`Îlá¼gº¼ì¯«VåõÀ}o8ó©nø^t`ëM1*ªÿcÕ#@õ°6ЭŒ-dÓSKÑå%€•c#ѱ…+©G”±SÁäæ_Ô½G G@Màê­ovF€UëÞâYõæUŸÜ“³vÌU‰fsv3«ó91u·Ñš5psÁ Èâõ­×3²Óë¥s€u9x—¹)5žÎ‰›²ëùëT/k}ÝòëgåΕfV¢æ‚À-Ý>í9ªó6ïýtç—ÿ}‹ PÃ8„zë€Ë#À7?¶¬5M×€4zÓµà®Ø5cmÂ8óI}dߌ=Pkàòþùœ}´ÓËçú‘ÍM°v6µ¯äòë5󌛞U ÀÚä××»:—²mVî¼æ,ÐÐPAnyZ×ÐÀZÀ€ZÀ!Ô[ŸìšyA>Tp·d–»»Ü›=Έ°€æÊ·0ÛGöÛ°"[î]ÀÍ+j~>rqEUY÷PÍì,Èp@4ÁºU~i­Kx#záT@¹÷én0 †p8õÖ'»º@¿0O{†c÷”wÛE46b¬}–€{rþ§oi IDATêªãs+)»î_~ÕàÄl./ÞÌøŠšyÂ]Ѐêྦø5ãß.åmëP_ôGä–ßîürî_:õ P£8œzë€×¶€˜k÷Æ2ìwØxû¬óÖ°7Íz:`ù\?ò’`‘¸åõ/Ô‡ôÁT=§ûåÐ-ÝŠu(y#j7]í&X¨{ïû|X€ÀaÔ[·{ÃÖÉûf¶€4ºèÊÎà˜œœX³·MË­}Þ5„#²Öü7_?ò’àýÃå÷W­øö¸÷Œr8«i©ÖºHb…¤·³,ƒ@8Œzë¶ÏJ›b¹¡­`qO³ûÖãÊ.г9óüöUõtv"óšçêÊëË(œ™wŸá>˜³–£0U À©ãÑ“s椤øeë»ikƒ @•põÖS{eïæH[!³ºµl$Õü¼™€c×:ÀmæÆþaùã⊺ê¬lf}òªÌ­v3hßëËËYM¢c>Øx}s9áØi5ÿ9`€× ÀIƒl8iüûSò¶«Ô­®ø 9Ü¡œm){—”Íó„«S½ßðî.>,@ à0ê­›hª¼>Ú?£>{q,]·Wj™Q3ïŽ>ÚM­zrjit\ë›VÅPnsAUK÷û§ÕÇwÍܸf<ÝxÂdÿªx½CÏò¦ëGälàÌ“ÖñÔÝÒ‡›°q~^´ÐŠÝ®Zà°h;é8,öã—þs{䨴î¿èVâË·Gš”±SZ0¹ñOu0PÓ8„zë10#V(Ê<Ñ_€å*FÖ(p¢×Ø•‹ÉX½gØ\ähѼúˆ|ræñU³ t$Ö¾f­„$z_%fs7¯¾èúòzë}pL<¿´±±jfPÅlî_úÊž”œ:#Š/iAäÙÝ?û ¦põÖ£÷y±Xú´>±Õ‰4ß}^ÌW7æÍAáÔÆj._ZÿØ:šZ3^o¾ÞZIDäñôÒúh}e,Ÿë;ȯnŒÖoÀF–ñ8§–È¿€×À›ëïäg²ž’<1±0äûìê¼Íƒd `À¶ À;~3výÇ[ÀbRÀõv³Øž£{öêÏ:stc±H¬|°^ÄÖ„÷ÔD$‘ÐÏ-ëŸìÚÊõõ˜q³¾o£>1ùh}ý~+‹&ª€¿1'ƒ À;χØfØP×ð*8èzû‰´\‰Š8@@X ì鯩§u?ÿ+`ûà†WÀš¶-påØ05€Ïï–7‰ýã->,xû`À2ïøË¿¿Çg €ØÖ8¦ët³€ Àlÿ ˜z 0õ0`@€ ˜ 00` 2`À€ 0õ€ï^NNÞŸ˜x¥ ð­ð~¸÷W`¾cxà€r¬Õ‰¿KQÅ06'wS'”² CdÀÔ[ À©6)ål„áÎåÛ#MÊÉkdÀlŸœ~t]é<êàÞ娙ƒÇÄÏôg¶‘¨“’)ÈT1'©·„€“w£Ê؃~»Ö»NÉ­¾&å#ùˆë»é˜“@¸\n©·€/}Õšt°Qu›Ú:ÏÐÉ]dž(È€©·„€µ´(¶Nî=£\¶¾€>§œœs7Êê6'S.So0k8~Ù¹ËÞ”–âƒ,Ë@°˜z @x¸­C±ênK·âšƒ”:½ÓJA ÐL½ Äœ>®˜M°R碊=XÌî²zcQ*So3kÝÊØB6=µ]^*àîÎ/4 2`ê-¡`­¯[‘î\qB‹Å³d ÀÔ[BÀÚä××»:—²mvNߊú¬È@A š˜z @èØùú€Ý«¯©óšFA ÐL½àྦø5{A¤;Y 2`ê-o,§û£V#ʦø€@0õ€7€{Ï(‡³ÖAÿ%(ÈT-Soÿgïüb£ªò´¡Lbš4Á4Ä'ñÁ×=÷Ü;Ó™;w:3èÂ÷=LçÌÜó;÷Ü3é™o~÷ž ×M€=>þnäÖÀ‰çG3!äT€™oò/À.W`ñW+wËõ'=‡ëK[ ~‘  ßÌ| wv-T¢Ü§•K_ùö½¶û•ÒÈ=MÊJ&d€< 0ó-Àµ`½üÊ7LÈ0À#À‰FÜõɾ}ûN7¦uÇB&d€d\´}ÏÛ+2¸0ó-Àµ`&d€œ°  @€n.A€`˜ù@€™o``F€`€›H€çûp#€ À0 € À0 € € À0 €#À0ÀM,ÀŽÆÓÇŽm[óBß±c§ß/¾!xíŠ[_M½•÷è…ýåÞûã»sjÃ?=<`uªïÚ °çWå²7Û?ŸS»ƒê¥Fý«  €§;?vëóí×Ûæ§7Õ‹6Ljš\TÅ—–ÏkÂáÖy ÀÎ!N9‰dê_ŽâÏkBüöiù-B=Ój]¶l?ÛxI‰Ïp†ñRþÒàÊw+3,d¸®e_Ûbåö;ô6­V^~cŸ˜”WÞr}ø'Ÿ/wþ˜Ž{w•/øáX_×'mÛæÔÆ=©Èù^Á– €§bÙjå•oßóíæçç¥W‡Ô[Y³:œÔZü=‹1*¶58>xm(=¿K©9Š?#¬æøÎ u0\ûagW¸ß²lÝ~¶ñ’7ž9à ã¥þü¤'ÀÎOÞVêî pûåÈë¶â…ƒ7Ká­|X3ßbçÙúÒß_ö^kvU¯*z­1›Fº:âã!Àp*{b±.¼5;ê‘ÿ`›”¿_v >~M3À½Üóy.{€ À)ñï(•“§I¹o^ pMæ+ ÁCsœë%Àe½ÁKýyü¦•}üa5±;/{æ²uûÙÅ›¿œñç`kvÜóÖrGT€×7)¯êš»qƒžÞt×ã÷ÊLð‹‘wnø ð©‚¥ÛrØ2ÀpêËîª{ËÈÏOÞ8¤)juèÜòÿ'ö÷äh¬¼ÅO*¬Ž‰øKxÍå$ígoþ pÆŸ?@€“œÝ)¬vF€툜è,„÷vÍ|mPÕÄwÝj=œk^wÙ7iw5\óùÆ:tãÝ}U<ßÕa™Ž\W}?×&Ö{zÇb$ °÷¥ /ìýa[œ»šË×|§?ÛUðÛçÓÝý­vïÑéÑÑIÙžÿ€\Nb€ 0œYxƒÒ"Exuéçyட>°`ïþr!ÀU«Ö<Ýþ¯ý_Þk[/ïb±âÖߨœg/ìßûß×­k;‡´kM«BS £ñ&ǃڤ§ ×’¹èãÀ¬ìܢ͗­qÂ[Ö¬j9=Y®<8=:NšPÕÞŒ^™ìpË¢G4?x%,“˜U¡Cýí£WZmúct§GÔÈþ:ºÅöÑñÉŒßóOhá& ï®ÒÖXêž]ŠdŠþ™âÇooÚ?ëñ;4š,¾cD“=sÙ²ý9Ƴ:~ŽîéQÙŸCýÖÂ;–ã?žYųúüÌM€å¿¤¨×ì(54w}“"]X¼÷ø#ŶÊÃ…++ò!À~!°a¼¾ˆûõ‚oì]og˜n´Û1 p|}× !ÀË.Ç„³àeÏhûúñ»±ì5Øåòjì¤+À~¹ˆbàŒÖ^•.ÀÉ À °ëhaÝ7b:ómm^Øqþ…’¢"y“]€wŠÙðÉNíñ© )Àëþ#çÇ/ﵬ_Ö¥‰IeGmĽzƒúÂR¸jâ…Kèä çêB#äšMQvÌð:½Ò¥$Êè7bdMwCÈhý¸.pµ#¢0µ^>FOY›Y!Êaìn ’ö\?”ÅA]ˆ5v6Çì¬ýKŒoÚÞ´V?(Ì¿ÌíÖÕ-°,§h?Ãxǯr"OkÂxšÇÃj|ãÇ3»x‰Ÿ?€Üp$ß«ú¬(jOÚ›”º7;×ï¹Ó–¯ ðUߨ©ƒWucÕdöŸž†Ë²'À;“d€cê‹÷‹ªçEÑ7i-ÀGž-øøë¶ï ŠžªM̯{6Ú—¢¯Òàð:>+þ­Oéþì]ÖCÎth˜o[ú׿œŸÛ 9Îß]òqËGö@à¶~ø¾`iÛÝ?^(_z›&À¯ŠùñÈ{b~|r¶«,#ú¥ å•;½Íò”[áSîšžYx`H=îÐæËbË ðÉ!õJø¨¨hãó÷ˆé¶¶ÓÞýظV³A4ßÑißÒ# SÜcƒ“Óê¡^ùX=m6ºG[Cbw;;½ÝMIvN¨©ÚÎîiuø3c§DW½ú)ij÷/1¾i{ÓþYŒÇìñ7ÆÀ‹åTíg/ñø9'‚ò€y&’küxXŒoüxf/áó#vV”§>­¸P1V½jÿ¢P©/ÜluàÜd€Ç|?Ku•ü‹Ï¶»ìöe—¥Àz½ò`—·Ñ+f¶ÊÇÔ×2Àc¾}¢âeýý^°¿è«çE½g ´Ûüš3À1¼ =VGÕsZ{#ê!£=¼“@€Óš›´ùvå2Ù%8±ªèò§ly pÕª¿ïÿÇÂ+¶úÚò+Ö<-X›o±Ùjž)¸çoiÄ«©ç䩬3ל.Š.!`'BãîHÆq‰œ/ûåI»Ú)°‰àÀx`Êm{®¦Çú¶´ÂØÎÈϹËÜ2é/þ´I>© ŽW‡†{.ŠGãÛ¸¢„×êÕU×õ%õ Ï!uÀÆ ÉVàØú?§JÛíW“pIÑWµzÆwÁ¿g¿8[! VÏÉ ›Õ3ÖLN§Žë 6ß¶4æE€; ÖüÕ#À%Kï´Uý®déCòth!Àb~¬RU¾àiÄ^¢{VRA4]¬Wœê7²IÌ«ª.H½A˵‘„@éÁË Ÿ,þ´[Û¾*¤iRÕùàM1+Dí6*”ESÔº;B&s’‡tóÕú“Fÿââ›·7í_æÇÏ-¯Ñ]MzšË)ÚÏ0^ÂñÛh>>ækvMã‘8¾ñã™m<€¼ °£û~E£ô¾Nã¾G•ïÔ×½ÑVXÚR‘· ðI©¶#»ú¤À†å·€ÿ±wö1mœw·±ÏFlÀJ¶ASP«u ,/uœ®]GXÚh©ØR´Î Ñ¡$ jŒ4Ñ RÔ“7–*iÃU’5 æ¥›JÛh«ÖMÊþ@ ʤYj ‘?6)©Viíyîýž{ñÙç[òý¨R}ç»ßÝ=Oäç>üî~Oü+ñ%^mhà °²?Íß9*n9uÔP€ßþ¶^ûŽïuƒw€É§ìŠ`qW„ÃIò/# ·G O“ñvKÿÍUnpå6ß˵û^ x–=ë{=@¾ø9>A®yÇ÷’x))i[€Ož·¿Òn”–¥µFë[;yF»(l&‘g•üNô¹Y"Àš Qä„GæÚ οCH l²1>cëú4ñ™íÙó£§ Ûl¿°$¨&ËVÇÏ6ž®ýj;¥*ÌÆÂÊô‡AÿjûÓi<pM€=¡xï‰5«?:UºA˜©dOÁóï*Nø7ºR«îæúyáS<.®¯º™«@«÷—ZÊë¸ø ˜—­ÖpÎU %Ù%h72ÀràøÖ‚ÕÄ‚ ØñAúå?÷¨ø±_Ëx–Έ$ °”÷½PMÝ8c<9ÃiW€² iXyø¼¼›oÕ”0¦‹¥ª•|1¨e©±v•×tªÔ)<˜â¸ÄÂð[b+$•G€ù#Ów€…ˆÑh›­ëÓÄg¶gÏϱ*5 lÅ1ÂÊ.[?Ûxlû‘^‘jO +ÓFý«îOÇñÀ=–YùˆŸVnYZ´ƒŒƒÔ€÷•º“–ŠUEÈD€§dlf€UŮⴠ´ðÉL€¥y€k«}ä#œÈ$ÀÈ8ã‘æ¥E|¸À¿±)ïh-—fÿ˜H/àï|_àå?µªÚ÷JÆxŠÐØàaeXš¸¦ÓhFÙ ý{[¢,ÖSuâ¥7´,EÝJ`Z!J1ù°gIz’Ú®0/QC’›–3À3Ù]Ÿ:>»={~j¾rÌ^üL`«ãgŸÖ¶MÈ~Ã*cËô‡yÿ ýé8ü¸jéêo“¤-~á«úÝþu'¬®ÖüµÝ.é–ÀÆ<ÀAõ÷g­3À’w¬+Þ§Î{sž`8¶Åÿ Ÿù‘ñöTÞ84î¥)_JÇSÖܰNúdoV®‡<ËpCRú¤†häg–`jUVWˆOº¢y>)¾é[˾™«`×§‰Ïn¯Ï«þ°×~2À–ÇÏ>¬m?EXC³†ÂÊô‡…óýé8¸/ÀÑ=O?Äφ´Câ¢îf€Ecž Sg€ã²“Ž2Àñ]ÞLjðÖ<\,,×l+DÀ×%ÀUký§æ¥Ò§< °g¼üU!¼ò§^KíòÒ¯2e€gÅÙr=uý\.|AzD–^R pȸê/Y=¦ò ¥6pH|X—~²Ä “£Óê”J/½j {&«ëÓÄg·1ç—KûeÈ[?`QSSfïìªûÃâ|$ÇñÀ}^ù3ÿÁRµö6®uE€™ n£~ö"Í#ÐÒÂæ›ëÍ2Àö2À-Û|?n¢ÞKkA“ãþÖ[hž¾|`n p‹¬½Š çS€;Ê‹¯óž·Ý[h)À›·ù^.µ/)Jý5ÎL¸*Sªg™Y¡©í‡NÇ£àÍF¾Èï¦Y¯«Íf€Kf™Ä«bZôxrhã pæëcâ³Ûgªm£ý¬3ÀŽï4<Ë ý±r”“„UÓŸl˜ °ÐŸŽã€ë\qºàÁÇ=|x#ÿën^C‰v;LŸ—ヂ ×-ø·¡âB†7-¬›r”®z‘Ÿ8ظÉ÷! Þø§âC^³ pû—eeÿiB€«à7ù¡&2¸¦èxþøBuásßÜ^¾ßòè•/ÊÏJ["VùíºÆM›=rKgØåo!BB#æEëFÓŽª¦®J§åÏÄ”G†C¢ñäyvëÄy€Ù pMç˜úIê’«ÇäÜ1ÿ¬mm2! W´ï˜^€3^ŸÝž9¿\ÚÏ:œáø3ÀÔGiçÕr IX5ýÉö‡™Kýé4äO€ùªüT€ÉÿèïÎQ¾6Dýé5E?â'AÚí_=°‚N´ÖÿÌ7ÝÏc·×¯ß~µ¯¯vü¦8Qœ¬y·/ÖÒ*Î<õ×A²&ë;ÀÅŸ^:Ú÷ƒ÷¼ËùÌoì ïòO{·¼ç{aÜ´ ô[eeeïg%À3ñxD“Ž«W€3Äv SGšÉx»3ÿ\ñ„×÷‡Ã¿ü‡÷{LŠ`°ó­¦?“ññzÀN¼QŽ[¸¸µŸ›Ò¹›ï0f¹Ä̪žØÐB»^h*®q#sÝ[SÜçr¬³M=ñ¡~Ny°—H¤êÅЪ—˜¸ØÝ}5Í­"FµÐÝlîçÆÎë3˜ôÍ[M"yÃèXº{U´§oVÌ4–³› ú¶Nó‚Åp¦ëcãë¶×ž_Níg•ÎxüìâéÚØibâj:ÅlMŠóðjú“íÖô§Óx7ï)ðË å¢÷¾ñ=Ñ>aÈi|Äïß{øþçõ `2ÀñªÛÒ,Hâ³Ð‘š›Â2Zú0%Ý”kèB¯—žér±ÆfóÃ…dÙ÷áNÓ*Ðq^€_ÉJ€)3J˜_þ¬ 7„¶Hûõ~¼Õ'€ °çÉMüð÷£JèF€ùóôz_=Xj/?Ø%&Ž)‚²Dÿä¬ã’Q¡ìñ˜{ZRüþ3Qy`qãaÙë6“uWäÉaÃtxå'/âs‹ÑqÞ/Ù føHRû&q½x. Ž›à¿ÕÏŠ3! µ¤Îp}ºøºíµçgÙ~cÆí²ÈØZ?‡xú :íÒ@ íµrQ2u²ý¡`¦?kœÅ÷xPXÞ{\úM¬û½¸æo7˜Íƒ±ªÀwÞý²f|Šèï.~E¤ööú©;Ws¯MOóÀ'r…ÍHËøøp“Õ<À_Ür"ÀA0@€3óš4Þž º!Àž®ï’ñn`…é4H¾ý‡Ê|òôãvãÅf''ˆÝ4H={*†æ'G¦ÓÝ#¡iœ'ûŸ„Õ<2=×®ÎÚrÒܼr¸ÄätzXHjÆÒÓÉ‘aV#6ƒ©¯€UÑ’žŸL&È)K)Ñh ·0×ð°õõTØÒm¯>?Ëö³)¬êeËãçÏ Š6ß?¤±*•i©TýÉö‡^€Ó“Iu:‹y`½7]:wîÜNÕ/dHXcø@”sŽD"¬Òg…{TO Gâ]]=ÊB0Øeµ$¢þŸÙAµ‡‹ˆG5Ù:þ›–]ÇÝ€ìØ÷“½'ªí‘¾‹½½ç.Hy¸'…ôc¾òQx8P¯³†ÎæC¶·²U›£êÏ!O ­Í⤴çhÓ¿bh8ª;l›´Sˆ\Q(j-꩚;Ë$²ÛèjrÊá¦Âáh[×§¯ß^~FÛë‰Úh|óãçOÛ~!Ó3úþTúCÿ0ÛŸâ€{L~<Ã⯨ê÷*0œ ð½@䯭²ã¸›e¸°Ð›íøhòçX×ï”y€³¤äšqµå¼ÐÎU†B¡·þ=uÍæ w§gÃý!ÀÁ/Ëþç—d/Àùï6I{¥ùwî6ÂÎöÎx}᯹=Âw÷(¾‡¹ñ¯²¿ïÄͰ¬_­ÜÂÜÑžž×f“bQ©ÅE¶×·ØÛ €ßGÿ¢ì¿ð_`…hG’Tê/¨l¯o±·¾8\Œã^XE|hžVù•Kø.6²½¾ÅÞ`ø¾``Ñ pÖ„Û<ž€u‰ç{šl¯o±·@€!ÀÀ}+ÀÀ= ÀùàÑÄ€û F‹` 0 À` @€1 `Œ€`@€€{H€#}{{/5™.käŽòÂû?X¶þÖË6Ù`ËñÖ9G8îó@É(—6ÛbÉü˜éwËRæûQj:G.»Ô–•©‘óvoH޵[]_¦ïíŸÍýmÇÏS<;„[9Jâ|~Û¿dp~212=qÌÆõdþ÷ þÇÞùüÄyœq|¡û‚µ6käW¸5Õ"K¨•R±ìXEˆ.kç ꪎ£ÆU²­¥ØE‰¼•bcrªè%”¤¢uS#X²ve5–Äâ’ª—ö²bYÉZ‰â¿èÌûsæygw^vßšTú~„áÝwÞç™w•É'Ïû΢àëoÄßö¦ßqÆýêvP€§þÒ‹ÁZ`Í|Û>™œ±KìE•}bï¼ßªÕf¿ܳÃÄ(&ù…<>žpç‡ï_ÓŸtÚ¥±·1þw¬^­Ùù迟•ýõlÜ`&Ã_~õéÙøµ/”m2!'Lsþ¯:~ú 6кkæÛöÌë¼Ò·ÙК& Ù]Au®æ:>Nç·Úò<_pI~!‡'Üùáû×ô'ÝŸvaÿ=gª;l}ü{ªF±6üh~¡¶"ý÷€ju IDAT¢àñç_Æ/¾å ðä…øÛ·íyøþmE[9!§wê7l e5ß¶E*ÏvÙØzØð#ÝÃ]±ïšO”²ëmuà .É/äñ#Äæü£ôß¼¿H˜‘L©:lcüY€ë,d¸ñ ñý€(øôZgüþ?g:fÍß¿ký6}6þY°ÝH€øs 6Ъ‡›oÛb¨Àõ&“[ý~x9~N.çöV¢à–ŽGOdýG-À1•·“ïÜQdöÈßOhU€ï};|Úà±_Æ/ÛF.ðª0m«'äå3xh]€ÃÍ·m1QâŠ:˜ß>ÿÿ$À“…b›–­·Odý¿n#ßDõH¯Dùû ­ p÷8·`O€'?ŒîLşį=´•râãŽ~„ÁZàPóm{ô”ø»œ©ü~¯û—…z%Ç×(âx®`-‚´ÞT€{¦Êåú¬$¼É¯ÙYíþ?”+µ•FðÃÙòa}δšcìòÅÃÚCÛ½VWîVˇ³1û§ôŽaÇ›ÊL‘žðF]osܶ…rx‡5öG­ŸÞ«¢Þ L¢ïçb¶ê\†À n´ÊóÉÅ.}~!Ž÷WÝã‚»ñâUžœ/Òž¾ðÉùòR°0þûÈŸ\Þ?U~ WÀjvÿ‚ý'M“ °ÉPåCÇ?øý€×#À#â·ì¿ \ŠÇ? ´•ò¹_|ïk e5ßF‚éü3Áüãpcj~ü†õ0ós¨ÐT€×KÆVmŽi÷SEøiÉ8¬­±÷{•þ[06kÃóÝ‹3~f†]~n¾{º`Ê™býÀX]¶~º¯ú+0ñø8wK¶á æÝ#}«ÜS5²ûÃó‹Ææ3}~úã=U~|~~¬*°O ÞÀù4_6¢ûìRµ¹eû‘k9e¾bdüƒLó'×£÷O•_P€Å°šÞ¿@ÿB€»1ýø¾Ÿðšxü¸½ÇØ'ñøå@ D/À¡æÛHÌÛ–øøïÀö ‚„å ó!Ó­à[2ÊÖ ¼öº¢ªg+Ù}3vs¨`(·ÍaF´eqÓLšVEÐ^\éŠõK*_,¯w æ7 »ì§kdÁ˜¸æY[éôï¸Þ–¶ãOçì%‡™[‚§ÉO{<“ã騂ïÆàÇ£ŒW:ŸäË…´’Û›åv*Îbþª|}’ûãÏ߬¥÷'?¹=_•_@€Åxšß¿@ÿw^Wä£8î¾~)~ÿë©ñÅ/_𠘶UrÿÏ:ÞÇ’}@Ëj¾î±³±&xØpÞè´7ª V€ êåœrm&&\vçö®8Ë9wq¥;üó©<×°Ô’õÓà+t&^6tÞ+M;ý9o©2ß³…Ì:]~ºã¤?2^*Ðb¾VÍvÕ~xœ_ä¯Ì—°7þÎå¤ûCó§×£ç«ò °OóûŒ/ÙÅßîå1cáÆŽE€»§/Å-þþ'kAÊÓ¤­œŸüøÔ7ÿ9áZàî0óm„0w[M€—n3¹­YUØ-d¦óªcþK»nÓþx’õ¼Û•²NâÏÑÆ|¡”V`rúöÊ¥£ŽÁ¥–,óó¹“ãÏëòÓOçÝU¯}AâQÆ+žOòµâµ•q²^È_•/­KãOïÍŸ °p½Àùªü¨‹ñhîŸ*>O€Uù¨ÆŽG€»þõå›_~=5rÁž€i[5!¼ê8q{­ p¨ù6:2^/¬g=a‘Øø±wšB€'J†¸¿ oö ¿¦¬Å–Î-mÎ B9˜'SˆWgr\áØå-NçüGn‹õùi޳n+ R!e¼¤‚,ïçÃßɵ36Í›ü•ùö‘w€cM˜æO®GÏWæGXŒGsÿTñQÖŽ?“{ R¾!/ÂAÛâ„<ùê hC€ÃÌ·‘á PxÞP °° ´«Mªƒ' ’ õùÍQ®V–D&Î-qs…’¯ÀÔ%ö1DŒâêëÆœÉgÚütÇy…Óy»ËD1U¼²ˆý–¬Å ä¯Ê—T€åñ§÷‡äïlƒktÿTùQ–âÑÜ?U|Í*ÀÊñ€càé³'¿hÖ&ä_úæÁ#³ ´)ÀMæÛèHT=¹Ó¦W‰grÅgÁ ò'–Ï‹rE+ÀÜÂ$¡ ¬€5ºcEµ¾ÃKÀ®n¥å7uùi{‚˜pWI–âQÄ+É— ©¢\ñë(œ¦o^‡àD£U åx4÷/ŒkÇŽW€Çg:íõ(´ÅE°~Ûñþ0Ø@ûÜd¾dÕÙ}56R0Zà'î#Ï|!(Q€ý·CeãÞ1DÏéñ>•pÞTT¯$Ýåûödw»$Ÿ2öí8›§ä¯uùéŽûñ]]r~“âÑV€I¾´LóWä{´ pŠ>x>ª`’`9Íý;rX5þp¼<ùaüòT“6¶A¢à&óm”XåÓ˜UVm$À©%áYf*8®,ñín$¾ê­é$“ÉI¬M+ª=U¢OË9{™i_rY5×ýUCæ·Üüx¢jØù ñüVb4m˜æK+²U i¾1mXº?4]8`OóûwÔ °rüàXxìÎkš´!À@ôÜl¾gÕæ»;F¥Ñ#Ð|GYK` sê–#¥lEà±’°NÕ@½îýÎL¹¸aïÏÛGvÄÙ˜VTó›Ò“ÔÂÐ.¬Ã{07íՇͅ}~ºãÌ÷xò#%#k ¢¶Ló¥BJò櫯K÷‡æ¯à`~T€i<ÍïßQ+ÀÊñ€×#À§ã\€Ù?y{ñ¶5ÿþíÍ“·ºUmÅ„<ñÁ‰÷z1Ø@ëb¾¾MìáÓ™‚±½&  izUÖDÕÈîϭΧÇ(Öæf–Œ}o¬õ©ùñµ‚ðž.TÿÅÑ%#»ýtnîyÝZ¤‰ØáÜ|÷´½°U ¢:Q’ ¨“ìssNˆÉ9Ã+‚ö°h·Y‡ 3+>M~ºãÌ.³ÛÏëKÆêß÷˜ÄÓ°ìÉ—)É?/íO%˜âý¡ùë˜æç °é\0OÓûºÜlüàµðéµÎ¸ß÷wíä½õé[ñ“·¦ìãr[5!÷|ÜñÞZà0óm¤\áåS¦4+ž°p)âxUº¾’½­Î¦B€c×—¬ówMo`çÞ7]eÛò6M2‡5¬Íz¬Z±™qΰü‰VT“Ë9ùMâ ?3g㕘YÞæ´‰Ñª³½v“ߦ*?ÝñÁ%+ÚÃÙ4_Ô‹ÆÓ ,ŽÉ— °œ ߘU1õûS¦tùk˜äç$eÏÿŸ…"ž¦÷/dX=þ[`Àñ °Ý¾÷í”tÜm+'äå3?ù5hC€CÌ·‘2V-—™­f¼ àXÿÚA¹X©Ïu©gò€ÿ¸+) p±Rdf‚ Ú¾ @¼»l¹Rß°‹€cõJ®xX{è ¥PQ ¬•ö¹õ*†ýR™Ò¼Î»?¬Íu‘ü”‚«=nåÇBMñmh< *ÀòøIùR–óÿ/{ç÷Åqð³¹5hÁgÄ RÜE"²-(D±,˾„4 ¥¨HÔÈU¹Ôª VS.\©R¡}‰M ¢ù2~H iÝHT‰ªV¥T}õ â°TYò“ÅÑý½³s·{¾=ãŸÒáñÜÌ~¿³+>žÝÙH¾r*žŸHþqÎO`eÕÆ•Öœ4ùõOµë†ÁìÐ\œÉôßXž/ú[7z¹X˜_¼›Ñ¹Ò𕦾þà©`hBÎd2mÕ·x^ÛŒèoÓô× ÀÐ< €3! À02 € € ÀÌ·0Ì| € ÀÌ·0 µpÇů.\øóÿ}¢Ü[¹Ì„ @€ŸÝ¹ýG5ð*η¹É-"¬M¿\y“š¶ÔÖ>£nVúÆÖåÙŠuÛ/Un'è*MßmйÉ_š¾UGóÁâìhµüâê“Ç“°}âþSê/Ñõ5¬ ·*ÄS­~Å×@2¾øŸƒ­Y“ÖóŸÚ¿èÿä (¿õá!uHE€Oìɾá ïªÎ·zýÙ–õx•ç[Ý0Œ#/Ô#À]%mJ¬ôÍV´¦¡‰ÂÀÚ)|çÉ pOé^]žç ®”_Âúäñ$kŸ¼ÿ˜þBç'•ëKÙ¡Oåú•_oÉø“O‹ÿú} ûŠøÅámÙ/ÌɸïÍÖߪÊ0@ýÜÿÍ•ìþƒ¾?ù6_—çKBa'µ{×+~e}o[f­ ðÀLaª®|Á•òKX_Camr4UU€Û/ÏÏ/ކ„77¬‰5>«ÜyuyþÁÂåJ9‰êÂüãÅs†Uì3?ýxáºíV×.Ÿ-Ï?ÍØŸ^Ðsšo¾¤y˜Ju[<`êz™ãæú Ñý¢ïá"¿Âü|¥üâêõñåyk|®]ŽÄ‰WÕ(ßα'ÔxÙrG¸ƒùGò•û“Ç_!Àáü¥ã)ÚGòS®\/ŸÿhÑë  .îÿ¬õ¹ãÖ:ð§­;±6~w4»û\F€Òàõ!nø|ë n[Œ{õ_¿¶a¼Z€s…ˆužëuÝf²è¥¤î°p™úÎͶ@]_)X{4y¤û¼×nô¨ÂCŸCNŸ«¥1è(íì-[({çÌÂÒ>ëÓ{ôÕÛ*(Àv 1»æF Œk_YäSЦo¶Åç— ¾³ìÖØ'¯B€Ãù !möþà åÉ·[!ÀñœŸHþÒñäó§ÊO!À¡¿”¬»,÷'_oõ pÇÕÅ­W{`ͺfñ‹CofxZ.#Àé pÃçÛšW€Ûÿ²eÝ?>º>öñ×-jv0œÿuÓÿß<4Ö?lÝœ3 £{¢ªOÍh÷Ιš#üT±ü×íñ ³áÒf¥ÿNh³ ½cëÇ=-Íß[xÂ2@S(M/.k×&­O÷±Q&ŸàìŒmx]%·f넵Ü^Ö K½cãËÚìÝøüâëÛË¢~l¬¯`/žH¼‘ör¾æˆ.™‡Z87ißrÎ_™o°?iü£,ç/O>ªü¢Þñ«ê °r¼×@}Üqx›ýÖ…¡“Ö^Ð;ö¼Üœ•ß?.—`€ô¸áómÍ+À=ï®ûç÷„Ln©*À¾Áh÷-QñŸÝ.SÀn›¾cI§-`Ú¼Õ@¬½^V­–ŒÌH÷„¦|MЀ©_V‹#gX+œöæJ¯Z?äKÓóSm]¥Ù‰‡æ§kPÑ¡„æY¯êœs=«ÇŽ¿§X˜2+r¦[‚“_lý`Q¤c žƒ2ÞP{)_!¤ŠFÅç`þª|õ­!öÇ_¼X>?‘ü¥ãÉíUùEXÿª+ÀªñHM€;tÞ:hOÀ}onüÈzé¸\F€%À œok]øyËOvf’ °ðØÑLŒ<l={›q_l]Ö4[€&‹Ê­’LA²;ÏYŸ“Ews¥3âûù’Цü%ëÓàWå¡Ä²­³©RÓŸó”ªé{¶«9—_\ý€ÔŸ2^Y ƒùZk¶×ì›ÇÅñ¤ü•ùJì¿s¸Ðù‘ó—'·Wå`)žj+ÀÊñHM€O¼äø¯3_=°»WÌÊ®ûe QÜÀù¶Öà®/7ý>Sƒ›î6•©M€‡n‹÷FU+ÀîBfOIõ¢\Ýh×-Ú_Ï™=?lË[Ä}º_(C;09}{Ë¥ûãÊ_²$ÍÏçLQÜ—_\}OÉÝõÚº@<Êxƒí¥|­xm›Z\¼É_•¯¼ùüÈù /Ò^•Ÿ,ÀòøW[VõšûþkOÀC'ßúTÌÊYçè@h7r¾­qØü|ùµð ·™T€ ž0†Øø–×L!À3Zð}8¢¸9ðcÞÚliû¥ÙÑ€Pv•¤›©wo¯, å2oypOÑ&vúV|~1õf·Î‘+šx”ñJ+Èá÷ÿˆgríŒ c$’¿2ß­Ò3À™*,ç/On¯ÌO`9ž*+ÀÊþÒà€ÿŠM8^éÿ¬U¼¡ÿ={¬PH_€>ßÖ(Àú¯Ü­¯’ °/,Éø¦R€»@»j¤zcðÀ„g|ö¡¼â>¡Š–DêÛ/ wr…RìÀÔì£[Ú0J¨¯ó`Q{àq76¿¸z±BëìÄå ]0U¼açk¥ù°rþª|¥àðøËçGÊßy R¦ÒùSå' pdü«¬+ûHI€wœôý×z Ã7ÛΟv7¤”Ë0@úÜðùÖ\ýo-UØ®×ÑòìN·œ@€õ²'€gêà²$ÀƒÅé»ÑŠ;–wåJ^ÖÊÈXûæ´é ZŸKÀ®ö„Ÿ<ŽË/¶Þ:ÝÝÕ8"ÞðøIùÊBªXŽîøU‹÷ÈO^'`½Ò.ÐÑxÔ\ °^F€ eî;šý¢×ŸŠo´î?¸ÿ[{a8{*RF€Òà†Ï·G^ØôŽmooP °To ð‹?³+ÞÞd¸¬9Ú·wB[‰ßvoyAغ5lÜsÞaíÞ·tçàÿ³w†?Q¤w֙вnD¥µxÍ™"QÀvK »JõˆéÝah¯$ç¦\‚%9‹©‚\z—Bs—"œr£¢ð.`/Òœwži¨GjÚ^_î"òf_þ‹>ÏÌìì<ÏÌì30³²è÷c¢>ûì<ûû=ÏÀ³Ÿ}fŸ±­¨ft—Þg'ñ4Ìø¡òìî‚qÓà*öÂkQ~¢úl|§FŒÿ1ñW€¹|ù`>‡|×¶\Å_x~H(À\~¼Ûâaê¹ñwlà²kìVí²|û¤1f/C€€À8ïómÕ;ºì^|Rä(À\½ú÷¢¿þƒ–kß-ò"Àúò©¤-«º pÕˆåZf^À22t‘n6làSæžN,ÍIæqÛ.ÐüŠjé{ 1Ýú9sSÙic9ãaæ.Èó»’»^]Rô|ji~£p˜Ï—_‘µíÍç+ W€™ñáó­ÛòãØ!k=7þŽí@ {S>z’-ゥmÇQ/ÓI™/C€€à8ßóí®7¶ü‰øGí“mŸ8^Í×7ÿlËo‰‡|Rôaf-8'Ʈ͔E·K ée5RÌX·<8›Xd86kÙ§ª:6ÿOLizJ7BÚžy܃Æ}€ùÕšž9æJjËÐHŠy¡ñs}8:8*ÎOTOü•&pVIèBÇÆ#\æóå…”Ëßž¯x˜>ÛóãØSÏ¿S{ˆÇÏÈ]}¨ ·Vh—DÇn…J´+±ø2ð/Àe„&*Àäß0ßVþ®è‡ÿ¹ðÕ–_ÿÙ+JpªßþnѶï†}UôÅ£üüÏÚŸU”ÕûmÃÊü#€FóšW-)‰§uý±‰Õn»@U.(ÓË}m#Êss¬É“ýMÖïéRAÍ~¶zDIÌßïë{˜Ö6i"Æ´Ú×_ܪole[Q=2Ë. ÆÉó¦2ïŒ#IÅ\-%ÑΓÛµøù‰ê‰]&æ¦G”ëIzßc.×àlÿqùrBÊåoË—oÏA€™ñáó 0Ÿ_Fp3ç—S<Öz~üÚ€ Ø„Þ|¡˜LÆrïøø˜,ë[cñe0à[€Ë&BÙ øhþçÛpÍOhÛþ¹Óº ´Û¯^s¨7Ê[¿xýÀïõ‹¡£Ë§DZFM¡¢C1W·Ïê·Õ™s`©}D;þiÔ¼°ñä)Ó›N‘Dz2!«h7ëÑÖŠ£ÍÆšÿñ+ª‘+Iv±™™Ô1—˜IæÍvÕCKÆ€ô½›,ùÍ9å'ª¯Ñ¢]í>@7õâãqY¶ö—//Àlþ¶|%m8Ûžƒ3ãcË_ À\~FRÚñô3 §x¬õ¶ñwjò#ÀÅM7Z´Â1ÿòe0¸ç{¾ Çÿõɇß}Zq`IâëÑrשê-ñò±¥Tj•Øj³¹bÇ °T9±’š^L÷…,¾BŽ¿ŽXxzqÙ²óÓ"hÏË%¦¹Dj1=¥/jÆÒ‹ÉéÕå;¦PZVTm;05[îskî.UÉ,SFÛió«Ë}a.?GÁÖkù‘P«èmøx\V€Ùþcòå˜Íß¾ãמ“[ÇÇ–¿H€ÙüxÁuŒ‡`nüÚ€@؉ؗ3uîe0 à-¿¹4öã\ßÌ¿›k¾ SOŠÐ;ârk}Ö¦¢ªîìtoDeJ‘p'iÑj¼SQÛËvfRUUR£N­E¥êåÉrq 5=ìNË4H´“ÉÏNÔ[½¢ñçxTÇ^ˆºäk;Ú’®|£9z€kþ^P]BóØÿ|æîí@°¼ÐÅ€‹ж®A€_Šù¶tÁ¶ÛoptFÖkDª—ª\pÞp:Dòìvj¹£ —@€€—^€·¾"™ÈìPœ¹_PÁE(v´ŽìÅÀ…Oa·¿Ùã@€!Àx½zÓ¡¬._í”TæG7ßxÅúû—’Ì]›@€!ÀØNôI%¡û o¾áŠ,h¡O†qæ @€4M¬¤’Ù-„7êR2‘J-á{ª†`‘NI çÞâ¹°éŒB  À2 À @€0æ[ @€1ß` 0 À` ¼ê\6øåÀÀ̃ì1Z®[ó„\[ßðz®rQ/_h 5ôv}ìRÎ/¥Wgffœ-÷úüC·z¿ ãätäØa/'ÍÆŽ·ßñ rü·×—¼—ïx7º¿ØÔü£Ý»Þ(¾¢(ÏÃ¥³JbÊõwÊÊœkÝ®÷ã(5=Ó_ç©/«F¦ïù8¼99×+?Q½÷x<ï¹ý€ÚóB¤C¡$îßÿÁŸŒþ§%$B—nê4Ýh¡åÓC'Íç´ï—׉&dõ\èèžeöÆÓ†àÔž)yKœAå LIïã¡Ïs<é––‚|þ§Îå\Ôž‘3œß½.ý}¨õ—\r|§ñ~þþLn«®3ÏÝßÅWðìg¼_@ÿ‰Æ×çø3¯„çù|,ôŸ6V€ùùÕÓ|»~TžJê‚â.ªäÏ,3wíòrwpé£@˜ËÆ„3É IDATÏc½÷x¼ï½}A{ÌøøÅ]€ýöpç'"ÀemÄ.¿“彚7µ5Êä1¹¤Ë0àØ ÙÄ?Ìú_ΰ¼oÇšX3rW1"ªÝÐuönÿÕ™Žå¼ pé¹FùôÐÀø÷-¿|Mäà~Abµõ%”oÜI!Œ¯ÐØ×x¿€þ¯Ïñg^?ÎóùXè?l¤óó«Çùv½Ôô(“t¥oÎÕšâɧÕ9Õcñ¡à=ßøò¼¬àrùy¬÷·ã½·/h¿¨ÑhÔ¹A¿ýÜù Áð¡³ôŸ&âqGé­òm2ùÆN„J¾Õ„øá˜ÜÐ"ž½_ò¶õ·4_Î pÃ[kà};£w붸¯‘wØÌKñe‘5XOÇIí”ÈûwµnÇ7üáÊ;yÅç¦`_ãýúO4¾>Ç?pÎóùXè?lœóó«×ùvÝTõP…½¢|sÇõ)Åua©ÐøÈlbÒWYÁåòóX¿†x¼¿–ös·¨"UN úîÿÏOB€‹ËÈísç7µI—üs‰qqµ~VÙDH¾ô -$œ[÷1‚Ê—³,ëÞ˜®GZC®KÀíìZ3_ °éJ=â_¬ óe`_ã½ùû/p~•Ï76R€ùùÕó|»nj‡©Þ4'¯ïðx@ApäJòÙhP¼®ú ã ¬ý Xrà|çëãü€u °yáÕûò>2é¶WÈŸjD“‰¸á3:!÷þ»®LÎOð)Àq"‚ÚÐúWÛ7TÈ]Ô„µ/ ‹&do;`i¼÷QèøÇVVÇ[{»è&Wj›f£qÝ‚â‡? ›\]¡=–¤Öñ¥ÛȹÐ>ë§…|ÙÒ¾n¬…#±ñ±Fyï/˜¯—¤‹c ‡´|j÷Ÿß]y£åöçùû1iR½eûê0û†ŸkïH½öuã·Q/´˜JRyAº«“ñúvé¾|$ôxèæÇxÙ|Ih•£¿x!ážOŸ^QÒûÇ¡›å.ʵ'±|ØòÉ9¾ŽLìÚ!åc¼EýÉŽ·S>|ÿ1ý!߯ß.À®ý팵}.žöŠìçVäô=[{NñÚOæüÉûÏ›U€mó«·ùÖ¥³ô»œU=ÏÍ_ïƒéÅ$Ýúˆ à®am¤Éœ\Úº’J¥»át(tÍN+W^[I-.ºE@«©ÕtŸþ£yùéÕå;º{]½¸”Zí–ô¿Í =ުń(ÒßèF3Þf¸¹ÚN›Og=œæ—H¥ÜòÕ«—WRZÿ\µÅc‹×©}&ßÊYºÇÓå%Òå†p[ó·åûöÎæ5Šl àESÝݤlhÒŽDE v$1ð‰äK$J3Ib6³HÀ@DˆFÆhâBa&Ñy1ˆæe.t‚nÇÅãáÆ·w#F7BVâñî¹÷Vս瞮ª|t†÷,B*Õuê|T¥î¯OÝs±>€uÿÑùˆã ÿìóp½ÿü(“Ì?œm۴Ǽ>­X±b¥¦||)µÿ,¯÷ç/DçÞö»Ïø²âÈÉ:`q¾¼ÚÁ§lúÜ=? ÉÕ_(æ1nö*)>".àìbªï; ˆV™ÄÙ%uvó# i_<@yä ÛXoã?ƒÿ`A&€?î0»'OÁµ­?Ýé h‘þ%Øß°æïWطǰ—`Ý_ĺƒ/ÿ†¿%€•øù1üGçÃù£ü#8쀕•áê:SGûgäCß6íÁ×§+V¬Ô€w-–sðêsëQN½lsùÌi÷Ôù„ä† WkC„·5~²”:µ7຋©Üø“¯Wøßa˜9\·äõz‹ì>w5çÆ2µM„lY¼ô‹ø«òЭ³«Åª¬îo:éN?x<3ûš·¡füsqüJêÔ*ÿ ÄÓïºËO¢XÕW(e¨þÛ§S—ßžg˜q`ˆé…ß 3¯Šô€¿tÔº=¾{ˆÐoø{±ìö=x\8s?E0þ|k3ø[˜ùõù-rŽ®`ÝŸ˜ü¼ÐïN¿]}âöm{¾ãâ©çûCÅOG\~·– €ƒã{ò„ÿÆøé×íaÔ»_r-»°áFú {±è×ÏÜV¬XÞ°øh¾>:3süGþ °ÇîÇÒ\$?|šþóÓ5†1À§DøùÓô×O¿³×ó$ÿÎ¥_|:2óÍìà{8²“þÚÌ7•9N€ (W>Iß»ÃúO °“'ûUN=u`ÒßS?Ç‹Áukéîõ#3³_Ò/^Æû¿¿n öÏÌ[S8°Ç°×8ûË"ºÎNõéÚñʵî?鯪Åß`ì?:ÎåŸ ÀJ¬ï'ùÇÏ®ïKâ¶möàëÓŠ+Vj À»*E±êQ×ïÝÔ|ù,{ _>›ð\)ê³0ñ¶À{»:€U$7sð»×žº:.ïeƒà«P@β/ïí5¬vä®ïs¢8‡€ˆo#ý|~µØ÷4¬6FûÙàœW"3S¯¡ u©yèäõ|kÇþ»×ó½üåçÖ×®Û~ÛT{ž¯|zAEŸO¢E8àw¯–—¿å/ó˜õ6ËÒk=àgaç /CèÇþ610ÚÆË°~¸<Ĺ¤Ú¼h¤Ï`äOL~MÎý”ßÍâÝïŠm{¾#â™Íù6ü!âgÄ#2¿[Ì?>¿záØz¸€ýÁx(x>î™O¿ã@íõŸT¸û}÷zÁ9WšK“Ëæœ`¸Ã8Wð ¼Â)š+õò_'WÞ=̘|1÷ýô‰ÌìÀÅ—jxãs[‹°¿e´û!Ûá1æ@ã_ìþÎQpG¾oChi¯v<òñýèÇ pbYqVý§üÍÖkÆJ°8?†ÿè|øxÊ?€U{× w/‰†=hÛ°ÇŠ+Vv€wUNÊU;»Í§'र$× BçdÀl<,ÆÑ^êÂõ#l”ÛUïÏ9 lDï¯Ü>–w(vøPYŸªn#ý¢&5}ØQØUÞ€Åû‡Ë¾+ÃEfS©ø¨~^w-ˆÙ¿Sÿ-³Cò!C 9ÚùÌ¿(ˆ Ì’õ»JW𠊵ªûÛ0á¿ÊÄK³ÏˆOè0)„> Àš?qù5Ø?¾R3·=ß1ñDù6üÁñ£â•ß-çߘ¬˜ð ¥_µ§nIL5€*°XåºÁñõSÛûÁŠ À›àØ Ns€ùÜ[Ç_¨Æ¬§Ó¨îŒ’½™p åžä)¿¹Ò%ø|ã$`Xã<ÿp/îÀe[Ù$©EꓳT V“@ç_ÜþH²‡´´ê/¯ÙÞ/Ãùÿ¤¿€ƒøËÓiùÁþãóáã)ÿ Ví¹$ ö’ù‡íÁÛ„?V¬X±²ƒ<Ü!ùWðbùàx 'àİüamk³{=/8|™—ŽÚús?À«±S7òõG×øìY7 DH?€‹úÔ¿nýB°²?;ÌteCóÀl¬Îx °“½ù:åºË¿Tðkç3üÓBKS*ž¡½ô€–]Ò¸éÇþvuø]‚i6âÃþÐÑ@‰Ð‡¸Š?t~ –…Å®w¬ùŽŽ'ηᎨün5ÿGÅÛJ¿j|!0&ã¯î1Í2ü x/Ö RéÀ$¥%8‹Ó&‰ªqžƒYèÏ%^ªŒó/nˤßõ:DÅÒ^õxä/·Wd×çÏ¿þSþâ °œì?§r>ãxÊ? Àš=ì+Ÿ.8 ý‹È‡Ø&ü±bÅŠ•àw =€§°›ðè.T[ëê¨>Tą̊cÿ?7Ã9‚l› êNï?<\îû®µ£'/æfn^)‹ÂÐFéçð Íçs€•ýPà“ºÚà×R3ŒÊy3`vÐj³lÚD øµó™þßRP-›]Wð+‘ú±¿•`N' ÀF|¼YæJî? U¾%ôaFþDç×`߸Vþ]öç;:ž8߆?8~T<¢ò»ÕüoC(ý ·ƒ b}”?š˜×OMï+V,o^:ƒ dRî€Qàpð³à0€OuöH.»Wòå,is*ƒ¥‘öCME¦F6ÁÊxQ™Ú0éúå2,ʧÉeBgR6óëGàÔ ;ÎÔDÊÿ$±ì ô£e_”Tô€ß˜Ú‰ô#ý™¡ÕÇÇÉL=¿ õðqj†'¥°îO\~MöóÛËk«Ûžïèxâ|þ ø‘ñˆÊïVóïT]˜ŒwÄUØ©”y·7¹ÖGù£‹qýÔô~°bÅð¦% äüˆ`¥ ´MÔŠÁ'æâ§ 6Û¥8Df÷Ì‹ù@ ˜´ÿ%Ô0 Ð×·¹s4ý>—±þÅ퇧|¶€¨ÚCÙ«°î/wóCuÿ)QX?Îò_.KäTËå`ÝÏ©ÿüèU¬ÛTíŸaÞ&ü±bÅŠ•঑ù2H¯ŠÓçÆÐñäábŸFBx›àìbjhAV€õ:U%Õ3[¼ËÿTRcNÐÚ¯Í@ä-&¬¢:ØÆØ¬W`@'ªB¸Á¿Ò…w¸\­ümëþ†¯ô†ñB`³N˜­»y¥(gjâ]„¾hŽÉouµ¿mÏwt<‰ p4SñØÇæ?€‹¹¨)ܤ~€áÕï¼'W3ôŰqýÔô~°bÅð¦%»à¥-ðàÎÑ•—ÄŽOµ^GfXL U€Òè€Õö&½¢¢õ%(ûøÔ¢Ï<Žó/vˆY¿K²fa¯?ä/R¢lvüÚ·à™×I8[­ ´iO¶¡òeTÎôñϰo[¶bÅÊßÀÇúÝåða»ë÷TûÉö¿DaØOò@®?Ú>¦‘ÛÇœ†Qör³œ¬-ÛTtA¦çëFú‘¿`¿ßºÉ­2Ø\Œ×Ég¦`Y}$c}‘—ߪ\«|GÇ“˜ ÀF<¶À‘ù`:Ÿúu†YчåH¦¾$ìd2yåú©éý`ÅŠàM‹·&WkuZçÒ›à?üWž¡” ÀáìVýÿí›à4ü¿A𩬜lTT{ç4Ü…u{º?d4>L|üF.¢Ó¨¿xç_Üþоïçåoš=±`ä/®bÿ 7VnÄ/ž·Å0ò°a—c°lQ>Ö?ümØŠ+ïZ Z]‰÷¡‹®»|F¢ðB‚2t¼B8Ò³;€½J9Wþ{çÅuÅñe™]¬Y¼8Lm“%I©›vlÚÅB¶ÁQ‰qIÒ:X‰@b[§5%r!?¶4Uì&PƒÃÃMecãb“ÖF¨%P"!êª-±ú@uŠ,,7•STµÔ}|L¿Tª:w^ž{çîÌ®wÇìÆÿ߇„»;sæœsgöÎßwæ\ª ´.` Vw’²¯å_î$[Îà3‚ª:ջ颯PUàÙ `kè˜ؘ+ËÝxq¶7üDF+íЯ£j1½þ*cŸ‰WªÌ$Ëiù¢ü³äG×0¦‡{=ööl°SÿÆÀ…r¿ä¸Ðßù´V¶>sþ¸ùHæhÇþgúÏ)ßÖëÏjŸÀ¤Ù—jugY{q `úüq÷zxÖ(Ó§eZ5–^Ñaz–™,ºXj$ÅÍx‡QÓ‰¦,B}n©ÍΨfOÐÏØ’Ðw©Ebƒ²I]‡U†ãŒï„ý÷â„Oç3$¾SÖÇ`6^v†ÔRš×ã8Lõ¿Ó °%>Vsü1þÀ‘ïëÛ†Ü7~VØRE·Ww+å°žŒAØn@N¤ÖŒ&:‚¾°ºÂ¯G<™¯Ü +r†üŸÜy¸|£¾)ÑE ƒq=ËÚOToÖÖ…Uˆ]f#€EãaMã=RÑŸø ¿¬>È#¹òý~ÿ‘ÏPƒPüSÕ`gŸ‰w¥:רà-Ñç*iÿ˜í³›µÔ‰5Üu^9öl°SÿÆÀ ‚²üMÊûÛ!ŸLó•?^>’ÀŽýÏß)ßÖüZí3XÎXË5aK7ŸN˜sþ¸{=Ÿñ î:c~Xj;åŸÓ÷eêDgá¯Bˆ´?Ž3Àl¼¬@dâ·Æë<Lõ¿ƒ¶ÆÇ `ÆŸì«ÚFúº½ñYüaÚÀ€û&€Ë÷õÍ:D Wç)D‡ÏxÊ“ÐKdJÉ€,ÿŸ3 Ëõ›æ?²íXØS]¢Uéý†7лó|ôäEef,{D©¬j”Ûf"€—IRÕp LµeŸŠßØ{±@Ør5>AÄØOT{VV ÛºÏGÛ®)Ç·àÍ=j½[ñØFý‘Ïà÷¼ú³²Ë£›bßðK’äçÝðg?å¶ ^ìŽQô§pèíÞ»÷rO'¯È¯|¬@ïåÖ¡¥µD[P•ñÉÃsõÝgå„wåq;’cÏ^;ô¯U Þ8+E‡;…âýnô·S>éþæ >*¼|ØõoòýOß)ßʱoöGû#‡ ¿‰ÍÚsÀœóÇåë€ Àìøê0Þ¦²lëôÅšã¾ë}””¯Gã÷uÂWqg}4Ü7½Ï*XB#¾Éæšß]£Ö¹ªhißqÓ{ºD Î¼(º²ÃWqýbsóÕ)¥H“¬À¦›£‹ªÕÂV–ÕÍCôc¹¼]¿~¿¢¹Üñ“ Ù²·×eƒm5ãWö9Ççô½¬.+®_êð}W„¬{Ìøsx&L¼¬@¤ã·ÄËÚãFªØø0Ÿ.€%퀬?›‡.M5¯—¢mú̯C|¬?Lp?°Yüh‘<ø M==‚P¯= =³Áë€\›·Z]•mÇÀEÏjË´„ºJTë^²§¬fÔkK¡d¬¬ÏÓm-PVd©Ï¯*0k?aL”­š/ú98÷ !p£½µ§Ók,ƒ¤<®KXû†Ÿ ¼di×”/{_Ye7|ÍkZ¸ØbŸ‰·\É—Ðòxyž¾.,å³½œo½Ãå>ͱg+€ú×*€õ“Q›–Mu;å“îo®à£òÇɇmÿ&ÝÿôñòÍ"rì›ýÑÛ3¥í9 `Îùãîõ@æ `v|uoSÎV2}*k’S3$WùÈgÌ:æ©ËÖ\â –Úeÿ;’±°¶q¿q•ï?»b,”5¬OY G™+–Ê´=ýÇΨODè7‰ËÈžC0É1‹ÑŠEÚJ;jí&S|—xñ9}¿¦Cñvzß:RÔ‹õ'Æ °9L¼¬@¤ã·ÄëQf€gìñ£¹,ñ;`&>-(eò7 ‹?EÚ±*|¾ë§â‰õ‡iCÒG/*íªTíU‹œpãµUñŠmÇÀäKõmK±­§Ò[|£}giU{Õ‰+mM7µ¿¥Y߬,¾Ñã‰W1öÀrL­yò°àš-zÙ ìáC•²Çʽá÷4jñ­äÎ°êæ½ÅÛ»ùøÒñzJ‰½ÓùÆÂ6Œ–ü´µŽV–šnœ^Å?:Çž­¶ï_¾6uwªûÛ)Ÿtó•?k>’Àqô¿ùøŽù¶`µÏ à`µ×Ü7”=Çw€yçË×À³%<166-«Õ2cÀžPß½±ñ©f?O°”ß“÷¿àšðÀø¤IÌl–EÓ]Ó1W16>Õ¯Nj†§Æ#Óêª:쌪¥S™iY£úRˆš¦”j‰ùéÉf?Wà:~¯Ä'»º‚,ëÄúc˜Î/+éø¹§œ°¹,ñ; `:>V[ý ÕNÝ‹T(»ÄÓtpß0ðpkëàúx¶\¤t’È´Sˆñ„¦ßŸ^9O^ÜIÿÝ– ÷IúÉ_gWË’‘`\v¸ù²ñÏ/‰‰Ûs‘¹8^"ùdóç–výïöù•T.yçOû €»xáó-ؽ?ný¾ÉЏÌÜ£¹Ò”|MûëêlAªô×yÌ?¦¡¾~ÉrØ:}'Q=¢Ä³&yVNžËqaͺ1 '(ÕQñqî(âû^uQûß‘›)F¼–½MñÛÅ+ÙÝÑýcŽ?®{˜XC¾ÕŸ:∜² è‰3>Ž?‰ús$€ (‰ÔØÁ¶ç¢L½Ê>Ã}GÌÜîÿL;¿p=€ À2 ²ÒVë×׿ÚoJ¨ г[÷ ð N»AP]µ/ºl?Óý€tÀHqŠÇ×c§Õ» º @ÿÏçó ×ø8à¬4ÀÁ>½‚¯¾^PÚý8k´]sùðl0Ãígº?üñ@ÕÞ–_í==y3_„  ÿçÑù…ë@»(€wù¦'OF£Ç&"zQ£Œ"%õˆ©U›àŒ£6O¯€Ò‹û}ôÿ¼?¿p=`ׯ[i„ôUë g^zƒ#Šëçü8Ó@g4ÑáÑÊ’â¦ößaPGÿãüÂõ €]oKûîEfJgâD¤blêÞSœéÿô?Î/\Ø­ñ6XGê³Û–xNoê$È_À0  @À`2 @€ €!€`ã- @c¼R+€—´ ·¶^žù LÚëcÏ?ñÁÖ®ýùû¯¥i²¯Þ:²ô•Ãÿ|m<4ºÉvuŸÍz汄ŽQôã¯+÷YñÙgHtû„ìÍBýßìûåö‡þýy÷pJó‘àù¹©y¾hño>^îvùd[þÞ2ÛÞü˯ÎÕõÃC|;å·ád¬®-¶­5§T‹©Àeß8âx‡´ÒWgÈBrÎýI^Óù"¼¤F4öôt ÂjeÄ-­)ä:…@}ï{Þ€ü¯µkß-Õë/íNÚcY|mY¢Xáù˜3yÙo/]þ^÷ùhsÏÃܶ«X>XÖwÿz°ò¿ÆíŽO.þ²í.[Ÿ^ø­U‰£ðO ²~Ø»3'Nû ‰nŸ½Yà7½á÷öüqÃßóÝÀ)ÍG‚çgÒØ#¶úÉ‚…ïÆœ›=úçO¼Ûsû£ÇæâúáŸ!Yïž>/I~ó]‡»òCÇ5Þ&£€%I’àR(€×ð#3—öö¤£^wàʾ¹÷'Ù|°ù*ÜÕ¾÷ÿìLWÀg\vجì²ËQtu+Êz ¬œ j(r–óÇq±½3J ŠÈ¦V”´)gQ›”@Ez¢‰UkýA©ñûãÎZ9¯iDïZИS´JÁ´¨e[ÿ2ÑXsïÇÌìÌ›7°, ÒÞû6]™}3ß÷}ogæû>ó}ï;ðŸÙšöøEÖˆ°=ÀÏ\0løßi凼µÕ}›Íkëÿˆ¾Ï‹ IDATÀè—úÀÏ:X¶ù•z9ÂJov84ëm÷À†±5`O,4äT lØî °¬e«m}äßEÌïÀŽ­¶P;èù ¥ôC(ç¢"N¡?ö5æ*mÌ4é°µÓn¿_w«y\ç]û}a®𤧠…yçL˜e à~vÌ ¡‹÷õüäÒ3E ‚l¦å\Š!['F~é»ýÚ]û5»½ù'Û \?ô‡B£?e7g& €Ã#÷ {ãÃÅÃü¾µw;´8¡â]zþβ 9¶Öä_­}Böô€µýË„ &LBÀòD¬WÂ&‡»tDØ6ô`8æiokËi¹Ûí^*‹‹£ |Ä_‚à¸z=l1çUÁ-r{@8öÍzÞ§µ83b—«ú~B4üüTp°')ëLŸ¶ ÷þÉ5(×ÎüÇ„ àÈ}þ<)R À½ûÛ¡Ài puj\Ég®¡ Àsª|ð¤ìékû— &L˜„€ç¬[Hã¥GKgLö²¦\€åñÖ‹n‘^êÜîrŽó^w· \Åõ‹ÊÑ0gtÓÅëç'Ò8õ?fŠ€ÍUÿÜòæ†ïßj.@4W¨%®>Û& २Ò h¬¹Qcp¦$óQ~•2)fÛ¯k¶Í\µâD&ÿÂZ “å7ó'RÞü×üCxl1yYiã–“³¸dð9  HO.V¡/” ¯ J€„Iw7|+g)Ú w†«6Qý ËM«&öpÊöYŽG0-ÇÃ6õø0"{¾¨ðëÇöLž§À„ý$ð>ln÷øÚ¬Œ¡°÷ÏmاÝãñœç×5”V©ÀöuÎ{ïZ»ïÁ&Yáãæv´×ù Ú£´_1;žtÚ¯8t¸ç§€ƒ­?:OR·vÞü°"XûúvýÐ$:¯_•˜0ù¥pøl´ðW À=øÛP0ízWÝÏ{½ž- pm­³¤Ã¦|檮–|˜ jg-w.h•vÙÒ¼´«%£åæG~à´duµ´ÜÔ]ó»pçæ:ö¿7/ç¸Ó¶W½³¶ôJË5þ”lÂB²œâ¡ÛÝ4N€¢`à­€¥$Ñ"€¯DùŠÀ“ß©€ü'prc&;Tž²qÖã|Ž újÃ.îûš?‚uœGþ[vè‚èЭG°;€`ÇCl«ô‹ßr®Ð$& €•åæõ&ƒnýQÀ\Ùeð3'¾wÂp:Fy´_rb´úh\z¥FJýö—`gƒI®ßZ“9úËôíK~—G»›ña”ý{Æ©gQZ듘hc~l0iôií'Ä{êev¸ëßH€½d ø€·âƒ|¿' ÀŠöö[Šäçx„¨m?qþèpÀç§€ƒ­_(N¡YÍu¡<<Œ}}½~¨FÈÏ„Éÿ'‡ÓXßßK×»ú~ÀõlÝ A4v]GàØ+ˆî0àÝ)®e5çbâKFåÆï "p–vÁ/2ôrVÍiÀê2J:ÒKðæ±0'ÉèSvÜŠ X–ɘêã NÅ\ÝKµGm/VK®¨`³Àªþ¡èÓö/&L˜0 Gî˜>N}žúkD½`sÏÂa¿]M–k²ü$ M6zìòUùwM[ËGÆLù®•ÀËpÐT`ËѨˆÓÿ>Xu–‡k‹3ÁpÝà  ÍÇÁÒ>.Q³æ‰vZžÌ3dÉmB?vøç2+ç¿¿»P€•å‰õ¦ÃÛö.üä_ù%à× ¿ýšózÊ÷ÿ‰‚yŸòxÃÉY=°Bà GÂrŸ:˧îy¿1Å”íBü›b:üò¡ƒÛÏe¢úÑ*æXkõ“ís.2ÁÈoÜx”i:^ŸÇÞóÎ×<^f ôNnÛ[¶½‘×`µý¤Ô5{|++jk»ÿ8ŽàÛ?ÄÛ ­×fЫŠãñÝï~Ü.¦†üë»?¥¶öQ ¬lÆ~ÐOâoŸºÐÚ¯ê_ôü$8èú£—ÓNo§Ý~­ûVs÷vû6nà¯ê˜YŽ61aÂX ÀºþvÀXº&Õ÷óÀ®g(:ðqÀwwÞ++›y^\‰”‹*ºƒ-WŒIeU]ÆcaàÜß`ü¸m8ŒþÖ¢9ÕÆcmIeáU‹/ÃÒúueáYÕˆ`/>p³Ë¸³}Jw@E,s ´gaÙìÜ|˜êã! þ­Áx§m€àÅÂ^¬@UÛºšü "ÀDÿèês°‹‰ &L€#³Fà·ÍYrOŽýÕ«K€W~u Y8û#À€|aK\) @ þ·Ñ úwÙ$N,Ä+\£óx0¢«l<êwêS7qæà‘ÑÞ‘¼{|޳’O´{À«õ#‡¿¡ðð$ˆ2;°œ¶µéG›0Ô L¤>•ýèñ^BœêåÌ8G˜ p 4œO`C§`_»o-¿>/C¯À^ð7 ß§À¹Ôéñ-g—Ú`u{Hûc ø1O‰ û›Ò~uÿÒ¸ç' ÀÁÖo>ÊÓ3UÕݺ請eŸçí¼»,ûú|ýÐÄœ5™0À:¬ïo €¥0q?èzîø`ft8Dà€{s¨^b~Æ~p±†€9ê-c‹ñ³Z4iYÜQ-iGBzVLeÄÉ­æ¢?œ%Zö q%Ǫÿ >%Ê Xq%H=4 ÖOý24Ø `ÈWca/ÖËùW×À#ƒŠýˆ>&L˜0a*ŽÌzVô·€g.¾ -^B–k² Oäbµ˜©`wÓ&L˜0a*^:Cò·€wLŸ½²ÀþršCö"–€DvãìWâË’ºåµÀº SÿltaöÏ@N‡ïi‰Î‹xÉšq¡ˆÏvZ´j¢<€7U~A`õ6€'ô#‡‘vgÑXQn.à¥X@½l›3âʨE0*ë|Q$!ëo2ÈofÕ°ª>-Ož% H¿½€Á¸…—¨ôkÚ‡ð4æ‚,㈷5LD†sv÷À~ûË) ×zLz`´Y÷ðA9€}åIÃ5ÂÍ0gÖÿØ;ûØ(Ž+€ïq·Ëikæ.æË\[˜(ñ ªc±ãR™`ˆJóQŒkKƒŠQ 5ˆ¸E$á£ç(”†›@B ˆJ@`L‰(R[ìÊÂ%Ó …„¨‘ø£ ¡"ÔùÜÛ™½Ý=sôÌûîcfvæÝÎÎüüÞ¼ç €-ãü>4&ú™Øòý ØÃýi…×d¯Ÿ—k~ûß ]ž|ÿ¼Í^Ò7œ ‹ÏƒK‘"Øi½M “ùÎ=ÏçsBà‹®¤Q”é‘_B|Ù­ˆ<«¾Kúà$‡„KëD‡€õø!búSnÔ¼¢e#kè·¬ÄØ+~=Àl}ÀÄX%MYûÃö+Fè²þ~TP'þb‘PB&`N?\{R¤H‘"%elZo—-þÉ›pUhûõ˜FîLºG30ô€ëÀé‡|ó–cŽ6ÆÏd*H¯÷¿4jÚ3ë›*GÏ8˜[“C\8«>8W‰ ^-XLûhÁ§Ö©u‡o«ÉzUòN¶UR ^fçCJ9B©ÀŠ–¾"ל3Å€-×㘾.-‚YZ”E*cÖp®®–ö¹ñÁ¥uvМ&Ö#ÓÍÓAx:h™`­-[û/ 1@ÀnÜkÀ_ 7(B&¯1ð‚Ie×.Ðæñôm„[B0PdaË‹ô˰×û“±Þ&yýj«k¼EîÛ–|ÿ¼ÍV`?Œ.EŠ`;­·)`<ßùç¹Ó|N À¡…*ÁXjá„&Ü6ôf”F€Òþ>eW{.éPÍù€àÛ ÓËì•Øoùð2çÅ#`V©Å0[*ù¾ÚÄÒñþ°ýEgvq ‘H¼jŽie ÀØÌë‡mOŠ)R¤¤ €Íëmá¤Àô©»ÁüGSA‚`%X°) 4ÀãœRg àÒJÿºßP9 6Ö@Ëoöü¬YÑ"°Ã'A|týbúÓµ× <Ó>Iû`^ÍEiŒw™µÆöôô ¹=œ ÷qRØå£Ð)HƒdÕÀ¯Pà[“Á×sãC¨î .‘,Ó¼¹ÈÄtI°×€ÍýçEËØôð/(°ó[fôܾfÞ;Fè+·l@%ï@§ÀÌZü›°åEúåØÛýÉp’×OÀzF†–|ÿ¼Í§™"ŸÍR$ Øq½M!óÏs§ùœ€¡…•¬€±Óof ƒeiÚkÈ1sÞ]ŽÄŠ–ÊøÛ‰ôê#[á¥(ÃXä€Ùúq@…§Ûøþ°ýÅi‹,ƒ§üqBÆ`^?l{R¤H‘"%UÐáîh#Ö;[^ _{¹?Ùó»É]?‘ ôÀúçmþpðYûAWø)i–"XÀÎëmJÏwþyþ¨Xï£&Ñ&h¦x­³ä v`èAcâaÎŒ›Ø ö‚¶Údg>F¯ß.r¶¿ €UÓpŸ€õ> ÀR¤H‘òX¸øùÀÛñ¿>Ù;hò³“?Áˆ,å¾wànÀäð/kvuXQ*fúÏä“3ÀCV´qô ðyèHxI=”ƒÔÅênº>Õ`)R¤Hy< †yzÁ|çžçI0m?3¦–Ÿ:Þߪþ~aÍëZX§R¬„ÚòSÍko˜Û‹8ó 9~ðwL+¨ðasóñ~„ ämPý¹˜zø}…·—tX Æ(ÍîíçÆÔS{3õ!¿[µvêÞ˜Š§¹þ0ýu À†¾ÏªíW›ç¶ª_à4Hœ~$K‘"EÊc`C`ò£Á`1üê­·~ PÜ÷ÜùHÏXqhXâÚx" ­ÄèïÔ?ªÄ›”Iì¾ñÇ¥•L<¦Á¬Oá:­å壌,§G¸‹b˶ôpõy?Öë&;Ÿ´ó“ùìÑx$%³Ë‡S¯Ú0êòlæ,¨3QåOG‹æ!à)X\˜€Íí3ãËË%; ßͳD啯{vÀˆŠú|›zõÜÿd´‘æ¡5·ç`Ö«2–o©½|Ó]ò‰=CùZ°²“|ù3# ´§ñôÍ$AjÌçË3÷˜^ïOþü®÷ëÃsÃc]pjçH2k©»¹)ßi²wP|în½8£¾ Ïv¾kìó<)FQ1³æµªj€ÎeÑ:^J©ˆÅ“ùêûHf!«JÀÕ ½œ¸¡²jQÅÎÑRvñ+km­3²×R~jißZZˆÉ[lfæúÃô×›ôñB+ºþ•`N?€¥H‘"åÿÀƒ§þáYôfKÕà$ É­žÎžK9ñ<À ƒÏž¸Ö3¾³§ûÒ¸ ÏÔbG-ý·ç6†[ίÙ˜¨p=ÓWŒ ¼¿åü—Ûñe´²s[μžr»·¶ïÙ¬(…'޵œÿÑ{xoÁX€'~tÓTÖé÷âm\øëÆ–u_¢ º`ºz_ $¬ˆPSeÚ®Kæö­ã+Xd€Ñ˜üàè)¢òJÃÍo£lí‡F"› ¨ï¥Ãé´çdÖî<ü†€¾/þ›4<ݸ2'!w XÙôÔýyŽ) tâñ@]›ÇÃëüF°ñãgî!`zº?yö|ýôCá,WàÔÏáŽp¾;ó´)€S ÀŠÂÎwæy>PVʾº|ùv[ÚAÛèóÌb–¼ðÕåòË·¯6kŠ€K:TšWß +ôö·!›ªRÜß[×~ûêEá-À–XXŠû`4ØþÿØ;à(ª;Žßæ.ËÍæ²9r†„?ÊßÁ#ä´UHå¿h)C5Í H›‘I”óL‚$J«ü‘!H@0§€%èhA;@±i‰Ä™ J˜V(”¦´0 30Ý?÷o÷vïv/»w—äûÑ™ðîöíÛ÷{{÷Þg÷í»í!ùÙ?úö\Áß^ð–-q<‚ãU-À|<~CÒ^Ç ±`)rö­^ýá(E›z-&¨w"­ÙVÒ@¬Vš¢Ù4EùÞ¢}ûï¤âJФ¾AÒ~ÿt°áQå«OŸ>}r»` [ôezãéÿÝ]rL!Áß½“Ù¿LýhMKm¨ü[÷ÅrÂÈ>ƒJ’‚:†¢¸~$Åœ/ì)á"…MÂüï “$ù)e.ï_+å;&Š9»\­ö–7"×Ç·;Q}„Ç?p€ÿ§¦$ê¯÷ù©¾üÔ3cì–YV_ÿ‘EY‡ñå ºˆç¯Ü4 Ü3ÀÊé aŒ¼–ùÛeâ“¶ÉÝgîØe ùÊv(ßÎ)%xßWšÍÐ÷ÂNkèw=×ùåû×ÎÏ=L3;§Ãwðt¸ãU°ñÖ¶C&É.ÞV’Öa¯©…į㌾LdÒô™Êóÿè¾(œk£²Ü ¢õYâºjo¹>JÈ,ô=¾­Cýu(¿´.Ï,5;aðÿä2_€ÙGÌ]Z€EßgG…Ïå*ø2§£5DJÎ-S V‡6 0pWè;ôŽ×x"‹‹,þë™x™0艹cÕLÿÛýßÖ °Æ´Þõªbk³ý¹>JÆPgãŸcýÕ•O•—$Œ í¿†™ƒÍ‡$'ŒÐ)Ø |CåKÿÀ‘N=¦¨ÿô­ íû=$0@€!À‰/Àùé{ª·Ö¸÷=â^úª´ÎÁ=j¦M!çÿÕý¢Ü å„`{ËÝdlXö ™û9¤ˆõ‰Àúíîò:‡e §ú«.»FšÆó4'u:·òÎb| ¸ õ·9nwySÁ;ã×&ß¼°Þí^×TüÅ0@€!Àkq‘oRÝBÁM¾œ’ú m˜vµv¿'+P .À®ÖfߢÒ7ù_ SŸð°?¶ÅˆZÐc±­¿êò©ÓÊ-¼3nOEÙŠ ’ŸHö®m€Ü1 K¿¬¯È+ó\û̪[•äßKô;À†Êûì¢ÒÍm3²Õ'‚€pX<·‚Ccަ|Šîg2e³‘ø<p×éo©¦‚'Î]ùÄÇ5Ÿrwü]åùÊ«Ò_>`€C€Ò€u/¶u†(¹´¨ÙéŽ.[\—<¦]ì×*éráä0€`tÈ€#Ä€`0 Àèo0F @€0 À€Ë pZù¾Õ«?ü8ðB›åKå–ľ=²<©ŸÖW¤—yn —IG&§¤¾¢w¿èJïqÜóKk‡‹ÙHÇü 1‰O0½¦yŽTïV—§òNó%Uñu-»ÿàúýᤂúlÂñgµõÐ+ÑÄGUyO×èneVÕWä–Åú—Tœn.óy;[éödÍÐ4ž:·Ð@€{÷ë5M±‡ïoÑÞ€\þå¤$CÒÊ­Þ¸j›þé[\œ¶ƒK1é“Sã$À}òó·k8`õ @û,P2@ó>3l1–yN¼@·”K€zÄ'b<ÂůÇ4ÂlÎ:•¡fç›í-O…ñãwílmýÊÎÑà7`róWöV‰údG'ÀúÅ#šø¨)Oµï`\¡¶Dj?÷ù“;©é¿Nþ¢WŒ‚óó%mQº; VÛ~th/kž;Ô4®} IDATÙ{Á9R;í ´_€Óf›R^9½mÛ&“©?gÀ¹³Ç™˜6™R^`{àÜ)lj[IOSÊÛqàïÎO4`ó’ µÌ1_öÎMêþô¬S[kܯnë'™ŽÈèË„yOõ™AôÌÁ–Ÿ‡Í>ñiãëÙqŠ/Àœüþ!½XŸˆñ?ª¼ä a<4\Å6_´ßS7FnƒýÕõÀnoi»wQ^¿WÞ± pP}¬ • *XÏxD5åÅD€g6ÚXc³Iß‚/m*Hn òS6›{ßeBñ¥. VÛ~"×¼½@l8§ª§É/Àû[´7 W½µ”ý“û‹q¦'Ù¦÷4U3qΔ¤”ÏØ7öqs£ÓÖ 5½¼´spÖbuácÚ¥´.Ϭfi«\.¾"þ«ÝÞ–Áˆ­¡Ò_Ïʼ$À¢újXçx¨ŽO ðòóŒ3wü*ùƒsÑ °Tþ‘EŠ[@sÖ¾½@,8÷ÓM¦±“w€#÷·ho ½Ü-ù»ý × 3VroßÀ´,~jVÒØC€½ó…• 0»9•Ÿþæa¹}æ o‰Ó‘Š($²>·*]QV€IVög6/Tu X±‹ãÓ^á3RÏ8ŒóT,i# 0“jËmq¾ÙÞàöV)ÀúÇCm|M€GeÉ}V©¦ää›ËŽF-À’ù™ò”>Ø«ƒkÝ^ †Ìô«¦•ÏN îk#ö·ho 쟈õ’éa¦žÛÓÄM½Êe:fA<}\Jç`#aY…¬•¦RÁÍaq:tqžeK¶*aÑU€ÕÅÇ+ÀÌ ìqßÜi8$>í>n’ú×Z…L(ÀWCŸÞ|ÑÞvÇ/À¡í­N€cMã{î3Xv6BjÓÍ]6*z–ÌÿNzü¦@kß^ ¦üÊÉQi¡¾¿E{Úðž7½Æ]xæžûèØž¦W¤ûÿQ~ì;çF/o7:Ëxy›ù÷òQ’ìOзÿÉÍ¿×éÜeÈ,nt6îß.-xãÿBô,xÔº?T”ynmdodr:7°–¥¬gõ °ï®Ò^ß–ÎOïÍ›µŸ¨_H:°†ÌÇÍó¬Ôºç漎ø™yÉ ÁæC:,ž×|«J È¢ü#¸‘•q,ì"Ôžkþ¸åìÆìSÌ/Jî¿]ññ 0{\ÙõåÒ|ùÞ ÅS‘î`•ñ ”?õ@º‘·ÃÌBbØ*YÁèñœÂ‹•…ëY1éKWëÅæ–¶URÌ¼ß º]ìºj¿Ñ/ À!õ àµ×¿r¬á,·×¥!ÌþÖ^úæXÃ2ùõˆ‡âøpBÇo§ÖÊ´w¨‹ÎO•œó»ƒŽ²“Ká¦VÕW°ËÔ|ÈôÞ“R7{G/ÀRù©åÄ¥‹ÐIÄ+8>Þ§'¼×–—ޝà󥪽@b p·\n!©;Àáú[´7 …ç¾—ÔwxòRnBtÊÉgL¢ì;ÔôäT½x c¼NŸ÷àÎÆHîÝW¬ê{g£×†ƒ8Ÿf¸×wA» ¼aÛj-¬/úoL]76yós«>@,ÌfÝÖ¸…d·]¸|€à|×/À¤W€i›ÿfØx1¥û÷ ^ê™"³OHéwò²þ¬/ü{f‚+—Ù:7ÅX”_J€KsKÓŽÖ¿C£Ù_žpÿíŽ_€¹áºD} ™Lš/Ÿ"öàFÿñ¨‹OhÄUB¨Òªê¼îËÌØ·{gŸL5ÿ/Ü®wævŸgß>3ÏsçaÙèð¡Ç˜X!Ìîê( ÞG¤kà>¯8llø— éKÕ ›;.!àÕÇÐ “^æYŸ§ÿz÷¡A®ñmªX˜Sàt^õ8€p°Q!ƒ_*D‚ì6Ó?¼áªöïž:ÞˆЉD׺{Þ:Ù¼ôÌžïäŒÀ†ö€·ýÅ &þàRzâT_GdÕሻgû»ÝÃUèûÌý§l À%1c¯l;//ìèS³çcÏ>¼=ØöùëÝ0Ò,@™±Ç>GvƒÅ%Ëq¯×Õ…|/<öè³ÿÞ…©¡Mü)0èçá±zÇç_ß ß}D"4W÷\:³þæø³¢¢‘–§[àÚH0 ?¶<(x:}ö°jh?*='~õ±‚—%šý-`óýi€C½ðþ]sá¶ÒŽßT®UÚ‡Žu¯9sñ_€õÙi`;Óº{qö~"¿åì(|Ë$²¯É¿vïg©éÀ–ž·ÒßRRRRRR)ð¬ê9¸êQåV” zþ7woOåÝ›à®oÊO –}yÒáÿðã²²àpî}°ªß™×ïÇ!UaØÐF€GËÎvƒâ}üwàAæQ/¼P#^Q™UÀˆ"Ø ØoîÚý½¥¯;Ô 0Àþ’ã¤ÊNÀæþ‚ìoìY ‡ºðò Q4Õp‚x`KöÀ…S$ýZD™mC&Þï«kuÀXª{ºÐ¹ e %kn™þSµàÊAßÐc¯7Ôè쇤u؇˜=›öáìÁ¶wBÚ¶¨8Jxœ;ž2ÌYë¥k Ÿù>…2;ÁX Öø½áòzñÞŠÇÐÒöe €Y³Ü9R4®#xP4ƒ"À€|ë1×§ÏVí,ð¼\Ü ”¶ðþf¿Ït®¶ÀÅ î èöy¦O-ïRJHÎÙ¯ð’i Àf{±öÉ)óž"Ž„÷¿À¾fÿ&q?KMg¶ö¼•þ–’’’’’J€gU¿€ù—ðòg¾…V!é žÈK§ €!¿ÂØ.`°ùåÛè ÿ|H¼ØØþ4Œ#ð}LÛs€ÆÞ`ð€/w%æD4h®w¬ªmßƘýQÏ5À²`­LóîŠÖïdÖü·™þq ò¼Š*óŠÌ&;C–[ljϰÛY—C¯Ô£µdé§Àlÿ)Ù‡ÖvÒ °Ìõ–ÓÅjˆØó±mÆ\{´ìõ@ŸRºCl?*¾_+Œ¦Dоðf8ŒWåšÖß }ûAëÛÿ¹"׫°à{ §;£ËpàmAà¢tŸuâ9Ð鲇Eû ÜNôjøÞ/¬¿Ü`ýþ´4‚g¸I›N&†"úͯýŸÒ¥L›d6·cíÅù#Ô‹¯üPpDØ|<ï_û÷³Ôôà„Ï[éo)))))©xós„ Yö¥ð©Œ.>xfÏ2ᜬIàÑ÷àÈÿ‹?î<‰÷#<È|LñXÖÛÃ0ÂaÔþ¤ð`ª™ xÚVG fG=?ÊÚâ¹¶S© Ì]¿m‘Àîö!a83ý#ÀÃCøUÏ-\ƱædÈ€(ó†®OÀÆö^²‚Œ `èßO˜ï?%ûP¦ƒqözƒ½8§4"ö|lÛ‡±×áÛwÍ51ð ,‡íÃÜ ø/"ãÈ0Ü Zݼ‹þØFXp=&n»U4‚ÙªÍÛE„³ §Íí Gd³‚½TY X¿?[l°ShlôTã…?6–£3^ñ×»¨’àÄíOzú¯¬°É^‚ß7ÍA‡~ìñÿÚ¾Ÿ¥¦3[|ÞJKIIIII¥À:ÿb®Üúýc𩜱{“þL>2G”•cR¯Õu©ªÍ€Öõ>Àâ5À†ö( 4ÒœyVyy¼P£¾ŒC3cÎWsW–¾ÞT5oõ邚™½æÌ•*¸²fúG€GÃEY”\Ë<€Ï:R ¸=—··ŽÀÆö<ÓÏ‘<Ò˜0ßJör? 2D× ¬D0‡{>¶íÃØƒkT¹Þm,“)¾uüTd‹L>ké®Lü `¼¿[+ AÃh`Áõ˜x¤H× q8mö°hŸ`-–IÕÅù›`óýi€Ë»´b[åQø±¤‹©¾ ༴XÛîüÛlªúdØRûù0—¥ZÀŒ½þ5ÂWÀ(’Î/ò¯ÝûYjš°•ç­ô·”””””TJlà_˜ëÛÏŸë=¿‹$Á"Oä3nšª2HºjâXœÚŸ–b[ ¼Š*çþŸSý.­ª‘ÍüÚ¼ï…"`„A’`©êp…¶ì0Ó?)óc8˜𬽻ÎÃáôлã0›r–à_PÀ€aÒº»\ÿ)ÙG¤ŠhJöø4b‡ˆ;ûö1ÛƒkÐHq×åL ÇQ¤ñØû Ã|Xá–›a“n$`€¹#T8üW;±þžlE€ÍIµUó™AßþÞVÑ Ûl¥ýüZOÿ±n¿Ë¾½Dþ@‹›CøwÂ/ð¯íûYê àDÏ[éo)))))©xùÆŒ† ¿žñì Ï~€ÃÛMäe3?˜rŽÀý%’;V­u^.$k\M/ÒCó.‚ýYç|Ûb0¨© °U\¡S­æ×º-®6¿¨gV˜ÌÄë]Þ¡¡­äXÅ`@²˜—ŠœLý§dÀ%+\—’9^Ѻ¦ëja]Xî|’°»æ• „„z=CQZ$Ç1ùI°&àxç] ÀqTÉë â\ðzý “`Ï#Æëa#Ài³‡å$Xô…ú}°þ€Õ¤X´˜{¡KEÕ¤3 VL©Ë±Ø”±—ÈÙQe☂‹D±Ç ükû~–z"xâç­ô·”””””Tò<ëÈ ÓL«Ís22N¬!(l| ¶áÊ੎kY  C`i1÷¾‡†¿÷ËRˆÃ@š‡fUݰÀð÷ü—oÃØK°·ý«p&z“â@޽ƒŠÛbhSÿàå‹§²yõ21«jïúíE€Áõb0-‰)n#)¸Æ)Cc×>4)O°À=o½ê927º8ª bÏ' û˜ìÁµìî®ËøÕ³Bt¼vbÆ2"ñ·º’à¶«t—e»"I²â÷`ý#dk‡õ2HZhºÍp:ìaÃ> ­7‚—÷7û}ÂðDöO”º"âäê Û­œÀÿ‰Úse¬ÛKä¬ÃUž× Éaìñ¼Åþ“zÂ8ÁóVú[JJJJJ*^¾Ñœkl/þs>ªþ˰Õ0¬'ê&uniþ&u_+ؿ։¦:¨D#M €áÁˆéܵhZ<èyÃÒh¶ðJºãÎÜß’¦™ÚdRm™£êrØ~pÃËØóíGSŽý;ÔÎ{_¯HÒ>€U˜ËËøžü3ƒÊÏP‹=»öáìÁ¶d½d±#«©ÑI£ræã5›éð¶ΞýÈk €Ek€at7‡’.Ì‚u þ Ë›¸˜0=Â:Àð»ZW "Ài°‡ûP@À‹䬿ÙïE€'´¿…:Ày×pcízlp"ÿÛ`;öù\£“:™;žõï8þ“zÂ8ÁóVú[JJJJJ*®Üš±}$áê9hJôòã3Ð ¬Ê?ÿññÁæ€ð”pîò²‡Öì ž»O6ìÙµ´¹r ©<úR5 ä;©¬Fa x™g}žËžlÞ·ç6àED(0 /ðû;Ž*íp$šSò†Ní9זּ”‹íŸZY0jG@ËŸU<—ß¼x^鹈꾲çc×>œ=˜öê0>î~õìÝlçÇqÛ¹],'þ‘D Œ, cð#¦mA›Ø hZ¢’D-‹øÙ(šB¥@ U’ò£H¤R‰¬­jGY+64"mÍ„4‰©êÚ±µ¨v÷œœÏgûÛ!ïW+;ç;ß÷rzüñóÜ=fõ xBë?Ô¢ì§yyyÏÆÀ¥>Ÿø·1«™÷«Ç^~åë›WµæÜS}åæœÏÃ]uáàèýاÐ}6cÆõ«Lxáo«7ªxê‘L}¦–å¾Ór²îÊ9»6Ñ’ñx·gÖ·þJ¾x^Ó þëÎSݯí¹ð—à î’ êÏŸîœy.x*Wcä©§œ(n%ÀžTÿaíø'zý¢M‘²ÉÔËìx(ùÝLýQËÏ—Çã${~5+?“ho9Þ¤€CÄ+¥1–vö÷÷H’¸5VÉLiíýýÛ ¥ÊîìÑÀ®Ùwƒ³ ÆB«]À‚˜ø_Ú¿·ÜM©X|Rƒ™ç]ª}8Ézk–vÚÓO,Õ.n Îì,~Gý+O.f ¹8ÑÚ] ë°JrÜîÒ›§NÚ*–Öoõ7Úsc`ñ126ª‡mÉJñâ³Ê«Ö0_¿˜éeïÈê Àr[•v«áÈåõržú^y««?Ï&_Ÿ¨z^?µ,ÐA¦¼ÿƒ«Ì–î]h$©G–{q°ê9F~ñ?{\] ô厑ûÏß û“µãŸàõ6wä ŸT½L‡—‚ójG/o8_ÌÆIöžp„àÅ–Û[Ž7i ÀÙþÞZñàPv[èâw9¿>Ÿ=*Xù .Õ< õH¦>JÀnW·¶,4ññxGnϬ8nýÏ”…æ½ Œùœû«sU——‡fñ’‹z¯©oaÊ¡àY7yCÌî1—I€Mtü½Þv8tÒX¨—º'úz™¢ÇC>º¾ÆóËìøa¼`kí-Ç€”°™êÁÎÎÓúG§˜/š†÷èòž(h²Én·þ—ò©2ø@ù­ÛåŸ|£^ïQ—ô„>ˆZ{ž O§˜`ý¾yv­w9¢žˆ‡#Ù[Âó²F­_nºóÍ„ôÕ'ÁòáÙdR¨¾1^ï±y<1êçþ°*Kw›`:Ùë3qß„,ËÚ€×&98ðÕæ ×Py ÊÿÚß›ìóÝùï½o“-ïOS“ìkòÉnW𡺢àh9\/mG3_dê£]£*Ë6OÜúÅýûµPÿä”·¿óÏ/Åí»Ì··*©õEÕ+²>SËB÷«3©oôñ5?Œ©œµ~wOY¼k€uyØR{Ëñ ý8 cn=ãð(Ͷÿvb†7ân´o‰õ™)êŠúì8»Ô©>jWm>¾õË.·ƒSÝŸPΑG»ÉÔ'^KXÂî´€-µ·oÀãß’a»óÔ@ƒ/ßœx^û(-·µæî§>6›kßösö¬ÈÏM_äýÓ7Žö'“õH²>i Àé®ÿ§;ßÒ ÎðöÝ/ ¦¤Im}®«ý±åúš/ŒµìLS6® @¯vœ­—Š¥??•j ïäžÁùUÆ['=œõ™­ÞƒËÐúÑ?B—@“ýÉ\=’­O:pÚëï~#_Ü©î™Ìo¿-_»þ¾4=ë;òÚž}g«r÷ËVëkv¼@€Lóª·_ëÊ@öµµámœE}l®o¯l¿4ðˈïšîäÝŸ8Þö'CõHº>©àLÔ¿èø—]v‹8µí+8«Cú”Ö§N.¦w]œNP_“ã…ÿãl›Äñ€ŒøäW®u-íh¿=ä£Ð`¼NüÅ—©ÇH듎àÌÔßU`õÎq©lßl+#_ŸëͪÜö¿ëOU õuy8§š¬~ÃÂñ€Œ¸ ˜$à) ²>ºþéÞ~ ëãd%ÓÞ@€LE @`À€ À€ Ó @€Lƒ 0 @€L{ Àƒ ÀÞ}ƒ§Ï‡Ÿ¨VWXkÛòí—ºK©5†üïNZI` à}Wj’±»O{Âß[«>^{¨N·TÉBiq¬¬È}ÕG±H.¯›)-üÂ9f{ Ò€½k¤œô÷÷HÒ‘€ýkHÊ=RNs8W¯–b4È®‚‚=ƒÃöG÷Rl’ ÀÕ½…’!Çno@:pï¡õ‡ÿç ´&·¾PPãꎜ¡àBþޏ rykîÏ(6Ö°ÿBTY€µ· µœíUþ |é¬6ÂÊÝ-b–nXV}aNaü\¼Šb`9{•¬»ûüGDNÔÞ€phÔÕVišÒ¯+”ºƒ_BWÕ~U3?§yk¼ùp>C H*ï|¿Â€¶· M¸f³ô¼øBZ»xÝÂÊB©Y‹Æ+ÓŽÄk]ÏÙ·Ï/R¯òp n…´¬EÌÈ ü&nƒ<éGYû©5©àÄí-HKöÖj³Õl oÉÌm J«¼­AÜk Ž @F°…ö¤#{ëk³þj¸zEN·¸¸!Û¿Æ¡Þ:nƒ\ô¸}㊠Àˆ°•ö¤#¯[È¿|lÁ´ µUVp}¡¸tüùÌôÜ‹¿/¥ÜŒ0[jo@ê8œµ\³ymŸÚ*KÛjæKâW ä’a»ÓÉ\ÀŒ,[lo@ªX—Õ›`-öw¨óù·JËZê’^wŒ¹f8Ÿ ÀH°Åö¤€K6‡ó¯˜éB¡z’vChCƒš­ëf$2€-µ· Å\½ZZ\ùxJ_àvºF™ €ñ€k6KÍ»‚êÄ< bHtõqGÎÅyÑ&ç“>Š €õ¬xö«8bÄ3€ àuò£l¥1–vö÷÷HRsÅÙýœ} ×`={Oèn{µ˜ Àƒ ÀÙþÞZñàPå!Y‡ó¿ÿ4Å€ ÀXÀfª;;OWXZTÛLy+sð?öîî§­óà¸í<²‘ØVË€–,˜ æFeˆ×ÕóÅ5 ¡\4/Š´T¨õT¨ª&BË(‹4) PD¤¾(½Ø*.¢¨U$rQ©“/’F²Ô"ûÚ¹öÄÎóœãˆmÌðrüýL9Æ 8‡IŒŸ IDATúÇ—ó¶'€O]ž]~£Ô9Àåc`Vx ÿþãÓ°²ØÀ{  €¥øÌíçö·o²²ØÀ50 àk¯¨ƒ¼îXÙÀX<€Oýë¿“õ¬k`,ÀîW_u²ž €°|˜€€fÞ@@3o € €  L@@3 € €ÈÀ¼üì›07·ù(÷@H.·38TŸmzíÝ—,€C×ùül`Õžqcöžþ@x5"—G—†TßWïUZ†`*ÀãçÅ ñgóç­7šŽßÚç9?|¬¾l6 À¢ìž™gëëËB4«ôíeá™"€0+€C«uâض˜n—~FÇs{Íwc~¶À²¼º4%ß„¯öˆ~ùÀpØÐ†qè¢ÃóXÈÓ›u8€ÊpxkYtGòØäyë]Ù/€µþ=ýs`ávù´ÿ©¿C_RCX{3+‹ØuN?,Kȃíœ @EXîñ}4âÈ `³çí~{€;?´7þègÛ,ÀÙ±>-Úä¯_¨?Lkƒ¹ûsxæI»ïHxŸ=ÀÞk§ïØt€êྠqCÍ_ýTàñÞî:1I`F»ÂêB àÚT2K¦â­j)šNÄ‚ÉÔ}!L8m ©d,‘¹ÒÕ×édL{z"^¯/ﳸíýšZÙr€êàðš£yLíœRD{ž\’Wá €0!€]àÅTÐ ;Ö¿’Ì,ùõw&¢iý¸úv2ï^±̉ê\ï—¿²áÕÀ¾;=yèsG—ºö¤¶¸1tQüyJ¿*e÷̳"÷fP™ÞgÞÊþMÜj¨+¬õo,Þ¨Mé=,÷k‘»}+ Æä.ßÅ„zCm:Õ´'€ƒ…¸ó[û{ìTGû†ëô»õM¨kAŸ;?=¦Måé±ÜmŒûÀ˜À¥æ­LX¹k7ªý߯‚7(uŽÊ.¾¢òX+_y8ôŽZ¶­žÝn§|uÊC þ¨ßï.ôÿöw§î:Ùp€j`ßpDï_#€C=_¨ÓÇ\®Ûsssë×#ÚHÞh'€0%€÷·+1y’o–¶¸ýWBâØ›^¾q#€õç{)}ðŸNÀvTE÷ýkðž–v9•e+C¡­^á™$€0'€÷›·;™s{•À޾ئïû d÷« ÖžèÔ˜ÎKæÒ÷öþõ•ËŸ°ÝÕÀ¹þÕ¸obôžœÊbz,ÿIbzŠÀ¼.>oåÐM¹Ï¯-Æšrÿl•œtæ°Í¹ /‚ÛÎ}PÉ=Àîïíoßd»ª €óúW^«?¼æ{*/‚•»LôˆCÞ˜ÀÄ.2oõÌÍpæ-Ê4¾`Ü)/€mÑÅOÔu WŒ!€°Ëun"׿ê6H[u³S¹ Bg ;< `L à"óVÏܬB{€÷°öžèÂv,{[¤Ò{€9Pº”µ ß}Gw¤û±q Ö®“îÀ˜ÀEæíþç¿ÀZGåm‘ä‚“‹``ßÜ¥®TöÖ ±1d¤pþ!Xá‹¢ù €©\lÞ®÷÷5¸ ô‹l“—€Î¦òbj×u¤÷à6H€jàÐ%Ñ?´{YŸ»}]b×Pï-í0¦p±y+NôÏîT5«n÷kËÝxWGk3GH÷¶9ic_°¼ð :¿µ¿×ʆX<€û&Ääg²„‡ëÔ!Ñ¡5ý¬ŽëKò„àðV¯ý†Û P™öiÂ2€µ·eÍÛÅD0˜Œß¬½•NÈ}¿+É`,Þ¨ÕúW.îݬõr"^[h‡@²)œ¼RÿõN*^à+ö~9Ðø+`ýÎ’7?’WŸ3ëëËB¨Kcut 1ó÷åˆöÞ'.€Š°ï¾#7€ûË™·Q¹¯W'‹7°’Ì,ùmö§3OÆŒË@ç+Àζ÷k>b0 ÚØ^¨…%udtè©ZžŸ¹`Ì àræm´6• Æ’‰¸êTÿb: &SzÞ¾xðB:¡%r,¹«ÚèŽü‰x Ð—ì½6pún€M°tz07·iœäs…çæ `Ê àS—g—ß(up~—3o£6uþ¯Á­-{ý~ã§ÛMYùPÔ¯??ŸÛë/~+ç‡öÆýl;@µð°Š(À{M™üÿÎ[¯×küËyèk8w<·7þÜÀÆÀ0೸¢Þyn¯ùncÌÏöÀ0–`Û?~øƒ:u™  € `,À6[èú/ŸÀ˜Àò Ì@€€000  ̼€€fÞ@@˜€€f @`éöÍ?˜›Û|”{ $—Û󞞨=òÕ>¹­«ûÍRË&rß¾qtÏL.YFÆ;½ål”J¯ÏãÝk3ǾæÏÕ5ÿÑüïç7]ž¿Yiý™ñýGÀg›^{÷ \Þ¼=¬²çõëŸmnŽÕ¿l¯'‡}½-þñ'cžðz|?_áñü…—6À <ÿ4¢þ›vÌÞ3¦ïjD.. eÆñÖ²Pn”Èþ@‰eÝxϨ1eÛ&Êyyý¢zÁ™ùié?%ž´¦¾1ýVáå#~ý}¸yrû¬®ôú<Þí¡ý*#,À{¾Ÿ]?ofü‚rÄëÏôï8Q<~^ æþàì+oÞVyóÚf;³¥ÿ>ð8`Òë‰IóÒ¼>æyböï%?ÿËðzlæÏ X(€}#Z]>[_×fn³*àðHÐXžI½€Ãk=bT>£g²ä@îëÝÝ7{—3,ZêÀj¿Yb”wONýûŸ_m6\>bçOðëc9\éõyÌÛÃÖÖå¹á·Pïù~vý¼™ñ ʯ?Ó¿àphµNäp¹óö°Ê›×¶3W{ÄìÒÿØ»ž—8–-<4Ífp;ÁÅÁ@$ÔéŸ#AÃlÌhfëF0õBw¡òÔEb¼ÙÜÀ[„»yàÍ*»üc¯ªúÇtsºª+ã¤ÕÔ¹`nkuuÓÕßW_uÕi>¸4 Pá×uÀ9óI¿ÇÊú¯÷³U†††®ôÏš5køÃá þÏã¹yþ‹–çž32ž]rÊÿˆŸýò†`çÙ«yz³ÜN®;Ç]\f&€Çï ýõðb!}Ö•’t)xl°™¾ìxæ|? Å‘?¯ÀеËÀÀŸ¾ Æ_?+€­ý>˜¯¯ª/tpV¾íÕ2òu¡ã¹ó·ÙŸ—œú³þàɵÀ9ó‰À9÷¯I+€­Y»øÖ ûOÐí² aöÏWÄ·jᲬZ#z¬NÊÑòÇ% »Ø þ]ó7Æ•–“ú ¸ãËs×ðØ `3|ÙñÌù~\»<|ãÀÖ~<øÙq÷þ·âÄ8+ßöjùzz9œxÞù-ݺ&|Ù?|•ðÑ à_/+€­Y»)8^ˆµéŽ3îxî;11͈¹~ÌWD;‹/2d¥ž‘'ˆáqB;„š àB³‘V¼rêH/‡á±Àfø²ã™÷ý°Ø `kÖ®¨Þýñp°+€³òm¯–•¯k^ôüµü>å´´Ø `+€­Y³–£n®ñ¤ƒŸ`+p§Q÷Üè×ZBΚQƒ à±oÎâI\zûiÁßÝàI®J+î~‘‹]ÁµÍÆA1À5Oü®+àZΓ{ÀzéHsÓð8Q?³%÷ X™ýtâ»i7p´ÍNø~øñ.Y^®]Oü‘Yظ ð‡À&×þ=2ÄS/õxñ)â{ù|â~èÆÒýÐúƒéFŽoÎý‹à7Ì—jl:Þ³fÍZÞxðÔ/ó¥ÏS3\õòÃó§K§ÎÖ‹Á£í…ú«Ã‡é„ÌxRÊ %üåÌy2 à—NyãËÐûm±Ç·Ãw" œ¹œv+§¬@$€›åõ"8Ó›]€ã/Ëà1¨?þêáó¯)Kœ&gÜÕCž³øÍÉ}¢<ªï¥ï.~ükèé‡À°üÔ„»ÇŠ¿~ÿ÷!¹¥J®" wKÎÖ fªúø€ãewïÇ×oŽh_¿ã‰Ïåk <¯¾‰4ߌ°ßxÛΓ¯âç=ãø0µè¹{ǽ¾êb¬Öî*ʱp¤Ç¸š÷QàŠ/%€³÷ܳøáû)ûC ƸýóÕ•‹ìºçGë5k7GgäÛ^-3_3==uìÑd‚€àùy–ñó¿>jð á Ä/½VŸý‘Ûo|}àŸ)žâx©ÇˆO‘`“ÏÏ"€e<–ï‡Öd ¾9÷/ªýˆ/•Øt¼gÍšµ¼ð`Ë Ro4×D.èÚÄÖsÆÊâÇâû3?øŠëÇTBnyò.Ox, àÑfƒk¡P×¼r»O07[£ dwù„ti…1ˆÈ]ùÚ(ïß-¨p6q ê¸ë-ŠÅ@$A± ²ß‹Ñ@¥H”‡õÕ‚,™Q{*• _rS‰ßxÂòU芴<’ >L@î®~G,Z1ÖÄʯœÛ…"c õýŽ':”gÍ3»Åo||59±º°_jŒìWs_LãSx<Nµv÷p=J X‚…|¢'zb øƒâK`“þ€Úc?ò~&ýý-Ù~2>9Çö?Ýó£õÇšµ$€³ñm¯–™¯‡×‚iËߎ(Èò1ÀóˆïJÅß©ð á£¿Þø¥Àêó‘? ý¦×‡þ™â)Š—z<ø 6pPýŠñ‡8Ü?Ð`|óî_Dû_*°éxÏš5k9 àÁÖB˜z2À³Kåwb;0Àîâ¶³÷ß÷³îÞóBΚQ#À+|cQ €ƒye1sÊß ¯1ÖhzËåõÂÈ&C”è;Àõõ* 8Šå=ªÉcP0'¸÷ *€9€eÁ“,ëãkÄîÈí‘öœ ëw|åç$ˆú …€Ì#©Dç·|Þ̾ÇÖÊwÃÑáéV&'8¿±akËä!€uñ)­D›Öh7p,¥ç‹ÏØC`|)lÒ`{ ãGÞϤ?´g6Ñþ…+?ØÿtÏÎkÖn”η½Zv¾žì `¾x”àùà9Éw*üBø¨Á/ˆ7~i°ò|äÀFׇþýžÊ{ZÕã ħ„†çköËx ï‡Î<”ã›wÿ‚í§øR%€MÇ{Ö¬YË[wâO/øÔÈYY`× ¿‹Ô ÒsD '[¬Ƨ&Üýj €» +¦Ê¦—ËÏøRÏ—ÎAux†é¦H»õw·FƒúBˆ´ó)Σ Ÿ,ÛAyX_³gÉ$ðèúìuE>¢>H@{Aûj>ŸÕÅ áDv³á®÷?ž”N”/­Ä;k˜gEFØmñÞ¡]¤°.>ÝöÑ.NkÚlˆ%JÐ_jcÒ`{ ãGÞOV¶?ïøÁþ§{~´÷Úµ%€õ|Û«ðuB—" øC:†xNò ¿>jð+3 `ÕùØ,€®ü3ÇSY ªÇˆOßãóuXjÂo?h¾Ä7ïþÛOñ¥J›Ž÷¬Y³–³îêß@7×x"ÆÅ[Bó¯!…hxHr¼»j6Ò? 0ãÒ±¸æu÷h°c&ÚKc:þ⽩Æ|5Ø<ôúí¶Lš 6P¿qšƒ =Ã|ždyX_+Þ3BàÑõ+Gìÿ¾ÿ‡ŽQ$ vT3/©‹D›ÜØïxR8Q>™d…ÿïäwJ¼÷¤°.>a²ðt7\1jôÆ—À&ý¶Ç0~äýÔ`eûóŽìºçG{?¬Y»YXË·½š_KK Ý` 4àéâ9Éw*üBø¨Á/̇¿4Xu>ö `£ë#ÿÌñT¨êñâSœô¯ÀR{~küIn'æÝ¿`û)¾T `Óñž5kÖòÀ ýË“`ÍÏ9œ€7y¬ÏûüíÔÙzNrö X‘æÓbó¡öÝÝW‘Ýçë¬ø›®ÉµúýšÇª “`+­`fÎX°Éõ‡ŸIÚʃú;S0¼~¡¸ó÷ £½q‡‹¨>@@ÑŒdH¸šø¥7Êëý'%€åá 0ïÃkÁd*ÀÚøt +EÀZ¾È¶~ãúãKIR“þÛc?ò~ª°²ý¹Çô?íó£½Ö¬Ý œo{5¾¦’`¥ð‡8†xNòR  ¾TãÄ ¿ÔXy>áÀf×Çþã)¨ÊñâSÌ÷èüŒßí!ð[íOúpk$À¹ö/Ø~’/•Øl¼gÍšµ|pm­«Åg.<±+HÝòÝñpº%>”„ ¹ã-J#ixLàÒ©³z¾–ѶåÌyûüó¿í–³^ˆ³@GxNå4ãK€æÁÖÝó‘"xäúºKrºí3Ž˜MJo·½pç'üQŸZkâ—>æªûOÆo€5XŸDÇŽO 8¾tªZ ¿ª…ü1Àêþ€Úc?ê~ö"€óŽèÚçÇ `k¿‘η½š _“ŸA"ù#8&ߘB¾S¿¡+«¶L"¼ø@â‰?ÙÎÏðØìúØ?cQj 7e0þo¡ZÜ៸K<¬O)€uñKü¢xj0Þ¬ÀªøðHpûxÖHRÀñ½lÂo`L°¦? ö˜ÆºŸ= à\ãúŸöù±ØÚï$€õ|Û«ñ5Ï|ë"þÈCþ žg´gòff[#€%¼ (¯u±ï4çgØlv}ìŸ1ž5u<ø”à{x¾©ø­ö'uB#Þœkÿ‚í'ù2)€a< Ç{Ö¬YËS3𕿙;žëž? ¥ð±ÐÇáB¬¹%ñ‰`HÈ<ƒK󷵸Òò˾”:F챓-‘\aë„—ì à³`À™Þt\£¬ÅY[ |¿”õ1Âû?{çóG“ÆñÙÞbò2ÍÌCƬ,¾ºdá•HˆÎFâ;,š‹&fØ›—€^Bt¢<Œ²šƒ¿ò^²àAr OÞ¼ì?µ]ýc¦ëGWu;3vk¾Ÿ^ÓÚUõÔÓÕÏSßþQíÈú¹מû æëT`û.iùg]dõ)°ÎäÉÙ칪°¸ ´ZkýS+;þé;2]WãEÍ›Mðý‰*€uã·'ªÿdÇ“ë3Þ4öÇî?nüiÏ`ð3 `m¾m—ˆùº¿¹öqµÌÜ‘só¿-[5™Ïwªø”/ƒâdñ‹ l¾Ó•ׯ­}Iÿ¢ÆSÆ_šù„O%ùž//­?Xð ñ[Ù1ßð«@Ç:¾xû¥ùÒ/€yFœïâÀVÂzÎnÚßœµ/E[:Ø|ï^›vÿÁ&ä(+`µ0½@J¼ï;_øMe>ôÚÁË–Kô'$M<9æ}ÆÁµeóm¨Gvùúµ‚­o™”¾9;xßföçêëw.ì­-eïZaó=•LZÜ?û®×»ö)ýNª¤>¥Öù/H€¬-ûÒ~×ý©ÀÏÜïúÙø{ ÀZÿXÙ‹>ÂnÍ?êðR Ö/ÈT´?Q°n<öDôßšäxrýaÆ›Îþ¸ýÇ?Ýù ~&¬Í·í1_?›u'ôkÌ+HÞùËosñ\šïTñKˆšøÅÇYübó'›ïtåùþè°®}Iÿ¢ÆSÆ_šù„OùþKʳÇC3ÿÄoedãËï߸Ço¿,_ú°0Š8ßÄ(€'Èâ;ª„«Eû‘èñ#Ãüæ áyº,å䥼Gêf_?rÛA8U-»«à®æÉ«Ï¾Øwºèg躓–j²#À …çgCľ—="¥÷K_†ÈÔy8ÁÆÕ¯lOˆyr¸´ôõx_¶HWŸlÍ“¯[Cd}ËûNiî£a.~²L¾z*éßòüâá'«CE©£$õ©°Æ¢1ß*lœí“ÒëôøS-€Sý2}øycûÂ>¾Z¬óOvÆ Ó§_öÉÃ3f§B¡õ)zºR£÷fߟÈX3{"úOv<¹þ0ãMgÜþãÇŸîüwZç-쟭Ÿ!òm»DÍשZÑþüÂÈŒaßzãÏ_~›‹çÒ|§Š_B|ÔÄ/ñ‰(1~1ñËwÚò\xû£¶/ë_ÄxÊøK3Ÿò)ßIyöxhæ²|¢êxA€õoÜã‹·_Ö?*€­ú ²ñu¾ˆW7¡?¢__ õãã}BÜ¥±jcļ<^-’õWbú¦+hì°ùrz'F̺ËÐ÷”æ ZÒšè;¿®•í‰ó;ÀÞ}Ù߇ìæ{íZÌׯléñ ÃmQºJ/WߤmY4Yô¾+G‡¡8 Ýßê[;·Ø–׺¤>¥ÖøO ÞÁvoswÝŸœÙu-šŸK‡À:ÿdÖ.쿟ì´“•Ä(Í¥NévË"¶?‘°n<øíyݲãÉ÷Ç?ÞtöÇí?aüiÎ`p—0]ö¹É”>ß¶KÔ|M'ôäMÚ˜öü?…ó™çÒ|§Š_B|ÔÄ/!>ñ–@l¾Ó–çúÃÛ¯,/Wbÿ¢ÆS¿¿4ó 1Ÿrý—•g‡fþ!‰ßªþˆ ‡õoÜã‹·_–/‡‡|í ó§hó=@’ð½‰ƒŠ½ÑpŸŒÎï.ÐSؼ\’<¼µvÁ¾/Âo `ú‡ó Ifû¸b”.¯ì dUwÕ wzW—ê ïcâã?*¥ËÞTXÁÆÕ¯l©ìÙªUàmãPþˆ*[_j‚Ú³×Û\øß­À²ØÝk{ëªR6ë—{òÖ%õ)°Úrâsg÷ý©ÀÖ˜ÙÚ/ZÕíÈvDÿXÕ¹ýéo^±åb®jø‹2ý‰.€uã¡i7!‹æ?ÉñäûãozÁ¯ÿ¬ñW¯”ýãO}þ@ƒŸK+óm»öDÎ×ÖÉxîÌÞÈÎ_ñ|fâ¹4ß©â—OÕñKŒB¼å`ó]ˆòltX?ù|È÷/r<õûK=ŸäS.ßËʳÇC3ÿã·ª?²1é÷oüã‹·_ì#€%ó§hó=@lXÆøÙÖÖ©ïñ«ü®µ½$}yiü”9¯3Üvi>Ñ’Nwßov…\¸}eöôx•Hö/d¢××;¦oטko5¦E'bñ§b¼%Ë­ÏZ\÷üàv à?ÿk}ÿoªw€yóm»8s­|Ý÷îôôð?òóW<Ÿ£æ»nů¸ãsWóÏ_¡ü«è¿´ü u~¸ùñµÚ¿G<>€X°|¡Ž¼ì·¹B†‘‰ü6ñ=Âgè?ý„€»%€-þôKœoÛµçzù:kC  ~‚n `è¶¾×+Òt”Üî^óö+ !þÄü„ø—È8¡ùùã   6©ëß—>o|øQÄ¢Iô&4ñ`| €èµ¢·¢Ì æ ô&4ñ`| €ègWöª‘ÿÃJIô&4ñ`| €è¹æÿü0@ü``€FBckæÝ IDAT €`$d€0 @#ßÀ0ò- @€@hœß>ÛÚ:ýÚúÅ8Ý~9!–~Sm'‚û£æëxZî/>uþ•ýpzzúu©çº5Õ¿¥c´¿óön¼³ò‰îS5ˆ…9´çïcêA%+Ÿ$wzüéüÑixÿꎗnü„³?³»Z1JõÅDFÙxÎGpkð_ýË?!€€ àí{k¬:¿˜8¨ÐíùÆs[ Ï’o– 9³bL (¶]je¯¶0³æŽÌO¿œîD Ìþ<à ¶í¥úÕaDAå €ìyÅéþôƒpí öôIPÙÀ}3­á!;Ü{…þ¬ý(Úå§wº!€“åoàkØßwd8ñçQ"ôn2ÎGpgpmˆL»œÃç[\[ç_³~u|¼OÈ ­€'^–‰õ‹}b.>”'ÇØù)¿›~2äk)„aöÀ–õææÀudv¥Læ[ÇW•ü=\û‚=ãæfÏ¥ë `Î^¾?“CļlX¡õç …‚êè“P>aþ¾í˜÷¯îxuBgVŒÒâÒ秉™ 9ÁÀãE ܨ>hÐ,›Ÿ°tÂýEµHN¬d<>c˜ß¨>ÛòX5¦'ä‘eó…ÿ9@~»%€Ks‡zl°Óx³PØxüÅRmÑ”Œ+&-ÿ>HYóåÌãk ²tß§ž¸ xšÈZÙ´i@IÎ^®?“cdpþ=ûν±6܆Ê'Ìß·]ËŽÏp—°u@^$èㄜàNà‰ó}Rª4pè| €ë à{yë?÷Ò3Í·Öuûºs¿ûXV¾y¡z–l*ÞIª–2÷gømŸ¾á{Ç0íæž ÞñT €ÌŠ¡{S+ÈâAñp9Š…lF–ÉüojÕŽNš¿!€£Û_+'ãÙçdà.àüYÿú²¥uÃæ[´!€›ùv™<´’p­HÞÛW¢­Ä\Úógêåé¥à„|´4ÇNôKs©;,€SÙò°7º€¤ÅÜ1œiG×Ê®C»#€“æoàÈöç>‰zÅtP׿?οoöjò-:!€'èçü†ó*pm¬T$‹þ…:Ææ÷‚W¥ ¹–(€mAÙ^­ÔÿëÎwÇ÷‹fýmÃ^eª5A®öKœ–¢ùwšîS&î„:³}\)7‹‡ÀL{)§2 ÷%ÑÐxªGÚ>__jb‹v¨bØw€È›_9ýÈ•W ²g£öÛa/ý±Û£«9þaý)~{ƒêóì—k›–æËóöòýé›!ƒÔkÍj¾tÙØi ¦ì®U£7ÈßÌà„ù[3þjÅÖu£áG++Ç·Äüþ̶Ì~Z]¹~Ù8ÐûW²-Ø«?!ìÏ­Ì¥&f<\#°ýÓ—WƇèã@û¶â°:ß xâÈ|eß¶¯;ÏšßgÉÃç-}|a.*>Ër,¹qyuJdªEw ûÎTgÏí}œ eßS`^ö)’¬o&û‘e¡½TöÀkЪ_Ø?P{òDhŸ©vèÜÛ¶í­–¹z™ò|û‚=² 7Û®=Ο1öjë“á°¿°¸¥ €5ùÀÿgïl^£XÖ0Þ·o39Ìq`p"YäèEÁ‹˜Ì Ì BI˜MÔd¸»l„d“ A'N\\Dœ$ óáÝ(dÜš•;ÅÍù§nUWwO×[Õ]Õ™Èôy뤫ꭷ_»ê—úºrP+ð¥Ïw'|êeÉ“…yïh VcÞ=¹“Ü!¯{Ò154 À¯æÝ/kl˜ù¬äðù¯sø¶¼µûÞŸÒðÇÚÒÎòYxª²[X}WÞ}ì.ݶਾé’#N žc.¼v³ðð‘+‡Ö/—Çì ˆ:{oÊ[…½ q¼“Øì”œßdüЩö¢4à–ýGë#þT=$Ûk,O'€åü²½J{F*Òì° T#³Üþ­—çb«0‹‡½E¯óåìÜþ þ¦ùûÌ߆øcÔNˆ3Ãù¿¡ôøÖùƒÆ£”VìW#|Â[Úág.oïß¶ð/MÓú ñc²|½û>üx$2~4í3ä7}²Æ6°¡¿… ‚ èøJ³"n=šZñÏ‚ÝXf½òÆr´S©ý]×!7+ò.=š&;úê„·Y;¹æ/šöó´jK|¥p. ެ̽ã?ñ”#•Â}>Ð_Ôk±XäK‹á R¼>¿ °€Mý-AA½ð•æ¬à߀ó…WþvàåhCRõÓ@r‡l}–¿ø¾X’éÌVÍ߬ɯI’È*{1'°²¤ÇÙÏÕÖHöÇêó窃¥ªÑßîà`V‘ÖOËË?è’r0ne.èì•´ù‹C™È€[òŸRñ§ÂÄ^cyZˆ–X~ŸÄ^Úž¦¼ßQĪk~«ÂלÏÔÃò›~¥š÷§ì!î'›âoðÈ»€ŸA4Å7õ}ž¦uï‡üBDºsÈ¿ôÐ1©>Sü˜ìw|”ìî¦ñù{@ÚgÊoó}ÈØÀ†þ‚ ‚ KàV=à߀j7ïð^9à© ou!¹C¶?K À_Šòg€aVcØèX ÛÇüœhOn8õf`©¾©zx*o6áÖOËëþ¼;Î??wA¿Ðå¿IíQê#þ¤¢öËÓˆÌËïÓ€‹nwd ¨ò¢§¬äg9LÁÄåTÝ{¨}  õ“¿ñ7"¸ŽýÀ_‚kŠoêú< ýÑØŸȤö(õRQ{åé$°ü> ,N`>Àϸÿ×™zø3¾XXûþ4×òô¿ñ'šQŸ)¾©?èó4­{?qÅ®«õ§ÇÉ~ÀJdœÀ¦þ‚ ‚ ž˜w·wbÇOºÕYqG«î‰›[•§äùêDõ¡<Ä–ÓÙØqJ¹6¿&æzl´ÉHIÀìßï€Ã£¼ <<ïŠrHý´<>޾Z$÷€¶|jÔØß+iêëúSÀRûíÊ#…ôÀÃóÑýÏ®Ýàj4/È,×½?=÷‡¿-âïR î@2Ç7ñ‡òS~«ïCöø€À‰lèo!‚ ꀯtÏz½¯'~ÿÌQx/èõW2ˆ™Ÿx5$0§‡z`ÇÉ‹ÿ½"F›ã뮀£S[“uu¥ðÐI Šë>ùu+ÁWz>€ùá=¼PëWÊcØå6|ä’°˜kÒØOëWìI[r™T_.¶˜ÓI±×¶§Ö7"&êÚën-œë‘žOà|x©Ÿ–Çh†/¹dãßMy d>X©ÚOëWìIpÓú¨?5< Ùk*O§Œ3Àr{ƃ™1ý 0Ïë^å_{­ LíuocÌѾ?=÷‰¿-âExçÜ›.YÅ7õ‡òd‰œÀ†þ‚ ‚ ÞxjÅ[}jÁ?Ã_Ý8rƒ…X|[pr‡<<ž‡Œ/IZÀ…OkBo4Æáõ¥ÕÃ7åòÂ늘:òª¯ÖÞzÓõ<ø_·p²üvk÷ýþƘ¶Æâ[X}S^8ûö›Z¼N>lzíZ_z> €ö¹7R?-opÞõæNßï{7ÏüCp~?çç_ÖÅ0ZµŸÖ¯ØÃÜår9§ý©ú“ŠÚk*OKf _]ÌþSLàÐ^µ=Suoã¯fÏ—E1³ÞÜáÛ­ççž?7S/¼zSÞ:Û÷ªsŽöýIùûÍßñçŸDý–ÈßÔÊó$m`>[89\[ûp¼Ûì_%Mê3ÅÑ~À4²äö™òÛ|²Ä–á—i’0û¯E AAÐåp$~ùÑ댽Íãã}/¼Š°±èÝLî[1!ñ-IkØ‹_¬ð¼ç?‡ÕÌŒú7’¬éOf9^%ºI5óåÓ\74ìLùå{±©JxohüùDæç`ùk½Iý´<Ê\'/Ä)°W'¼Âçíã}7˜ÉÒØOë§i6àŽÝ£LÚ¯OçO¢±7µ„ü«¦Iý†ø1ÛO˜ÄCöïiŸ)¿Å÷!K|@`‰ÿçÆ> æþ‚ ‚ ïÀ“¯gýÄ΀€Ûçò~8šÎ ÀÎàóío³µÂæç½ëâÿ7¾ÎV?–œ$vòÏgÝêçåRRƒgg«›þ*Lòò÷†¢‹gä瓘¡ŽÈ@ê§åµû ïÆ¯ÉQí§õ“tꀻ[Ÿ˜±RüI%Û›RÞ¥0mÏäWqùê1-`µ·÷+Ì=/bÀÄ2¿ÓÛOó÷›¿mâ¯ØtãÿŠRã[õ‡ò¼”6°h¾[}´s8dö¯&MêOû%&ñp‘ïÔ>c~ó÷!S|@`0Aý½¬Sãl{ûôŽÍ“W§Ò8?OÒQ®œ—Ó¹Kpª&”£-?åy£ý†|ùݧ§§Ëf²÷ö„÷–Rfµ÷»HjO)¿õ„»äúOáï¾ð¿É&þ‡¿¤½?í»Ìö]èýôÐÏÀÿüOgÿ_i{€íBAÐ÷à *–óÒ0’¦!’å‹ß*´×ðô+‘áoèGâã§`¦ü† ‚ €áâþRñå^Iü%¸%‚¿!Äô·ð/`‚ CßiÀÝt;_ÖÞní~­èa‚àoñ€!‚ Œù‡P«ž¨r‚ñ6ü !> 0A€À?°¶Î¾ÍÖª›;åà øB|@`‚ €h£? øB|@`‚ €!‚  AA`tÈA† ÿ³w>¯m#}7bpÁ"N Ték·JcË×LJœ×7I}õ%à\­³izH t›R·»—-ôPz)´=õÖ—÷ïzç‡dkF²Fªä4»û|zHÇ}5VÄ<óhf¾`2 À€`€¡· 0 ½`€`À0Àˆh€'Ÿxöìý§áK¬|Ç[á£ôû‚¼P*ß +_÷+QNš;~²b”÷:/F”€ ð/×ÿõŸ8¢Þ&åRè5øù,ö÷¾dÿÂñàÇ ðóÿ®„bì¿TÏWXyóhÍ©qü•Wøö(\s;Æòò%2À…>ÿFd÷vp¸Üš'«ÃÎõ6)‘ôº%„ÑÁ|Éï||ïyܲã­‰W¨›“ÒðBnï%ô§ãn_Ñ"«×ÆØÞ”ãÿÝþ¾€Ÿe€'7ˆ¹÷¿7oN ™å¸ºaúÁ)1;ÂçY™Šòêv¨ ×*²TË—ÇS©/w¶ÿ|úòýµÀ2pxéÜ"Uo“I¯Äß÷ÖKn€¥xc0Àr{/coßBÉ<˜c{SŽÿwûû~š>?b:;YݱÉ2û a‘·TŒ—ê†ù…Ës—+qõÛ< äÅ®¹î]碖/‘¦=¢Ô4µ ŒÝW?Ÿ’òÊÐGÕÛ¤DÓë§ÛŒÏ´-ü?¿ýˆ.?œü ÆÊñX –mò˜ÿ ƒ4ööe ¿gÇÙÞ”ãÃÒ1ÀW&é?.¼M.ÂôÇþ¶ó š‹r­íá¦0È£¹aÏIó§jùà–-?ëVËÀ¸ ðäÙÿ´a pT½MJD½Î³E;Ž…ðà„;‹ý&^{`€ÿÙí…X“z©K樷,Â/ñWŠóâÉt° ÇÉ€•_zsÊ7=¸$ÌæV&÷üÉÊ^çëù›[sä ˶+qQ­U³xâ˜4#â†áœ/¿cÌÍxÛ£”=ç£LÔÉaV4yö߸£¤c€¯Ty"Ï p$½MJ¼Œ•²áôêcîÌvöwNô gU˜nYCŸ½Ð¦R¯êm€þ‡Îûë3­¶Ì½ÇG¯§¢éy¸Aò×—â©ã ]}]ûôßßw|èø* žÄ½ßN½žÓõTãSzÏN­ò·££¾¯ï^õœ/û,épߘ}4Øte©i~o’¹5n‰ËßùïçîŒä°&XÎf€ERf'BÁ©¿ù%Ë&k©NçïQ[öz&È÷žðãW_à|>Ç ož"úw¥,Ïéà'>ó`€¤f€¯(8šÞ&%VÆJyÆUÖÇBÝà–$ßàÖd“£J$©’ p¯9H]å.¼’ã)úï§´Ç7^È5Üá…³¤;TÏãû °¤ÿr‡zvN%ž¢ÿÁF6Àòxáî<٧˧/?Š)Ùp=f€½ú¯ÄSǺúºöi¿¿z¼f|å»>*ÓÓÓ½¦d€Ó½žJüLq…Å{zü•}ß ïã=_:÷Ø£À K¼õ¨Öæ;Šó»¨* ®5 bûAzì rÃ’wѪe¯˜ZdyfhHý˜ìÙo¯ñEYÏ–œæ+ª˜ö$¦~ʱÚ-‹Où)ñTý÷Å˨3ÀÊx¡eoR™Îe³#Ú«èyP|ÕKú¯ÄSǺúºöi¿¿8>32¥¶2¾ò]ŸîI8ýëéO/Ÿ)Ïö¾ò—kÛ§œ/ûìõ¿+Î[…^ª›'|;0áê™Eoä5 ]w)ÍHìtà-›z]Ö¿Š~1OëSïÛ¦½lÍê4Í-* ÀýŠ{|ƒ«dÀàœºç×[VÎ'žiïßÎÀ³žÔêmRâf¬ôNŸ>²mÀä`ÇØˆã¨÷sã2Ñ7V¹ ê|ÆO§êF»X©Ï^»$ûWžk÷{ë«ñÔñ†®¾®}Úﯯ_ÄÓàÔ¯§7þ0\Ëb[xuíSÏ—Êý€Ъ¸z+ ð™=w‡©27ÀK}»|Ô4øÛƒ9NFZEd.1ÀûוcOTÚbe²ó(r±i>dKŸwŒÃ©«¥¹™ ì BoŒ4Àòk»,Ç@tÕxªþgô3Àrýüñ<ôíÕo‘õ\k€½õÕxêxCW_×>í÷WŽ×¯âé pê×Ó¿ þúÃA×>ßùÒ¸0À~ÿË’`-Wû{CµË“`5,ž‹)òòZ  ÇɨAû-×öŽ2ÀîLÇÛdï±Ë-öÊ¢C6ó»Ð.ß*Z§ÞûüYhl,ŸÏIó; À˜ °^o“3–<ãêÓGáIœe§ p¦a³©;÷6j^7¾ ˆ§1Àé_OOüÅaç‚c€ÃÛç¿~Éï °C±íÙoÄ^ƒôÙÚß$„^j¡Íô7â Á>AnY«’“UËR=X“?‹f€-¹÷jËÇÖA¶P'ë c+b€‹VÙ+PÃóif€•ÞÀàz›”8zíºÏ °ÏM°MÀDd"Šb€ÙÒ×)ö››Añ"˜PL4ñü‰åìL çñ °OoèêëÚé€çxÝø*¹N~=Ãg€5ØwýR¸0À®âzÖZ±·®”¿ˆ‰aÒ¡ö¸BNÜGÓîÿdA¾Z*oÉ]Py+f€ÝÔ$Š^hK/Q§Ÿ>8§ýíçú›·B 0{zÝÓûÑ@÷¯Ã¸h¬×Û¤ÄÒk!òàuÕ£TÌN— ÅXc€©ÜîÞvÞaã—|˜1•í[|®1‚žÇ2Àjú[Û²Û€ûÍñ=ÕaYXŸÜÐOšwìß^¦Ûlà$ôáGô1 $¶´4Kˆ~k¬ê&Bb[K³‰ÎËЈXžŽË—yä¶:ÖÖØ‰¶@´Å…{ªæ' J42M´,÷…€ã81˜ûú§¯²ö¼ýîîÍ·°\v[ÚNÀçœóm¦ì6_³i`.?¶5Ö°}0½ÜˆrT`b}0"²q­ñDBdŽGO®®°uÄ ­­t™Ï-ýu`.žm¾áPß©.ÆÏ-Ÿz~åR€“ÿ={±>Íñ=Ê”±Æ¢=²æÛËpû@€wàâÖù0+Ä3£§6ô¯±ÁÉ[£Öë+ø²€Ö/ÂÁÍ™ŠäìØ£L.…¥àf¼—%Ú.Iÿ!P¼¸‘‚±xòÙíÍ´½…C·̵ No3íOùÚc=lÍ£ÝI;UG†â[ k)˜%Pû»$s«–|+"S<'öø&'¶Â!ols¦Æu>7÷×Q€­ñ´ùF,2Í7ê;õÏiü‚åSί2àl¬O‹kÛÌÄl@[üúNó=Ûï/–ö2Ü~à”4¯ML¬šî =¹®•ï— Îßj^µ|+\Y„œc”¡¯žŠxslö¥T×sIuç|›©+iäë´ ª!?x²ªä°¿ÛAr=ɰNËg{¾“Ûõ¹Gì·þö« ³²øm¿ªøÍ òeà™`¢÷œ*ßfÚäë,³{W\¼7 (d.Ù­#ßB€@€0€€`0€0@€!À€#!`Œ„ @€0†`Œ| @€0ò- À€`  À˜R6¹61±zwûfZ>iú|}bân§cB®o OUÒ% oO®ÖÈáFï»Ø.ò ej$,cý× rt ‹±ûržôû+»àç_|î;îØm¾@Ú<ùE˜Íi¥±ýÖù0-GãF:Þ`Ÿ'>Þ1!+CÒ¹šeß" F‚±­™šM¨ÖW¯çó„ÚÒÿ}PO©ê I’›OÕ"ÛJÈð‰‚u€D*3X^m«#µ¹Yû+Yà¾:9™Ô_—ù6-®”›G—þËÍ2£#[-¢¥?I¯–x Ÿlwç­Š¬¬skÓ@éúÝÁ¿ÂÆ -Àe‰7¶µ´4KÈQ–r[/†ˆöÆ,ñöwÉ9¸¹4 Ñ™Ô ¹­Éê'|ÙH.‹I¿©¥Ó\¿ªª§êötBm Ÿ‡êÝ®ž½®ŸsV†¤`ÿÀÊøÍÕÊ‚}£÷jó­ªùÉD€wµ<öW²*ÀÍtÿK °ë|›3~íåÒeS€-ñö@€­ýMC Ÿüð  ^€çãô¥u(DÎÑ7ºä––Œ›;$ï}­ØÖH"z^H•½Ìçqòeß"‰Tªãk#RrÒ] 58&Xj_ÈÛC_·;xA.àñËUËéŸí[›%ÁpúœÆòØ_È’·Þcû_B€]çÛ4åî.åqyéöÇr:|äË4¿´¬ñT-ЕöRSÍÎ:ÏL€«nÿöàþ €Bàâ2íûº›%aíEO¼Õì´¬²ÛÒpo±!ÆWS%ä®P­åø_6PÓ=}CR°j°€ê¥G}!wç>ÌøwN"‘;CRºœÎòØ_ÈŽk –ŒÝ½(ì>ßfÂ·Û Et¥°6þn¶5ÙŸ§/ÀÊ“ƒ¿þéß!ÀPðœ<kÔjI¸/@¦ÙÓZbÎP>§~ªqû4-AB>Ü´LŠùr_B€µy.¹*cB ¶OB¬¬9Ú®.‘}¦X›©QÒàt–Çþ @¶8öèdYR€]çÛìpBYàì{òõïU ÏŽ·]¢¿8—Ý–ôKûš‚ÒOßN rtaÇ„ìîXÓ`ÏÙîÄEžÜ„Úß¼4¢×€ž?f$¥©‘p(¶_Ы+“KZ±ÿŽ©zÀ{?¾pÈí„Z™ oàâ ’¢©ýW›·¶î’Xÿ|äW²Þå£-ÂþŒNÌ‚›qãÆ>¶ú6øñ;š­=¾¾5ž½}[ü×'À©õ,¿Ç¶‘]î|!ÅxR3.³½-íS –gã§+$¯ØA€÷Ýþ @¾pq+»GB€ÝæÛl °2õyí3ô×hß§å>f»mÕ‹¾ù£_i?’üì…ïë×›ãyž{ýÀ¯=¾{ÿùðÚW-nØ^_™ªûütéµ~5]cï¯ÖÏo•¼qH™ºôy{Ñ›?Ë\€õïC0<3ܺ(íei—]‚ÔÜí}ÔMj;Û.y ¹›$þ$dwwÀò˜S>$˜Pûè=AˆiBÝ<¢ß˜è*Re|Õ£t%ª‹N¹ö™î©Ä|›N¨ô› =äâÙ±´/"ß=‚ ßÿT“^ŒöÈA}{—Íã·õ߆liOPŸ[ŸÖöEñs<^Á$föû™žù5„•ó|ü6–SnÏ;¬° Øay:þQ}…D® xßí¯äsì.ßfQ€>kg]»ñ€fã†OŠŽ|IwÓÚ髦—ÜMª¬\ÿvÉFV’Pþü‹¶xThG²géŰ©¾Ö‘Ç—Y墒ïô— °O¯£ °8þ®¿!Àð¬pÙ\ˆ¥ÛSšõ²â­Îr~@+÷ë5ªµyýÕrÕ ±Ü¦ˆ/›r‹ùè ‚S }C!YXQ;çuáðÔ7’hœÞxbö8û\òößQoŽHÑãF˜1­úøÍõx…» õt‡4üh@›F3ÿæãÙ°¶/¢{¡h¼w-À‡ïOu˜–ǧ6ˆŸ¯oë±eüÎlmÏ^Ÿ_ŸÖöñs<^&nÜîßU¹Ç¯ªêh·I€SoÏOC€gºÉØ£µ )¹ýíïý€¼`—ù6‹ìûsyéßþ½2õ°èÆ_hù…×Kè‘ßo¼tà5ά|Ztæ7úŸ‡ß>ð±l‹§ 탇Egn­~všª²6×§ÇsKþ8½<~rmä|¥¨¿T€·ßˆ¬.^†Ø¥—uô§µ]b× U× ÷jYy¸·µƒè7åh”Hâò${Bî X¯Òä˦nB€•9)Ú"˜PWkÝ “pTuÐ#gL}ü2ûÜ{&§ã ê¾ £îp8Èï÷ÓS*ý‰#ˆ‡I,t«’MúY|>©}E Q,¹C?a‚£÷G‘þhrÃŽœÉ£úÀùú6ù³ŽŸï¿ ®=[}~}ríÛêçz¼¢ù‡X€½B.€ñŸ5 °Ãöœ{ö¾/õWhãé&GOäÁþ @þ °Ë|›E~år);çùð;ELåWþTòÞ±úwŠÞc?gùU•žR¬&oʯ °^Ò÷à×^fÚK?8}æ{lzÖ·Ùú»­æëO¥çâñùÓñK\_!ô`g½«€ú3œö½ÃTQØh/Ýæÿ”%Ý^(_öÖßP€S;4Ï”GêÖ½­¶Ø»u¡³÷¯¼o¤¶9Xßø»—^S¿©Ü"3¶l¼öEB‡µjñÝÖ3éɱKÖþ‘-/™ó%þ« °ä¼ø·ÿù®•·!À°¬¸§]÷_]€'Û¾¿‰ôÊŠ×tëKg®¼n×!{ÞK2Ͼò$)^S˜m7v}ÕÔJ¬Öqî†^ãIX}*IùEÚyCv@=Ò¬[™Ñââñn@å/"mD?8­î{D—'ú–¹5’ó¹0Ÿž­¿«óõ§Òsñøü©ô¥®¯püáG€+ þ”»ÞÏ ƒç`¯WX˜#Û.[úí€rà*ým!xå¡ÐY­=íÔ§Z¥í7‘È‹{`2#«n}õ½Ï/üHOÚuÛôLÕbw¦Ó¯<yñé?̤\yÖ Ùü‚ À›çýWàl/Ù²æ-íi¬ñ'mé…Ù®Í[ì:ä,3·•u˜ë‹^7h1uÀ: îZjj2Ãd›7%ókÕW%.­U~Z¸úÏê7H$'.›¿Hˆ ÛW‚.åå­Úlz¶þ®ÌןJÏÅãóçfLKY_!~¸êO °Ûý¬ N^4ý°Ï×w¶oY³¿ôÛ+e-ÀúÛB ðÆãù5³Æ/³û"¡ƒùFì| Òö£÷Õ'–É?\¼ö…NêºU_-ì¶ 4>1ùr Rý¨ï×6åUØXë›\X’¢Q0,c¶ø/Ù„cWÇõ Ùˆ£c@~µßzNÒZ›M9¼ï€%©3ÀÊÈ5½pñ@>‰e@m¬ô3ÔìR¿¦6y蔪&áÁ;dËÙ¾FojcÆJPsñhØüÅÇâP=¬µ<–—§L!:g¿¾“«¿«óõ·¦çãñùSñK\_ñ›àG€+ þ”»ÝÏ%`£þæ\ïRn¯”·»ö·à»Cg~aðGÓi#‡= pâ“Ýd&Ö8‰§k¤©«WfÒ'~O¦¤Ÿ~&,¯z mqÇ A€`¹pSoÞÕc$GúóB›t'cW…rOrÏjk¦ì5Ý·˜»@KB6!MLš3JÖôMIÞ£ññÓI}¥¥ïuÒy‰Íß½|"Á±”G8#è$Àlýݘ«?%€\<,q}Åøàò¯?-À®÷³ðk“bƒd´ã§¥Ü^¨ ¶ío *ÀWÿœùkün]$Rý»Zo,uî lI©Ó–³ñ/ÀäCùÁ—[õ•¾\y} ðϬÔC€`ù pæuy&ßùÖ|LçÒõí8ú¬“M¡ErÖô1kžì5Ó»º p³> –õ5…ÔóÔziYШ ’cRZü¨Åñ(7 ò7ƒ)/´"Ky„kB]˜ª¿–ÂáZKýY¤ã¹`‰ë+~| p¹×Ÿ`—ûùÛ`2×Û¼ôÛ+!À¶ýmaxå!cg“ŸW?=¸ð•GŽ~xå—úH|8j`©¶6so«ú¨3W^Œ5À6&[]Yž‡NÊòL—®ÂÖo Éêà.A‡Lv¼ZA¦w9 Æ]f€“úñžäø}WYùœ¥32weÈò0&CCoì˜í€Ú.ž “ÿ†^­¬­A!Ê—G´+°ƒñõgÊoë€ùú[Óóñøüéø¥­¯Í{îS€Ë¼þ¼;ÞÏîßt£à¼¹Wþ¨¶ Ú+• Àvým8µ#ðz3õÎF7þøPä¶ñ=Vç!cRV(ÀäÈà“¶ÊÆÌèÒñìØ:L>/ȱH¯ ÊË 0 `Á0ÕÏ*׫¦Ôx‹lí”3{ƒû§ª²Ÿ°$×`õXÑI¶isK©9ýP3ã\ÑZ´nê yÂtX¹G•A„xdl.K$纲j6“ÃÕb§„IJåÙ®Ÿ «Ûè.D\ý™òsï'_*=ÏŸŽ_Šúf¦/:íìg¸,ëï ÀîgWUdݨBÁxp@>±¾Ú+ À¶ýmXÚxuG/9o©YP^^€ùøv{Ø –¯g{å¾aƒ.ué‘úHtæz0¦> }IÝ+s½-Ö'躥Ô^sbHë ™k®o±`ãàzEb3÷ÇÖÊ#cmÚˆ{s¯ò›©þþûÓd(ÿ ›9pkôʉê¢Ëý}S7êë»®%í2NLc}7ê»æžm ¨™x.£8ÒrÈ;dDÐvJ^åu±àõ´Ç.ߨ›ÓÇÂeÑ^(#®Qè ¬üë¡¿-¸Ç¿¨«~ôøÆèð鯉pFÿ¶[=Uˆüú¤Ö>£wëªÿ|àÖ•'ÿ{_àê?Ý×0¾Äܹ51­˜‰'˜Ñ¥ã‘¥<_é(lúíG/|ó¸_iî÷¶†ÎÖòñLÇ÷ )G\à[Æ'€J`õ+g¥3–‡¦§'d¹O{:6ôlútN¶ì”eí{’{¨ÓMÙkñ °š½¶yNV»YŸMêç|fæƒZù´¹¨Ôµ6í2HrR„[ÿ+½ùŽòø*a•h@ÍÄã Óùwªå‹õ­ï Ì•'zI?ØqÿaBæëO•Ÿ@Aý©ôlòsÊK»½P>\ó©¥ýírïo .ÀRë½ÝúÁ¾X¯>ϬM°6 \Ð¥¶á/5ÁM€#ôAÀÊß߉DÞ[-Ž'ZÓk×zÓ<Õ(DæmÙô­7ú_Õp\|ÓåõIüßßÉó/ÜÀ°|¸ªãZN½¸Øeí ‡‹úãªÁyz¤Í^sDÝXêx’K/\]AV_ê¡âs§sÁô©‹SšEǧ•Ë…‹Ô~/>>ö,×Z¸Úbß«)ÒCê ø5Ïæåùü3¤|SµvÇâðå›H*áõѺ«ñõ·–Ÿ/œ þTz&ž(&~±ë›Ú´Ýàɯ—cýØÓýì"°ñ¹B ðP®Mys>+Ÿö @ù °c[–¢—¾<_÷ñ_GHƒÜð޹Ķé‘—ôo\SOþyþã3ߨ-V À‰#uÕ¿dzÙÕ9Ï"ÀJ€ô©¹‰ÿž¯«>ó¨o…(¾H€©òú$ €å!À"2scc³Æš¤šqåjì~—xñRf–êe¢Ìõóm1Dþc<Ëþ­>ê´Öòœe²äv‰ã©<>ëïT~a~–ôa/õfâ¹¾©á©–‚ÝãeXÿbÇK4ôê° ü1HåÔ^X:úéÈÄËNk€©.Õ©¿­*F↖ù\^t<Šz¿;š(nü|F¸i`Ù °õQj˜Ë^ ’ Ü%E%›|µ¹T p`ý9ÞÈbû[¼£À`¼Å,5R{cohþ À¢8 À€"1>ïx70F‡ @e0¹ÿrÁNÈ… ÀèX²Œ¶.0F‡ @€0†`Œþ€`ô·€ @€0€`0 Àè0PQ\3>766{ß¾×Í¿orí7lI¯qºŽ4%WmûöóëÊ ±7 ™Sëõ¡‡áEƼó,·gµ$u¶ê¦*õû ìžý?{WóDzҦ˜À ŽÂ`›7<ü”àÇ  ˆJPŸcÌlÝtA¢>ã"À[\ua4¹›+ÜEÈæ‚×Uv¹äïzUÕÝ3]§ªûTÙš¨9¿€qìS]]u~ç×õ1þõ)ê#þéY—Ù²ó|ëë·OÔb„$€ÿÝó¯ÿ$ `-ù6+ˆ¯)Þ¸þ¿ ~'„/€ßþ3-}ž·ý¡ù·F•Í4 xühZ\_ÚK'äš7Ù›òùÚ`øËŸÿ»†å% 2×úf*ï°V~·ÏfîfjÂÊ‘Ïk%2!ü“1Ug‹ñ'3ËšýQ[ ŸÏà²ìEZt9±SmþãKƒÙmÚÎów¯©¼Í~mt¯‹áV`mù6+¬øºá±ìhAõÿãß%¿Áe™×èÖ÷¿Hº¢xåÂüü~£â9p[pûSVÜú~rrÀX_ÈÀcBm4ðøÓ㬸2—JÈõõ\Êçkƒ¡êåNp^Ry‰‚̵¾™Ê+•ËåŒ ¤%)îdŠV6<¶ýMÎ’¾î˜õw¹ à‰*+~Ý=ù>-ŸGÛZmï ôP^®0÷…ûª~ y·AkükÍ·YaÅ×ÀªÿÏ.€•üBÌs+îôþ}èŠâ• óÿåóûÍŠç­ÀG»«â¿qwNJ>;`£Ó-<ï³ü÷±Y¯øw!¿(.Æ×ÁÀÏ$€o„–„vÉ8_ù”©'l.p5Ôé/À×IçxÀTnÔŠOÄÿA¼;úDÀ£òG>!g}ûâRÛ…ªmøl’?ëáY/È–—+†|Ö—á`È¿¶|›v|ýjUà¬VÜ“¿üvŒŒg¼S.¿zð¥ÊŠÏI_ ÿ : à¬üN˜@ ü|§ÿ“ó¾ Rô¶ÿî±í¿žz‘æÞ }§;¾,Ú@Èó55&…ŸIÿ²8#*ÇÞèó|ŽðµÀ²)kÍ'.&|‚ßÀ÷Óq¶3^—ÓmžSɦò g>»žÛ+' ùךo³Â’¯Kâ_l|_DgtØš\Pªþ*.àÚ à_,ž#·F7OÞxÁú¥Þúö ½%€>Û“Ã\î'rçȨâ…àgÀ$€/ˆ†_\ìÈ‘¾¶¸  `/˜š±À¿0\ís¥wÛ÷óÉXî;÷äiVÉ8ž_e–õŶ–Ö¼h'âP•‰5™¦òx·Zø©ç®—,€þµåÛ¬p;±RÀqÿU8¬…û?ÛŽ£‘›&€Qhð·‰x²C«Ï`u½§r4ýñ4Ç~=Õü•?VƒÍ?*ö!ö?a÷oHÅ;xòj<’ß•ü3÷@pÀãÇ^ß³ÖÉ‘{ÁfVå­â7.Dæ’Ù嬱ðd ÁŒÆ€¿íL +taÞ½dPé]=ø¸ô$"BÅ^g°£(ž[ìÌž„%Tнt¸ÃDz w¤ÚƒûÑ˃õ—ë9õ¯„ù-‰¯0ÔW¹ÛÓ–‡à¬¯úü°öÕ䇷¯ØD–SðfPàÐ`ÐþZq½×úåayJ}¸P‹zYéГš-õù]ýx(¸ŽÑ±` ^ÿ\—» “p=1<Í´xà €Óã‘ìü®äÿ(kÿ WÜ~X+îÝÑðЈda~ùãÜ,{¼š@È¿zwn„ö¬ÕØ Ï`îȳÀª½p¸{³Þú·Uî†_wàö¨†õç‘ÛÞÿT~uæ‡búÜu IDAT§àzÅ•Óòû O\×Ó«×a{âö°Þ¾û lûÛçs/º?¥ýµþâzÿ®õÓˋׇ³x4Áɹ]Œ‰ôçwõãÁÕ>©l½'Y«3Àë§ÇÞãž4ϯÛW²ãñT8¯#*|ï•QÇ"%ᦠàˆ-ù6+\øZßšÿâîK(‘©ªÜÉ€ `Ô›(€¹À  šž à7þʆ÷ø³üyú; lÀqÿìu>J·Çø »-=hH7tþÇ⑬ü®æŸ¹‚£nŸ÷ãߺÐÀËòlÊîêú3þÇõg „<ï« ágUÐpǼ Í›þ-æT\‘ºQ[z(LóÍWÈòMk~ó<FÐ^#ïàTÙ(ÿR©$–Ì”Ìd¯Úwް­ÚÇ»r‘˜ñž€=¼XžV9LîÉéw¼†`Z}Áõx{–òö°ì/®÷ï\?½<¥>áB^a诤?¿1œÇ¨×Z­µ Ÿ‹GÀàØx~ OY¿Ö¹Ü|Ú…§á€2 àØZiᆠà&ÿZòmV8ð5ßšÿÊs‡×ßÕܧ¢ù0þ1ý-Ì/ÀcÑðWÓV—¦w:†ê};SõõÍß¡þØ €ÿì5>Bì1~Âî?LŸK4òÿ˜?4ø[ó÷<5Õg°*ôWç²Xþ;¸Ì3€þõÇ÷Ðòfñvÿ0½E¼£ºr<ÞPø‹G²ò;Ì?kÿ 7ܨ+úWÀ‡µþâ „ìr¢F!kSþÀ…nžìgé°ÑÖù …§Í-ܾÎëöõ橲VÚóR¶{Â( o·G°VÿVÄRësøª7éèU2hOÌ–‡`­¾°<›ö¤–Ö¾<[¥š<à_$OÔÙsCùjq½÷úò´úDÇOÔå.ìù]ñxp?,f€Åküެ<Þ$\HÀSòñ7_·ø×Šo³Âí,u|ø©2ë1;”=UcþÐào8rš =À‹rÉb^ `Ýß!å›ZÜ?B{ÈG˜=ÆOØýÃôñ¤+,ÞP0dåw-ÿŒýƒ@ œ0Ô¿PO,/}d K²&ÀÜÑDò\Rü”›Ä€¿y ‚XÔRzWe¬øuÿ7˜|8øÚëúiÀEC{îpCçÎ c·G°Vÿn?²©Ûoíi “ ©+’Ôë°=1{X"€µúÂòlÚ7FZ~Xû–Õ¥®Sõ(ó!ëhå«ýÅõþëËÓêvSdÊŒ±çwÅãÁyüØ`8,n¶ï¡Ý× ¥-f)K ckå„-€cükÅ·YáÂ×p|øIŠÒ×½‰Xÿˆ?4ø[í…n¹|èEßÒVE¢Îå@Û­÷èþ)ß$€ãþÚC>Âì1~ÂˆwÔG‰ÇŠÆâ‘¬ü®åŸ±‚‹Öô¯zÖäø±'¾aüE¡N'jÀ­6æ¯}‰§Èo~9Gä®ÜU“Wšüë|ÚË°f/ÝOvàš="€µú·+$¤Ûzá¾AЂ놭K©ö°Ýaû°ë31j,ZŒ9“§5ÆmpC°@ŠD[H홈†ó $UìÆXn‹ªm*Õ´–ê ¥–ÀQ[WQ›„ÒŠ¶$_*5•R)Š’Fê)¨ûr/;/·³ã½3¶óÿåYßì¼íìó<ÿÝ>ã¼ àº2¶9?“.º¨ÅbWíÕ¤-_àK·ÆŽÐ&*æ™ZoGЬé‘L ÑãÃQ3~Ã(ÀÉ¥ˆGL¥÷ €©ô\{Ü¿6’݉‡ÚÆi¹9öw¶?%é¹ò$Xô °ó4OýëþD`r‹`%óYw…°|çxQm¿zýLt=âŸL}ÌoY7%öt_¿y¸TÒ{Àü °™ùX•l.‚íèVÇâΉ)^,‚–²¦ý¯ë5ÍÞßÿ´v[¸­ƒ¤7’`w{èQ—ïÖìtÌù¬æí¤|™fÓ3þHš^âŸ< ¼ôù‛ÿÕùø1¬ì?ùüýð,€‹^,µ‘Àù­QBÆš®ùœÀ!›+h”Ñö«¾Ì¥Jkéýà/Jj:4—€ßЉ—{D«Øºð†Á·=³ùšVÂ4— ñœ^bÀ¹ô\{˜òØú·ÖÚ?–_е*£Ãæ;Ïç~_Ëí¯çšž-O"€E«@;ËóÔ¿î«DJ¦cY$*àØrÈü LT>5^TÛ¯\?º[´ö}{8±ÏœµáÜ*y¿Ö~PïÐj“.:õJH—§—¿ÂægÚÔÇÖß°öæ+°†?ê¬MîËwÒ~²:[&¨/û;ÓŸ²ô\yÌõ7Sž—þu:H6?©À,ßM½#œ 8âäø&Aÿ³ãEµýªõcËô‡1B»gH}$¥ðÜ®_®ïÕôu£ƒ—\°`ØÌždÀÎüŒÑòøFº~ÖåŽÏØ/ÓeÀgض€Å$€Yÿ+õ·~Q^‹ß˜¶_4ó]ÜÖhê3`Úþó÷¿«=Ø[*¿¤%×Á¢Ïç0gïdöX"€yBû#Yz™’µŸ;_ï°êRopû»Æ#~ý;Ÿ¿¯ñžðÎC¤­+‰õäÙ`‡)€ÍÍQë­º Zø*ÿ—©Oœ8Ü1Ï–C$<6ÒÞ>=:´ÑÜ÷Äδ_®"õW„yÇãm#ã%%MÃÑ„Ãk #=}3‰¸Yð‡Ç¦{«HwombʨðE-Ü6^Ò4um»<½Ì€³é¹ö°å1õ/Ø­‘ÆÉËCäÁ){Q¦‚cZxlÿDÏÙËCÂúr¿Óý)KÏ•—A—ØÍfú›)ÏKÿšÒÈNå'Ÿa¥7>ܵ-|f¼¤gjˆÄŽè‚þçÆ‹jûëÇ–'èk%ËT(¹~¹¾”ÓÛ}œ~ oDP'Ûm¬Í|CÆ•m­µL¡Cšk“8|5™^”Ÿ¹}ÈñsÖ•êê¤"éšÝš}ÙÙòÎù·æü¯ÄßúEÝ_èÏ ûj¶ÍsÈøsb)-Úþ î7{(°·T~©‚â3Dà/9Ìû/™=–`&=ëdéeþIÖ~î|y¼Ã4GoÐþ_øõï‚òýŒPÀ)¬Í.jé?Ô›‹QîÓHçèè!mM‡Üm¤veyôº™Dö+ŽUÖ ÷meâUo ¬=úñ¶Dm¿©{àõVþ¤{ÓÎhrŸ;óõR‘bÓË 8—?Û¶<¦þ!Ñ›Œõ'W%.N율%{’®/÷;ÝŸ²ô\y"ìÒßlyú×<©} ™üä8ÞâœÞµ-9XÓ¦LùÜxQm¿bý¸òýa¶?½×‘äúåø~PN°>'#©`*uˆõÜݨuœÀ5-„Ù8µÖ,•Ÿq9Îö^kHdäø aoÂv|Ç”ç˜hXû–€¦ü¯ÌßúEÝ_33ÀŒý2n^û>­ILÙÁýïfEöÖ™Ÿc_ØÄœ3u>/€ 9ÿ%³Çî˜IÏù#Iz™’µŸ?_ïP×ÒC¼Aùi<â׿óùû=œ¿c¸ÁúqPàóã3ôD {,¤`êDƒ{zpÄ2ru¯7ÄfG"™¶})è3BãÚpçì¹døïŠÆfû38(–fþçÊR ë'*ëÜ‘§—p6¦=|ytýñDú” õÝ3ë¨}>÷;ÝŸ²ô\y/[ž¼)ÉäçáÛæ¨c‰Q;à02¿”¡ÿùñ¢Ú~µúqåñýQج9ï ÉõËõý œÞ\q&->½ `sŽ6ƒ¦ò³zÌÞì±ÛªRÁÛÞœYÈ(€CÇ´&€ÁÀîþÖo}æä¯™çœöËñtrKI*§ýÜÿnöPhoù¥°ayì¤Îóy,ð_2{ì.€éô†yêl¨uø#Iz™’µ_p¾<Þ¡‡€4Þ`ü¿,ñëß¹üýŒð,€½P7ÕÛ;¹YôKQÝ$e‡CÌq&,Si½2i.¡+I[òÕÂü#ÉJxL¯¿jyžœïìϬ”ç÷úe³üò Zâõ2oýïs¼(÷Sž®ç ?ïaûÌ+Ð5².—ùEjº&'§So8—G#/¸=A»ÅÀ`±à¼ÝCë3ï¬àoý àÐýu¶ýÕ=Ï/k¤·Aš«¿Pm¿ì|}>úi¡];>‹\»PX*tÈà›x Ÿ,C?Ü;ô,ßÕ\~z¡ÑGîúy8*~ÿ€(€ –° ~ë}Ï0€¥ €ÑÅ`ðŒFº_Ç#^5]"äÁ~ôX<8¸@0€``°80çü–=ö÷f±3x@`À`É¢Ç/_kh܀޸¬Sºµsöµqô€À €Á’&„.@`À0  @ÀÀ0 ‡ @ÀpÈ0 @CÀ0ü- @ÃßÀ0ü ࢾ©ÞÞÉi_ùso¿YYùêÍçh?\¹qºøÙSnÊp,§îÄÓ÷WxK[úÇS?Œä¼M_zèÀÑÜäì·þ·ßy[éüÃßÿôî{ŸnÒça$Ü÷hÞ²§r{g>¿~õמýð1þöœúÃØ%I¢·>XyÔWýüÖ·|øÆéG–­xã/+øþŠûödÀ*þ= dE÷½Þ ­{$õ·Ö*Ò¸Ù‘†=fòá÷*mþâ»ÆO'Þ5½`5?7мgOýéøv—Dß)¶Â/?'>–—òò÷¬8Æ«4 îÝà1ç5ƒv><³n¡`•ú ã÷wª?úš‹^¼{‡ê÷Èÿ®W[ÜL)`ýüõê÷³¥)ã'®m÷#€Ùúª¶7«õÏAúìòëõy?È­ÎjûíÇSÅÎù¼U.¯tϲ`põ«\wåsójßB/YçCσöâ³'€ç2~éû-Ã\‡•ßÜýßüjþéòßE0˜`I à¢}$Üymttˆ¸n8Jœ˜=æò++o>_¶®ÿèYÀÁï®RÀ2μT¼ú‘‰ž®Ñ á±”-ÿ^üåØ~¯>p×£yÏxõæv`Å¿/[ X¥þÂøý_Õï»´Å·w¾[]ýÑÇŸ|bHÞ”¸]=ü/¬ø¶ÌÔW¹½Y­Ò/6œÕö)Úß8ê;ñ›ey¯dœ›øÏFS?xëö†ù²oü ¾rn¢¤D€ `þ÷^ `ú~ó/€©üæîÿæø?¿µ–œl7ÿÙq¬–Ô›ØqeˆÄÒ˜=8䛕o‚)r8â?b2üëê'ÕðÞK=}¿ î­Èèqó~fø¯P¦cúw…dΆ¢°§Ý³»\s0‰ž³FT¬NçN«Ô_DÉ@Ä» ¼U]ýñª@$HÍ DŒ¿-",i/ðÂÀ^í‡ O›ü¹xÅo­ÿŸK‘¡øË3)àÂ[+WÞ9ÿîõŠ[¬¼£Ï}ãøÊC á½óφ–ùßì ์_V¯þÛ„ž{6wÿ7O”_üÉò_ü–šÎ/2þ³ž;·XN·è¢Fº§÷iIÌ‹òùWíwŸõ,̘˜í÷o½ `3yèÿì}lÇÀw{Éöt¹3w縀?ê‹]ljì\,5çRÙ Ñ+ PµRëöEøk"K*Õ þ„ ‹–$²*à"’AU@¨5$B¶I@à"¢C "B|‰ÿÕˆ ÎÇîÝÎÇÞÜn8 ¬yÈ{·;ófçݼ÷Û73»!8pÚªÌ(9>UѦ–œÉ—ÿƒ€ƒÕ5Uîõ¶ç€°hYsG$P‚£™uÆD2œ|ŠXÐ^ ÀO8Û?”eQ]‡Ý-~ìß“w“ÀØ÷þYß8CK{Éié}qž€ÉcžCžª®ny\w]ªw—®LX…Åža•HžÐÇ)èŒz„òÕCz‚â&cî÷ÓÀv€ò»~vp24;ãoPªz2³qs€þ7¿ì`<É;ð³#¾/¾þg¡G°)R¤Ì]^þÆ3o£)XhaRÆÓÇ|N\ýW«uZ¬®îQ”øµê+šÒ{íªy‹h¸gôÈÕkã‹xÜð¥ZºÄ ÀžÝçúÞéúæoðAqr—• ä„+Í~#@4²G€¹`C°“•ç¸J¸Væ8S>â&w³ß³û“Qfm«{“IQæ{RjQ¤ãÚjz¶þËOFÞùä•ÃÙ–â胎Ae Ôß•{»ú‚zýýEâÙýó“o×'·Ñ.$´þ™5Znã6ÐúÒ?Iîgޝ)ñéÉdj¦›”àû+TAñá;å¹0$¸ém×…^‹ûÅ0:ÿ¶Õ®BƒÆ’áTrfã|Cíüàœ$håxNíU%ÑYãöûƒ|dûØþfÑ÷÷Ïûà6JŸÎçü>˜ë™úIûÙ ÀŒý›õqпæö™f'Ã㢅=XÛ«ƒñà Në/ŠYCjÿÍ»€ô:ÕÏæøÆ‘¢˜>(I™5¶ò¿ù`³}ø†ƒ.ü´·¸M­épg´d£b¶ÚÞ8ã-€ùþ¯¬ª¦eû‰¾SK•zðÿbf<Öÿƒe ;$K‘"EÊœà_üÑ ÈìÌA:à¬YÓ¦ªGÊ5(faîll­Çÿà#].ó¸æý„ò—Àõ'¢(v8ãW †Õõ!û¹hðÜ­™ ɰŠâÁt€¨ébAaáÁ#uL”¯;bßh;Úvˆ{£%—€ÍßS²`Ïþ„Ûå‚gÿ^ËÀ†û¦õQŠÁ± }‚VŽ Ôëè/ä-|®[Å÷Q À´~\ž1aÿ ôYj5ÂÇŒ¤ ÙÎƧÃáÓq;üÒyˆƒîSKÙûîFFà;m?6riø¯ÀøtÒPo%G_Àñ‡i ´—(0'&ûƒÑŸ±²¿9ç++®«øþ2˜ßm/”=0ö+²/€Yû'ô±Ù¿tû|[Ô}ö &BÖ²Û«íñƒ§õk~’Õ3ˆðʃÃNô³;¾q•hSŸÄlÜ\à¬þ7¯LÚGýŠÜ»=và A€æ'be¢f&CÒKû[äiécª| ŽF^ùè`; AD@¼ˆþžÖ¶š¸.á>²çÃUÿþR¸”€ëẢª¡õÇ®S{>ÜñÞ g4â>²ùð¡÷F£¨~‘þ"YSõÛñâ±m¿YÀÑCÙÞ¤zQDA×ǤßoÎêB ¼NÝŸz”„[%@yT8Nµ(Ó’áä}=#68¾¦Ùà·cê‘Þ¿®âe•äýâð™ójȤC'B@õ©½ûöM=ú®\aõEüïÔCŒîÙÛ ðoêþ’}ûæÀd°(mdsxù«êÀ…Zuìãÿ­¶È›í…²Æ~EöE0cÿ¤>6û—i_e…a«E1øTŒgÙìÕîøA„Óú‹ZùKã7É©[cSßßù•üo<Éd—¥Ìg÷¿ù`Ú>ÊÖºaæØ5|€€=ÃjCaÑè©4U5ÞZ°…ÿ¼­ýöuµt[ä›Ïà’㉰~ ÀR¤H‘"8 Ï{í'Ïm^õX8“ä “$úJaxðÏÏ`à|ø ®kÇ+p‹b*p˜• ÀÆ _íJ4t+ž6Á¡±°þ`…þ:C" Éò‘Ëïj?²†(@,«B~Õ ÀÄ÷)2dm«û·hÃ×—Z¹“õ`ùù` NHÒúÔµ»Î"2ÝDÀs´Êµ ¨¯é/’N}s¯&LÒ_D×ÇÆÁ߀ŸG;;§`ÿkð¿—á|¿Z®C&ΘößC œ;{ûÐ ªmÅ¡u¿ÀÃ)w…FF d]Áõ½(wÇ òãˆSãŠo馑ú"N%SèUÅã9´5²{6×KÐú3ö@õ7s~ãÚð'=Ž+äg€ {¡êgìWd_3×SúØí_º}Åmjéóúúpì!«½Ú?h€pZ¿ç¸Êß©jðÖýÐà­ÀÊøÍ»-ô³=¾qþ Á À³Àÿ›O¦íC„¹iQmLçQf<ŒðÆkÒî±3‚ š*oËÙòþ¯¬j ï€ÿhÅë'w†ŽVßO'¢ú%K‘"EŠ`kž÷Ú¯Íþ×Öh?ž H¡Àçáf€«GVêÇGª¯hÙâÿšà+/Ã\Ü„ü&ÊJÿ·bÝ@ðqgcÞ‹ ÖïÉ4öiæ­‘ÛB­‰3SåãgÞ0>4DzÃú{w¦'Pó'=SŒ]Æ^JŸFc± <¾ôÒ, ÀBýÅÀUr)‹þú-Ûq­_À½_ÙM¾ƒ.d‰ŸÕkjNÞC)ׇ¡½ní³ÀnBp ½Ð÷KaÖ»õ–t¹Ô¤&&0k€Ã©üñ¸¸½—q7ä Àtú3ícû›\³W!:“—&®§êgìWd_3×SúØí_¶} \øaàŒ0y¾È^Œ@8¬ßr#" Bp  ‚Cýìo˜é··ÌhHŽÌö¡?Ó>¶¿‰ó=[ÈL#/L\OÖÏگȾHf®§õ±Û¿L@#^¼gR‹>_d¯Æ^bæáo IDATÖ_Ya½ùÄÝ@‡sýloñ½{.È_/%?,ò¿y`Ž}6©nwÉE-'†׋x`XÍ)o»­ØÂÿ•UAW´p-¬¬l]M ; êwÀš!€¥H‘"eŽ0ím°ÂàLøªf`8º\À¾ãêë€ëÚ3k†€ßôµ¹¶75twFKW­héSW;ÅšífH¨òQ€hd? ¼È¨›`ó÷nJ¿Ü¡1Æ{ÏtØ…è¡{ˆ§h…Ži:ð,‹Û^bé/”‚ýªÛûŸÍ½Yø§ëÒ;F1õ‰Xÿ; €PÞ W˜+-‡o@‚7ÂÎè73ä¥1÷‹`£mË"\V&gV$óøŽÞñÚ«}îÌö‡Y¶}lí­"­Ž—&®'ëgíWd_$3×ÓúØì_6 ‡ð.Ô”§ÏÙ«ƒñƒÊÞ:¬ µ´Â,ñéís®Ÿ­ñxKßoWʬ°Ðÿæ€yö¹|­Ûüšêì¯AjŒ!kóéFG—Ço™] ³ø¿²*è}®ÅÏzkZØñHP¿5Þñx”lˆÜ"XŠ)Ræ43þ÷±°ih €G‰ºÅf ÀË¢®®¿r.k†™ß²u%«ë" ‚Ô7‰ñxF#(5f7@¤Ê×_b:™ÀÍghÈ¢X:Ü„¡ÿ".kpÁ þ¸šnïã‰7SB°Hÿˆkû['á#ÿ ‡­¸6–ÙÀ‹©/;ÇÑk¬ØÓHÃý°Â=aBÄïßͼ'¥ÔèûÅà]Æýã°æï4†6v^j Àÿgïlc£8Î8¾›£›Óê|]û‚°p [ XÆýp*¹©‚ I«"^BˆêSCR-U °( Ä6•$A´$ˆ‰ÔP‘R^„,^"ŽPeó¡•x‘Ñ’úµÔÙ—Û™}½;¿ñÿ+’±³7óÌγóÌïfg-|{죯B0ßNûùöñýíj¯Ã=ØýyWý¼ÿù—€¹Ï³öDì_?÷)y)|üJÃgÙëƒü5ÆøÁpÌúýXÕ4%¾}‘Æ7!§ºç vÇß2°È?u¦L.¯ ÀéíÍô´*óg¶<Áx+HƒäÿèkÏz!ÆËOÓWðãQ@ý‘Õ† zLxÒlü-Í ð¿y€]<õÅ@VVu®2W€­CÛ«–´ú÷JÓó‰Ý¯‘gçÉÔç¡$ÚD+oHà ‰»|v‚(z:‡ZVÈ×ãæÿdìQ[Ì-M¤=ú,ÀÁã­ÍÆ °¿ýa¤´}u*gî pS‹\s\ñº_þ,0Ä;æ×y¾ææô±÷+:SŒ%ÇR7Þ9€¹ö^/pÈ<À\¸˜k_ÛÏ ÀÖó!ðGýÂà,Z~eJ,¦ý+`c3`Ãjã¹`¯ò×ã»7^ý~¯@g_¤ñ‡ïLÛ§«¦c x¨8Dü-ë 0矫’É”Þ:€¥ù/'–dí¯cØò°gücXOýëç†v§|áS ëÃ`‚ 1À³_·‡ ¶Åp¯ Àææ_v8Ô`=ª-Ht×›{€] % «Ÿ>¢ÿ=}°êÍòͯ=ATŒœvt5Μ ²åsDÁ!X8Ä`:hz^6¾ÁfìQ[¬<£‹–‘¼¯z»; Ÿùºlîö·?”4mö¡œýݺ€ @ ïWqœßÕopžfDÒ´l6ŸÍ’=ÀYM >&Ø£ÿÙûå Àª+ZvIcT]4çí`r:VO¬þ`˜i_ë·Åõ÷|ýÅ®_¸8‹ö‡:›Í£E<~¥üÊ”ÙHjÅ^ä¯1ÆvLˆW¿ç!XEÛm|W*/Á!XCÀaâoy÷³þÙ°?uÎñÎOÓóÌÍD|y‚ñVÀ^ñ`A<õ¯ŸÁ‚ [ Ÿxfñ“%à^Ê $=’`˜ü89!€Ó¯U¥¬ST]Ùƒ~T¿ô*Yk©Ûÿ,ýiO+ŒÐh&O’Ö–“!OIug'b'ˆ‚4H8Ä)ÐÆ¡uµÉ§çðö¨ÍwÃf®”)ð´V¥4µÈIóhûig¼ý^´jŽ´@.V[­ Å÷+:w|ãXÙÕI×<4Š¥á »ØÇ~8é7Rãïù2e™c­-Ô 0­óºõn¶ËÞ¨+ÀÍelšP¸'V8íçÛÇ÷·«½ÒÜ\™ï˜{><üÙª_t tæ?ÏØ±ùöÑ— SêÍËØëƒü5ÆøÁÂk¬ú=Ó o_´ñM$ä2Ë{ 4ãŸó$—W/Z–ìš#O8¦©°[­Á”-O0ÞºÊ ˆ,‹â©oý`‚ °€g¿:î…—JÀi’÷7Kù·O¼Lȸל+žLòý%Í<ÀÖú£ºþ=ýï ôU@Òh¤³'ˆz¥yÐyçÌÓ©öP¯²åsĦOe×;›X”XÝuíþf ’s@4ÞH³f–7Ñ ¿Ô&I1ëZª66›y€ýí'Pv·²rÐÃæ‰5oE…ýr­Ë~¥Á:ÖÖã~ù°h0YÝ­¶ûÿºNˆä'É«ëââfúÙo‡„(Øû%9ö¡ªJˆà–+_³ØeoÄ=À$ ð‹ôSý6Gì—ý\ûøþv]O^­ùÖ¸€¶Ÿ}>˜Ï³õ‹òG`Q`§=Qû—kŸQGÂzH¹ëü5ÆøÁ­ÞÆ©üëáöB—}|+‡Š¿åÍìö¦yú4)ݺÚþÚÓý¼ñ¬3g×UÙúV‹)O0ÞºÊ ˆ â©oý¾\+: Aôxð¼7Æ­Zo‰Dâêú) ÀúÏ'¿¬K„o·®ézR¼˜®OíëÙüÔÖ›½÷àôöf€+T¥º¿ÞÛ¶~íU@õ !˜ú,†Q2AœœÉìø¸^¦i&¶È5ç>_{8±ä|¨CbØò¹ ¢nˆ+1j8ÞÉdÌlEû“]ïïmûêjᥲŽï*+¿™ì`Ù)Ð2öŒ_)§º·9,w¡ykõhŸ8ºmíUùÍßæD׋ì»x»²²Òã½ÓçVvÞøú­}m;å¬6—ýó^Nv¾ó¥© ‚ú<8kžã#`¼÷æìÜõàRqAã É}z©ñNÁ‹\+À~ö×Õ¦Ž¿µï¥³‡e#q{¿Œ¹UêÄâ};ÎþoMð °^ñ½…3væé+ÐY½ìÕ^š6øæ#ƒØíØ.û¹öñýíºžáM>ñåçøñá…‚çƒù‰,‹ÚyDùëýý5ÆøÁïß^?Ù7<9Ì^þñM4€­´¿ ƒÊÀ¾ñ·ìÌøG]­¹€ª{hçBÁófçNÚ[ƒH¼(„Hwy¢ñÖY^@ü£¬:Xåã©_ýŪâŸ?(èph‚ °× °Ôq³ïd_O¶˜`ýoO]î›z²¯·gŠ“=>ÆyŽêOµWm:³á˜I"ÔÍ&9n±'ˆ‰Mgn|`ίçjßÔý¾–;At—/H ÓÚœÚ À’4ëÈáܦ3?·³ Iù»••—ª&ç€Ô|ËÛ#Í'íY5Áj°´î'¤}YÉà ûÉvÖÛž€!MüâÃïÛ«RI¡˜µÏ€{½XÊ?¸Ôߨï˜QÀE7;Mv®ûÙ¯OØ6k~f'naïiãÙ í›6Þ ­+ùG—ÈÐ÷ >â´×€=ÚÛñHÿ쯳ŽS £ö‡Ó~®}"t]/)ÿtÜ¢g¶Ñ1Ïûy¾~·ÿ†à¤€yÿgì ê_R’³¹ö›l`ãýÁß_cŒ¢ƒñ"Ö_q ª&ÔðŒoY9e Ç€]þ1³°QgR½±+—yÞLìØâáoÂñÖQ^@üãW€%•‹§~õ+ A46¸H™SZÇŒJѲš")’¦¥Õ4ù½°Ñ&m}e›—„û=”VÊ{“Ê×h ­„êõpBl{¸K÷'M’$/¢}UºþPÅxa,¯Ø/Ži…:ô"ôÿò¦ÃhÎQbÙoì¹Tôò|íÉ„õ!EÕý—ØWÜM¶íÑ^U÷vrÈõÖØý‘‰ø dŠ}f"øCñ`àýàú×ݾºZûdi?”{üˆ^Åù\bèŽYŽÞþ†Õ5Ç$¨TœXºáÃZï<ÀÑãíøGZJ§c'aüͯ<þóé2û»ï@'† Ç ÈŠ2Úïâ¬+r͹Rî‰S†z7ÑÅÛa¼)j…ë;”(öò®–ÊÅpØR{mþšðx¤÷ÇЫ¸û1±¥p°tÉý¡ õ¯;Ôœ,Åë™e“r*“Wâ“#€!‚ Œ€,Ðü+r²kÏbm´ÚŸ¿[ùmßû‡x¢h`ÐD£~ã´ë±Ð#Ë?Õó¹g1Œþ­~õݵ‡åĈæ_iQ}òhI^ 3B¼… ‚ ðhѺC9º•ktZñ;{Ëñ˜³ÄpãÁiÕ;ß~ÔOÓ!‰þAþ¹coÛ»‡r©-Ê0ùCäúg’3ìJû>IéUq€nôLý¾ † ‚ ðcg¯½Ð>:8¨¼?ŠyËßþÀù~ëPé[+ì¿êþIþI’¡é æ8£nhý!rýêø»÷|6ÒûdâÇß·Ë`0A€G§:vŽ]ûGú °Ôñ*ÝoavÌôÇÈñOõ`.µñ_Ó†ËbÕ?”ç‹Ó¶LFo€!‚ Œ€<ú”ÎÀþá–óé±Ðžҿʰw+€!‚  AA`0A€!‚ Œx AA`‚ #ÞBA‚ ‚ÀA€!‚  AA`0A€!‚ Œ€ AA`‚ ÿ³wîOQ\Yïžé»æÑŒ3Å€ €ø(°DK¡D"*!ø$«‰Zš¬È®H¡ÀòPv%‹+V©QR>`×ðQµÄT±«)“*Ñlm²ÿ?m*?å‡Ý?aï½ý˜îÛ=ÓÝvóÐ9_žûès=œùÌ}@ÀA @ è]àŒ¡{gÎÜý*iÒMUîºÅK sßw³¿x?›ýßChê§³‘Ó¿“«Ìçƒa±@¸ÁŸ‹‚¿-­Æ»ªë§³ óÛ³`ÛÀ·ã·à Ò€æ/Ø–€éü:Sù¶dMùŠT×ï„=R`<ŠO¬ÕÐ÷›Èß4Oïuåµ–"®aUŠòYàÐaü9‹`²5+I•î®WÕs €ËÌå×þ‰\–VòÍËÄ¿ lÛ_ÿCÖ{—M¶‘ÏqÎíÉÜÆò|îËlxo‚@ ´`:¿ÎX¾õ÷l,HqMÔBl‘e{ÊÜVõ·×Ú¤¿’ƒ¤¯òžW×Ãi lÚøÏj<@ Ð[ À»¸@Ï«±±+·XʸU×bœÀås€‘Xþqµq•¦åÁCs €C÷#¹/¯OôýaL$Ⱥ¼§äþh4Š vÀ¶ý]“çdÙàÕ¯Yþöøž°öø‡º±ÞÇÅðæ@iÀt~¹|[[ÑYœêúxu‘ºžsÖô'0ê-Ð_`¡±€ò«Ö ·^³êÎŒç;öë@3À×FŽâÿÖsñ 릮på›L—Ï>/,žè»ôýë¿/ûíà¦åÞ«ýJé;ªaÉE3 À_*ƒÑ–ñoÐÆ˜­™=þîÉz¥=SùuÆòmY{`§/ŵ¨¾£XSñÀòÃçoÀå;¢¢|îp4Ú·êAøÔb%À®ª½ º½€}ýœ];~w²Räeôà­Õ]@1‡ÌØÆ»(–ìOÀÈŸàÓ'Î?gÏ=a âáØßÐa¶9É©Ô,ÞLÙM¶y™€]±ßÃ*`¬Ë¯Ó—oEèhç4;^Ñ×”Ô:î ´ÞŠ^êòì&|S²†H]‡G‚ÍQÍb‰´ëà{Rý!À¹°ÅÓùâ(žÁprιáj€íòv®÷Žx Ò[èxœk¸>ÝzÍ#r`÷Èщ¾Kg®¬0ðϧâ»GöÜ‹`4»¿I}fu׋Ìé»ô@bµ]{½´¿ýq=žšrÇÏÙàŒÆX ukr¦Ê]LÈBi›÷)ÎdÂňÀÁ³ä…’ýìÚ?£×;"ŸŒoûŠT{œ*t(*¸"ˆì&›f-ÜìNT?ùÏüùÿµøÇ–`þRpi›¸â6ó‹ QÇþ¢§Â™x±Ô,ÞÈ8ò=C€Ý±ÇŸM½3¥Óùuúò­¨ÆXCvªk­4#Ày±ÀNDºL™4VçË‹qK³º·K‚ à)©©Xµ 4â5 µ[bdЙê/s ×Ï&“²±MtWuÉ®©öu“žÖ,Ƈ,’QJlÈÜ„E`ËÙ‚G>‰I‚ÏÀ?`ì‰5ÜÂw"Àhv“ú(>»ÑË~ŸÏ8¾fñÚ3I·Ø¦ýuêÛñ¤Ê> d€37ió/Àt¹‹ y½¼T`Þû4K¼&XØÑlö,°0z‡Œ@ÒíÝTÞ+—Ôq¸¾ø”oÈ`pB†ÃÂþcª5Àfœ³](úóQ/Ó¥ãáÜßšÊÜC˜?s‰@›:ÞLM½ªwí¡ú@ ´`]þ¾|+& ;`HI0â%‘•<â7Ñ£q®ÿ¸§SÙâ!Ù9ÀlB7< „Ã( †õý!À‘€ª%Þ™¯ïO9xü–¡=urûFÅh5°å´ËS…%`ÀåÓð 培^Lf"0šÞߤ>9&ŠI~³xÐíiéüuêËñ¤Ë> d€[*´ù–`ºÜ½„ì_rG:'ÀÍä‹FaÉï L‡{ùÜ—?jxbë½g•|3N¹t{[Y-±tC‚/ÌL/Ú+סX:ŽýEÖàv¥mÞSaÆ<ÞˆÃó{-»cÏ’ÂÙ?Ÿ f€éü:}ùVL¶vÀÒŽ+kpå¡G² ˜ãʇò¦` H¤jm™ÒJ÷‡§7_¢îÅÅÉX‚]{lÒ`vm÷©Øj+äãƒE`C¾wª¹ÌÀ?±‰ÄÙ=62láþ&õñ åª ¢ìƃnO‹ö×±?.ÇSWîðù@ [¬Ë·'ÏÇŽ2â+ ‹S %ü²`¢0ZÈòÁo[‡÷h î+6j¯06k*]`µ|*ùýޏ´-±†»AÅù¿%âÞa%ÛÄ‘t³xûj§*»fO“vj5¥#[ç_w¸¶bquªkTœK¬¹TF”$`í1Heíäø"Jä#º?8;å;%àþhtÔÉ{OëÚ×UÈ•ñdZ=°5*kLE`Ëiמ¦dä ŒÊ6M‚…û›Ôg„óEè§o.n|³xÐíiÑþ:öÇåxêÊ> d€õùV À)ò±ó`qZàšzïÀe=ÑÅù¿¥mdûeÔ‘¼ÉVÊxëØ%{€A ° þu€m΋s='e­P˜Vš–j€™Æ8Ú“7q¦û#Çܘ°o&-1;ÝžÛCT§ŒMª€ q•Œ"°ÑKSü£qЗ¼¾îþ&õÆ×ýà Þ2¹5ûMâA·§u´¿Žýq9žúrgÏÙ༃º|«`ƒrø°´ä“–€k*埘œ©g•<{£ÞÚ¸ýLð'jE0âSõ™Àt<ûK€oIRSIÔ,Þú)Ð.ÙS A PÚ0_§5ß$5,KumD)ª`màEÀœ¸S‘Î\à †ñ7ËŒú³ÀÈfi ‘nŸ¶¼˜ü“Øvɇõ äKur6ð/50šÝߤ>1*4Ô“VâÚÝžîšö×±?.ÇS_îìù@ ë\µ_5/9•» À+Å…³M{y ã±EeIm8\5YIÎÍѰ¶}RóÿŸ½³mâ¼ãø}6–Û±­$Ô)%À@ Ð*Y„¨%„¤…@iYyë )iIV”Z 4R #PÂDRÚ‘"º¥ŒÀa˜oZªIhíα M¢R×?PUuš´{îÎgßÝ“œí;›à|?ù#¾—ç¹çyt¹Ÿ?yîyž¤p2Æ+:FUía¸¾Y‘Y©k&ê··v,“ʃI°#]€Õñ5¹ñVPˆ¨ñº”mJSŽ~Qí0Ó\µõ\d‘&£D§HkÜhó‹Q€ƒó,âyêô²°‘¾É±Ta OÄIcVïSê7¤0ê^_ç|)ÞÚCÙáežâjuzŠ+êk¸>&·§ö¸±ûb`ï˸¥£`êqø„ø&.3u%«à…+ÄYš$ìÒ2:jV§'ÏìQ}‹ÔÈš×·›ôöm2fŽ®Ÿº=«¯"¿ˆ“®]½öÖ.ƒdNy° `¤ °:¾&9ÞŠ3^å ±Mƒ6 ´Lè‡\yNÁ*Nžã7k•ëÕ!˜Ì«ôâ²°½¨ó£Ž"¿ð¤H|&Ïl§¤—…/‘4L8ZØ<¤åhÉkÛÒ¬ÅÂlÃÌ õZu¯¯s¾o¥—‰ãmuzÚú­ÉíI9nèþ€˜¸dW^5„S›gM·’ŽÆ‚¶™—ý‹*A‚ǥǟ[|ÙV%ÀšôŒÐÉ)ô]2N;½rÇ}ŸïÛLß!~ùïi³†8M˜K˜ž9OL·i»¶=©oTy‘7š÷V ³8ë´7Sø.ûÂD­)ØÒI0BX_“oãŸK»ð)>í#ðà;òîí²ly°< ”<ÿ)Ì;Mc?WžIÍ"8ŠüÂì σ¥J lq IDAT¶P=·!üJpô¤M‰¥¡zK±¨ìÁz®èŒÃëÖ*ê§#Œº××9ß½Iºˆs±°®m¼í¡I¯þß…º¾Fëcv{jº? f.[ÅÕn C"¯—§”0ÿ›vÜ䀜µ’͸¸³ÿ${´_X'vÂøŒsu=UWO²¹ŸçÙS+Ûî^©ëÙ¼¯oºÐC©`uzò<áÏ8¿´gßÕÿ®¥÷@Þ¼çóùÖ&^æ_û|»/Sø` Yºž0–+>þ?ôg\¼Ò½ySÃßIyÕí‘H}£ÊËël¸,cV8øzíÍxöVF/ lFyÄ\sqÀˆ`u|Mv¼ Γ;ò¨ÛT¢Øý†ÅÕµ”ÇÛ‰9ˆëÐ:ùÝÒTZžWmw ª÷úLA€]gêDº#ÒÄÉV¬Ê"8ŠüäeqBýâÔ65€E 0ìVöµ”eÓ$ï*Œ .¬¿CŒ˜S.,HSÔZ9Êü(‚£È/j]X©ÏY™~δp㑵nj¾°Gœœ©LÜhœR–-­K[Ò/}å¥K™Ÿ®0ê]_çüà<.ü…Kš<*¾öЦWb×Ö×X}ÌnOÊq#÷<4Ì?b/µ6_¬Í!Ó?m Vóåg³`oûw­þŒ&r E€ÕéÅ4Wo´67ÝÝŸGï¬ùç½ÁØP@/qÍ}Ÿïo£“%ÀŒs×¥VóåFR|2YrÙ’Û#‘úÊåõìõG,t]eÆ6ÝöæÓðçÌ‹°ñòÝܹè@€S(À¡~åxIõ6…3ΖC–¢ ;—òâZ${WA>6woCEÑFáš{X¢¯Up¢ó‹0¯~⩊ô¼°m¬(æO~?r…°1¥W+Š.ìϑڳ·ý|gg޶<ú¨s}ówËÖëÅ®öç%Ò”ô*´õ5T³Û“rÜÈý1 °A’Tn»ñ¨ž’ž¸2 5ÄÃìø—ï5ê!ÉE%&ÿ”^ºšoþ—Äñ«í,ÐÚöˆ§¾ú媽3 W³¹ç2M,ãþxºu9FÒ_€­?nl?ø:À)Œ·Î’c ORo› ö‘_dÙž!bvŸ³<&Tÿúškœ†ÚC/½ÝžÜú$·=Ðý€?(N67ïÑ'lŽM€ù˜HUI#ëîù‚±òÜfs¯6¯<¡¾JÇÙ)øë¤¿“!#Ža!ÀŒ' üO²zû¡'~M¯ë£>œšû¾ÿd>E5E€ GÁÛ¬ãh—ð†‘áò8[N²Vø/`¤°c˜pÚF}<7¿Žg/˜ôÙN†²/ÏÔù¬Ã!÷' F{8‰xj¾ñ}ÿÐØð`†)i¸Ñj‚;³æ7]ìz™0ŒöpJØñöÃSÖá"À&–'ÍFÂððÅÀ @€0F@ À` @€o0F¼ À- @€0 À€+ð¶ônÝzìÔ`1·´å89\…€ Ä%ÀŽ}dþଊ¿ˆ·CàÙë'͘ñZ*/:ýÅ›zp3@z pËÕ Çciì”÷-Ëçæ>)EçÜÀ’+UÈ€I¬Š¿IŽ·ëüÑýÑŸÄä ×gÒrHÔBùYA²²67}µ'/†´ÎÔ °û¥õ¶êjÛoß³ãv€4`ïbεñú¡Cí7N2à’ŽlNàÒy9Ü͹ö@€sX“oà…“2^1S€ù‰,dw>'|©àà-›í/_~¹Û6û¯0`H'îØYG~•¾QÌ• Æ{º+ªˆô÷ ïfywåsê À€9¬Š¿IŽ·¡S„ýç„݉pîg=" ¡J€­oöôlÞ×p’MÇIšT ðºêÙw|ÃýÛvä׸Ÿ }x”—ÿú} Òë=láO-¶„X>í°¥h?0E€Õñ7%ñvV¥¤ 1)¬F€Ÿ3vuó¥pò{?ɉ-ƒT pÖnÛço“¿Yoû(74¤‡)©ç~ ðÆ+OzµíXž§)3a<9!ýµíƒ0þÉJ–oÜÉÏŠ;Ü7ø .o¸’£Í/Nœ´ýþuòá‰õ®¶½‡ÒN€Kß±Œ[™™C#ÀåsåU`ÀTVÄßdÇ[…öU Ek;—I¶Þes?#æ··’üæµ4‚ðвR€§¾ÖVÆþ’ÿѱšüˆ‡„‰³füˆ’Ÿ,Àa}V¥'\0@ôÖqv&U€ƒüùV!…$Àsn³bþbA•ùÅIá¶ÏIýB9¾ÛöQ&îhH3ö(ŽšuR)À^oiKÇ4nnfLàèø›üx-Àîýç?íÙ5À¶ýA°Ëç¤çwÂxë›yúì<ÁÎø…ø1ëeëA»&?^€Ï °3ºŽõM'ª<¨®f×L¤”‡à-+Ù£]‡n³Öå9æÏ·žÝÓ½y_+ pÙ|¶íZgOUoÿwÏQêSwÛî!Ê·ªï¸%Fì]íª­¢ ðÿÙ»Óà(Ë€ãï›=òÎf³ ›á H„Ȇ@! B´–»£È02(`šB89 ËZaä°TD å¨ Pø‚uD¿0e¦£ú½_ú>ï±y¯…³Ønþ?gÌžOvwvæÉŸç=ʇh§%ÜàuZB€Ç`ëüûæ[kT‡µmž;VÉZà •}V=ÑÎ:ôr– =^ `ýZ–¶p;±¯–½âŽâá«Üã©\¹N ÑŲX vŽgpù¥hÿ_x½ž¼Üp½ï³.b­9–ÚÖŽTkwK‘-Q-€G‡ÿÒ¨ó,÷×:=6îŠ ßüÞ«¡ ÷Þå IÀãÇZû×+€Õy @°mþ}ó­%€»ÒºT ÈÊhÿYâ‚Ø ø×'åá‹ÌGÇ9°¾éóäîÚÂká(ŸØÚ9^×IŠofý‰úøÞçö8;ŸŸ—«ø´«kf¯5€GVÉ“õÇ\Pì[jÙÛ×ýþlvÞï`åz@×ÄŽ¿#ÿ8úRè+’-€_fë_×>À¥kŽ-+òÜ&‹€Ç `çü›èùÖÀ±}p¥²¨±ðȲ¢d_¶(€Å «¶ïn^cÃñÔî?Âø¥Ú qœ6ãÔù|5€'Ð6¯Ñ·ÔÀyá·%K‡ËƯ‰ûþZÀ‘-s?í%± IÀÎù×ë X¥;:y•ƒ€Ç `×ü›èùÖÀÕÍûäš7–ORbûÛJ: ÒÈ*Yœ¾(}±öÃ5^×Iæ‚lY±± ³ë(ÐYÚ¢s/Ïד—kÆî¹æH–ŽTFQ.<`?›’×û³ Z„B®›@÷ØüáG IÀîù×ë<À¥Ïûm‡©$€ø1ìÕ¿‰o-\Vá«ý­é“XÓ*3»´0€#[*ÄʪygçxÚiôÀA±ƒ¯±<ë|¾v$M^®9’%€C‹C_™ܱJ±íêëõþZaà¦ÀÝnF¿l> 4 I8gŽkþõ àÔñ)iÛ`Ú&€=æßDÏ·¶`Û9u…ôQE ïÊlYKc&ù¦õŠTFõ]ãµ,€¥²bcáÖùüæ.+6/Yxž±‹¯8µ±<¹åùïÏf¾U–ënµz?­™ûwÅ;ÙÌi ™¸dŠŸ3v½¸(í/0mÀ^óo¢ç[û>À»‚ö{ ÃWªä†›- àÐqyø*ãHîñbzhŽ’õdçóc,Özgy°yh­бðôE–ç{¼?«Gí¹8ø÷ƒÒ>–¹âxX€$ àŒ)î-­¼8cÊ‹{`Ú"€=çßDÏ·ö£@ëGQŽ3N™ÙEÍÉ##Ìf˜‹¸ž,¶M^ZcÖ­s<`ÛxæAªòr•î#<ž à‰ê3Ý|ÂØvz`•¬°TV¬ZŠûþZÀRÙÜ@à®x¡¯?â IÀ%S<޵áÀåCüîUa€Ç `Ïù7Ñó­ý<Àæñ§BËשÿ/œ'÷ZŠÔTûÌÝ€c»Õ†‚ž¬6æ‘oe³:ãy¬ÛÆ38$Žƒ•é~¾ÀjàÆŽîl= ´Ú»b°Á—ä:}-xä9ûº^ÊÚëu¾¿‡¹‚%uÜøó«âÂàMì ÉÀåsü —›ÄLœ¡*¬þ÷_=­ÍÏk¦¤ ÝÆih“vοOd¾µpúñhøü¿_¹|Ù·"PC×*²o7/5:òD4üÙÔC[/ÿg‘Àás§u‡cª(±*vŒç±l/vš¢³mÛeÇóórÃç^9ôó˧äæ3‰ÎRé5-‡Ïoøø”|äã¨^È93Ä-§®îwj‚{¼V«™;ú®ú›Þ¸з„$MLjM®2ö§4ßðLjjN?ÿ‹W÷î]ÖÉ?ôT€¶ `ÛüûDæ[kK…'+Œûž mϬ/˜ªeÙ`DmÇK²ö€=€û‰€Õûg+Ê‚>Þãy¬ÛÆ‹p°¦X?ô´óõh—e¹á7™R,€µÛ´ƒ]eæéWŽ<]P­o ̹§oD¹}¼VK¿üë»ï6Fß ò}€öÀ%KŠ´‹i=J˜~"ó­-€¥ÐÆ‹õѺÏWlë%Öac»ÌæôÕ÷ÊUu»|£¾®öŸâ^©Œ†ßöÏkØ6^,€Åq°²¯»ž¯y­¸öìˆæ_` `Is±¾îüÂÎb$ý7»í¾!žÒ{C÷ëyŒ®|- úð}ú’)€!C’­^½úèVïûùˆˆÀ¾é+¶çÆ?ðO=ßF¼Ë."E"±+YÁ=žÍÃÆ‹¸ J‘¶~-6?õÌ×_/?Ä—ÚS? 1qXl«´,€™ošÏ  ÀÿC+0 € €™o ` Ló5ÀÀ0000  Ì„  Ì„   ```˜ù˜ù:ýÌúh]í÷â\¯ë¸ÚóûóÍ€vÀÝ{v?€[3ÿò‰Ð&¼æòØ¿*eÅžØm/õóÿlíQ9ÃüÏ$qž¬ÖþNQ/P/øêj?}ÄC>/ªý93|•÷õ‡è8NV”ì+]øn@;`¯ù7ó-°øççüio^Ý»w»ßßÛ˜Kvwò;¸dŠ?™xð=Y9²oj¦d°fzÜו~<š}eÏ¡•Ë÷öô¼þP¡5ËNɾ³ør@»`ù7¡ó-°˜€woxEü(ýU‘>å–žÙî:ÖÀ¥ûS’9€Õþ ›k²jO>|håš%Êäx9;±¯o—Ë¡xוÀoœ¬P(`hçìœ<ß@k3°úŸñÎ"z3Ô¹wÅéRl<¾SZ§ä àÂyrö¹L©9€ûˆJ­Œ6|ç 5öm×)ýZ±oZg¾žÐŽØ9ÿ&z¾€¶må¼Äÿ”Ào~1(ÃÀåCÒ.IÚŽÔT„wõ’œœ×~Ùû ¡²mqØy½%|<š}¯'´çvÌ¿‰žo €­ÊçøßÒ6¿ÒvL²pÉó)OmMh‹ƒ(×^Y÷Aÿ¾ÿ· Á–7ÿ›üÛAiÝ7wšn¯5o[÷àN~Óí/Å?ä7eJÒú;ù·$ñC\8[YÐGr°TP-V€?0=Ríþœ¼ÇåÖƒX¹®o¼X_Wûý6ýw¥LË mœsªBž¾(ö˜ŽU­Žf@R°9ÿ&x¾€¶îæû»”ÞS›Ìa àŒE¯ï)Id¾$‹?ÎÆ;îòúû:tx®eã­Wxþƒ|Í—zÿФ_½ÝK àÎRæÎüü;jžîÿl©È¾)¹8tBÖz7ÀA#€#YY‹ÕàÍRéaì¸.ž¬ÐþÖiÐ7«œ~­Z;¬VskŠÃ,l™;ß@[öDÚQ”öNªgç SïId¿U%Ù·÷žw·Ø"€gµl<±¬öï­µšDëJ¢yóï¬í¼NܨÕ”¯V¬z¹©§¸ç–(^yrwÿ—½{mâ>8?±ǘD !@ O—6T…„4 Ê£I¡!c}°B_¦ÂÖnj) ¥e V¥XšÆºvÕ6©LÕªmì¯I“v¿;?îÎwö¹øŒ?HIξøŸõó—{Xï-J^×oò´Øº9pú·Ç{Þu(û‹¾Ð´gމëU\T¾Ä÷{@Á°jüµw¼€NŒ¿-Å+› ¸á÷Ñf;8Ðí:])¶DvÜÖeµ2äŸÕÖno÷Ça©|QÂW|ë„#Ri¶V_ ÷UŠŸ»D$GÄêá/Ä—‘\Ïxu\6ùð˜Ài¹Oõk€Óð¤ÕÊÅåKráJܹú¥Å3U°ÿUG†GÎ ¼V¿6·À‰ñ·QݿꞾйqÕ ;Øç’S3¸¯4ð¨Ù\½k¢*¯ NH^-,Åm¸OÞ\ºWÞZl-Ïå•‚¸KôñE©‚#W×ÅURqÏlÒì*vàØq¡öþ¶nŸ_õtÕ펣¢óßôˆÀ>Ñ¿ºÖß/ X5þÚ=Þ@Ç,šªé_u·TÔîdoß-Ÿ78öÇ®õ&3½¡"‹,nXY‘ÜÛ'XšŽ(¿Ü+¶–¢·_ú©_ì ,µò\Ñ©š.°oÏyÃ5ÀéxäƒÊŠ`©p•ó(I¬ìc|ÇÔÑÓ4Oܵ“ ;€Uã¯Ýã-°IÿªxÖ·|•›@G×û¾<ƵÞ¡ž½*~ÒÕbóf¯:€#á„~±ûoDŠà‹Ò•]»/÷ »ÒlЬlÝüÊÙ&×C•E™¯ž´:¾I´O^Å+plgß ú.Ðnz (¼V¿¶·À&ý« à§[í)ÏœU¢»åÓ %XúÚ‰é—-ÖüJ³õ÷öI©lÀRÔú/Ôv~‰žÙäêünLt ðâÁ%€ ÐX=þÚ>Þ@+G\¦ï_ó~ÓÆžYû)•Vµ2ƒëõ¬œòWB÷íŠô…Z¯†#½áËEF›@+…žñ#‡8=p<€cçEJ»X·'³Y³ 4xkÆ_ÛÇ[àØQ'odÀƒ£Ä&YÒ·Aö°”‘7¥?Õ‘•}€5¼ûrl“èè DÂb7`±&Xþnp,%€£gÁ„èÕ#ôYÜX»b×,€9vkÇ_ÛÇ[`ù„ÎÑ÷2 àø0mãy€•^ð kKèÚX¿8~èX __Žî,7< ’ø¡d…㦹"h•5ºíï:|}÷( Ìi °Øpüe`ì à†{Üsšó €k–š¡¿÷Ïÿ™ft¹ÁA°ôk€cçm¼k®r”hqðgùû#rñ:”âÕðØ1òÁ«†Í—»¼æÝ@·¥M ‹&­vm©V.ß°Ë<€Ë—Ä(À6 `ì àYËÜ+7Ä4G7š.X³–G~kÕñæ÷O:†_2;&Ôî ò›QÖnO¿XlìŽ\?t×Õck„ûBÊÅ}"[ƒûš”Ó%xTYÙÓ‡'8öˆ2¯Zá~þDÛI×â –‚UTòjiàÌ{Ç6mhûÓm]æ<³>p‰å 7€“Æ_€œpÜ×Iýû¬ê0srÀò‡c϶Ù<òvÏß³v{Ik€C­_ôEÏ‚Ô×¥„oôj9„‹j–ø¾9^ÀQÃßW{ÇNÏhtºÒÚQ ¥[x½I¹×/o6 àò¥º-¥Àšñ— `xë¹;§™ÏÓúï!CþXiíö’×{C»ú#ápäã®jùâ°rq¼”ƒO4ž©Ö°kë¹Ïö+7ãu¶{ë™§BA«\äï9Û]ºõÜÆýòpÉ«¥ÃY 0 @Nøeéqz½âE¦×¶þë¿–÷—õ&v * ÛÚÚ*} …ä •¾úƒÊ!eÆÐäemo–ÓžÞ·äB½k1{À@`׌1?ðuo €ó@ö–•eÓ»4vÁ­Ÿ8†Ÿ¯ÌÝ«Òþz“OÞ<0 Xìâã#€ €óÊŸ8|/½?”‹ûòïh;épÑ¿Pì#€ €óLûëõò®ZößSÍ|‡Ï—Óõ͘Vihû ;ì/Ÿßyæè‹,™@ÀÀŸ?Èß`Æ[``Æ[``@@@ÀÀÀ ÈÀÀ ÈÀÀ ÈÀÀÀ000ã-¹àÁ;^Ù¾ýÄ)d²À#F ›oÀ™Œ¿9|è“êøVú¹†}ÕµóZî%Íï÷•Š?[àÑ/ûø¬hã£6·>m¢fJí©¦Sð÷´5:k;Vî*ø7L‹Ó-)¾×®Û/ŸRü\?§É‡;Þôš7TŒžöÿ¼¤X{üwLµü&¼†åÃúû÷z,pªÞñ~£ütn<¿lÑD÷¼[¢£ó³òµ²qÍ0Y `Ýø›?ã­a`>Qª^Ÿ¸dwûí`Ë÷=o‡bS%¯õ,_îùés^kÓ¦·ú¸sNuŠiEÕ]ñ—׿ºê°ò¢¯»Ù°Ÿ^;q]Ã8§÷oCkÿõ*Üó*3`õü €mZ~²À×¾|dðþ%€ó,€/twüúÈ‘n÷èh7¬pÀØÀúñ—ÖeeYYÙ‚ Y àö+Ë=‰®ºâñüîÓOŸôÌþ½×Ê´©YSµŸõÓæ,¥ríÊUÇ7=}Â0{nhß Q+rzÿAéõÎòjnîzHÍ”âÍ!“çgô|Õó€¶iùÉZ_óò‘Éû—η>¸w•ø6ýñ:÷qÁô7¸kÕ¼îÄkŠSl @vX7þæÏxk˜í§„ ¥·äŽÙÀÂÈlpÕ³Oz^ø0ÀO,Ÿý—2é‰ýÍóÂO¬L›™üXñ}ÞÓ1Rð”-ª+¾W|O|B¾Ï›'š÷_3ÀØ[uÌ›êùé§õóÀùÀ™¼ à< àAƒ¥òzß{äèÿ½ñÔB§*€c?²0Y `ƒñ7OÆÛ9³)ðõ 6ŸØÅãù|ͯâ\þ¤ç??¼¼ÖóvuúiS-uã*SMk>»×i’`Qñ¶“ðÀàtϯ溼Àp¶–LÞ¿p¾pLÃcîqòÜñÞ-ƒ `l à¤ñ—¶3€K®|þ|™?À3—Ï~^¹Üãyágé§Í”O©½7Õ´&µü¡3å.Ÿ0Lçogôþ%€ó5€g-so–7–wL"€ÈEGÇ_»ÇÛ—Ç ÿŠò±íáÒs£ZòÆÝŸÅ?NîXv²)ÐÙ] ̆_œ¬ßznÞ‹æ¬þýªÛ}‹Cþé&Ñß÷÷œíÞÚù÷ýÑ­ý=OÖ:Ï}öTu‘áý'ÝŸ>€u/3›¤‡‘`é§Ÿ¯‘szí‡Ë=Ï¥6mZKGÀŠv×ðãÎqC­}€ö/toöŠÝ‹åOõ³¦nÓöí*jßÙkz !ÿŽ#u+å¿ç¢ŠD—×,S>‹«¯W> ûw´5Æ.H ˜`ÑÅßÙ{(dÒ ubç;Ç‹ÿ9¸Ë½Í«\ ÍýYDÝüúûOûx’Dûü4×àzÝt¤¼¾fâºQU¾X$}}GšcÆywIvo IDATïû¬pòüIÏ/éïWÒÓ&]ðÎÞCʧYŒïôíâÊë‘Ùë)¥¸Ã޽C-¾Þp"X[œf¯‡öñ§^þ ¯~þtˇžþ÷3xÿÞ¾úõLZ~£ž~Ø9úþÄ‘9`rÀ±ñ7gü?öÎ÷)ªëŒã»ËeÝYØu Ã’.ňdu@wÑ8Db2 ’µ:Qëˆd ”Ê" cc§vµFmbŒN·ÆÆLJl“6™:Žu¦™Lß™¼ñE¦Óÿ£çœ{ïÞ{žóì={a—ñÅù¾îž_Ϲçìœóá9?ü§°£­6èÓ´àãÁ@Qý€fðÂMýñgÖ>7À\z À!’…™ÞÓt¯ƒU}ú‹ˆ™=•7°½K”Ç0°o>²¸évñ÷Ò·¯Þ=E>”=gË1·°Ìâíé:L„D ÙÎÌÒÚ–‡/P®wû!¥ägx6©‡ö½†ïCŒÑ3Mi8½Z§¥'sô–é¸âÂÙ½I?Ô–Ö”Og´án¯ÐE3BˆBsì#Xøòä âÃòíìõìÃEv§CCÛØÁÀ¤À°ÖOxÇŒsó:‚þ Úœ33díá²=)P¦õ;OÊÛ[ì?ó`Gû%ý_°—ŸCÿ€_^Pž«ï/–?מBÿQZ^z!Qrf Àä›5þ¯[k+)))))å€3ão¡Ç[ÄüÅï†Ë·î5v2Àyܸ1pýÚÙÇ 0×Ü Ü8s¥kî©wú[€ùô€wLwÞº4ÈÒ‡>.+ýû7×fx§ÿF¼ºŠæ7¹öÎØÖç°ôHyCûÀñSÅ?P—ðwû~QÖ•=gÉ06¤qÇàÀg 7 Yä)¿t¢;‹C—­T‚¥¹ÔlÑ&Þ»69{_ëC×z††}%×ËÏŽùh8¡ÞZc^OX˜ÚÈ‡Ó ú™m¾Ñ‡‡È´üx˜ÆzZ^ùäÙ»3•xy ­“DèºèËÑ\¢oæõ;ú’UPž€a|X¾³=€dê×íõÇØ)œÌ‰èÀ˜oëö³Žz–îÉ€…ø°~Âû‹¯ÓúfèÄ'ÎÓ@ì à8w¥|rNo—íI€ò\6ñðÎ}ŸÞœÛ;ìl¿¤ÿ öòñåýCøîòå¹üþbýÏÞžbÿQZ ^Ú-èÊ Àì3] €•”””””ò ÀÖø[èññ-Û}’LÄ~áýÉ2aUÑA†HúÛøžÀvöظǻ} ÀVúWt>:xƒÌåž_¡ô`é»tâY±×»›Î°GÊtöãé±ò8ö-€«O?!¿ž?ýΛäÃwÞ–=gɰ;Êï„Ï x€K ´'Ó%”aÃÃÔš]F&ÑãÔÁÜA€ÀólùÓ÷ñËck¢%½´®Mºï—ðŽæ©(sAðŠuÚxâòsl7Í”OÝÎ}”;ʳñ} ™ØWZC`,_bG؃i«_ƒh/RÀNáñzÊGýt9p¼??ÌÕOx üØëÐû´7¸Ãg,½Í°«ölOšùu³N,koÏ‚÷;Û/ëÿÐ^$¾cÿ€BÒÝíæóí‰ö¥Bp*Éñ/ÀL]ç’ZÉ€`%%%%%¥ü0 8ÞŠàÀê ª¥Z½«è˜ßÌ áÍW2¨º3‚°•þ·:ë«O€­ü6ë®ßê]ªGߨØž-ÏÀÀ¾¼pø÷û¾\îÑ=¾’g<;W'` §@»™@7õ”¼F—>ûŽG*Ö­¬ îÐL?™Z]Pñ~se¦áê«1æí­I¶„†“\&–”n,–æ†$Xïp€UkÒÄ ˆtdH¿OÏ ì‘°–/±®~Ð^¤þ€Â ÀôÒSP7{~Ô¾Òyì˜$ôh¯•Þ`—íI€Òp´¶&µòöÎ;Ù/íÿÀ^,¾cÿ€_]$½[ÎRÖžhÿQ*0 ã¯À,’6zH°’’’’’R¾X 8Þ"{€ej3ØÍÍUÛ2Àlù pÐXXز7ó'¿šKOx·1¤kþÖèG^1±Dá +¼Ò¯N¢éÑòì ìå·)”0]â\}úÏŸy<Ö’g‡ç,À_ÈŸÍ€ ^ô†¶Õ6¤uɶuÀi›¬?íEE­=ˆ,#šY( 'ô^3% ÀáÙzóçðWA°ÅÄ ˆ2Çn…{$,ćåKìA„«´©?`§ðx=ýùéóÀ ~ð}ÄøVú´·&j¦À.Û³=iÚÚÈXMÖÞy`'û¥ýØ‹Åwì@Xz· êõ'Ú” Àâø‹ð¦¾õç+)))))å €Qþ-Ôx‹^ƒd%Pò©‰• 0+öfp¶‰°iÀöôÆ5HVy›;ŠŽþÚÛüN}ê ª]GÒ£åÙÚ7?q‡`=‰}ýò‡~ã@hÙ3–›Ë°äûŽSÏo¼ý 5Ñ^¿m'm à„6~ØC‡î;mǸ †[÷”f`?}÷<=rxÛqlÛ9i‘Ý1'Ú#`!>,ßÑ@¸ú ö"õìNOñ¥C³ÊƒúÁ÷¶R ýÚk«y®»ö´µú~¥ííÉÇ5HÙí—ö`/ß©@a郮¯A²0ßžhÿQ*(×ô ã/ÀKº}%Ÿ+VRRRRRÊ#ãoÇÛ ?ñ"L>e;y=t7nŽ`G^3ÈßáË49÷Ïfc§0H/óCûýÜ®r)ªýrdß÷oSKNë×9>c¹¥¢uNÏ„Jç ÀÚf£SþØ6­·ÛwÀ“ƒÒ]º¡w Õaár&‡~35vzÂ7›Y"JJ@8*§!Þ,Ƈå;Ø#`hï³À|ýàû°öàB lÂØv x*a®SpÓžP2_¬´½ç Àx{@ûsðsöæàvlß‹l¯|¤5íëí‘=À@êÑé©È/ºoÏ PRßï2y{»àLäx¿–€íöç°X°W²ر}ó°˜Ï´§àÅ।G½¾$Þ´M«}O°’’’’’R> 8ÞVïÒa2ýÀ@<Àž‘2ýìå¦ýÞ@n§@;{€c/Y·'qSzãÚ#¡|É)Ðо°gó¾ââ'tü®øêgòg1¯a_[¥Ã3&w\Ño;Þ6^_{~”Þ4zžÎs>ÚÎZoÊœÝÃp¸òMùñkb è×±Òëa0 BìqàlñaùYì슷÷Ù`«~Âû¨áï#OæíM%ôçØ%Ÿf'ç\Û3”ýÚTDÞÞhÿ ­½’µíûõwÑ4äCÛÚ/?š³‹ïÔ?ÍãhÇüA{*^lÞØ£µuåÀ©¤¶r­`%%%%%¥|0:þp¼ýñO‹Ž‘y`üAé(Øó§ ¬éýeG;r¼ØÑìY3Xtl¹ãÈI|טî…ö³û‘`zÙ=ÀÐ>'å‡`y*NÿåMVÞ)¶ÅWö,Èõ X®=À™}‡ôžWG,6ýMgÎ-ƽ¤ì‚Ò,÷Oïÿl¥É”÷µ¶ŽW~èˆajpzOnîHLùºo‘h#Ãø°|™=Ð~q *oï³ÀBýàû‹ ië?×_pØ/öho{’-™&ü8Ζ@»mO(ÓCÚhC.í Þ¿·¦ÉÈûeœš*9Œ.I‡öËú?´‰ïØ? ô®¾¿Bþ|{*^lní׎˜¢#ñR¢M€ÉoòØ86CÏ¢Ü4—ÔúΩk””””””òÀpü-ôxÛï­ztkìÓ¢±C°¨Ç¶è¯3cO½‡õC¦ÖÜ Ü8serî©w:s¯ °'ôqYéW¯L{JÃ[öNÿïá¡k“gï53Ò#åQ.'böCûÜŠfôöÎÿ'Š3ã;»ã2·Ëî’Ý "ˆI •¥¥êY[ ÊYÅ;¯ÈÙrž€ r9¸“€$B¢ê%$"=0!xÚ*&=ûk/Mló‡‹ÿ@“ûåžyf¿Í<3;»ËîíÖ¼_&ÂÃÌ<óyžgvžyí3óŒð„'Ç–ß¼óùÙöõéNg½´²>ß§ìšiÖ~Én&?ì¡ °}Ähm¼áÚ;³²‹æOß›B6’.«7¸ùšÑ‰ÎÞE¾VuN)ÛFëx©ðÙá3¾éƒxÞûd¤r¹ŠÈ÷ÿymãè —kïµ4Õ‚A°ŽßëÉç;z¤÷¨*…H%5ö¶·r}åþõâQƯ,Ÿ2ÞX 0)ŽYµ|*éÀõ™ò1õWt‚”`´©éÞØðöx`G|Íôì0¿y†N‚n{Ví°ÞpuÎ ó%ŸšCioEý{¿mo»Î— 6Íæó{TX¿Þñ¯ŒWeý Çû³}XŸ_&y{B€ã À>jG‰ÿÞ4úÿ°‡tÈoñ|ûÙa7Yú$  DK€eýo¬û[ó¦<úF¢k³@{Gp«öqâ G·yfY¶LI¡×û§6gØPîë~4˜¢þ$ñ ó-qyŽÿ5CE‹óeÝË5·ýy„3,^2>îsv/w\¡¾—13üSŸ3¹ëQcºêö*û“ 0_XX”lÈøäâï_þÝs=«—–Ѷ(¿’W¦£"ÀÛL«»¤} N¬×ýFià¯À÷da[ÏpZÉÃMï¶ô޹d…:o³Ø÷£”-W€ÀýÛz{VÜ¥Öö‡Rs²T¦þÄâKÎŒ¦³Ço›XÞÑïkÂlO"”íîRR™·Cnoùñ#&ÜÚl(÷ħõZ*Eü:Ç¿$Àñª¬ôø`?óŠíÃúüªäØžàÿ·ëáHªèíé™Þ®¾U h°©¾c8Wû=À Ößj=81v5s´Øã»õï’¾ÿþÜDÈéÀÌʧeÞ LG QÌq<ŽöovosŒãTî_/½ú3›ûJùh\ö˜ä/¯ÿk…ª?yýÛSOhO —öˆõç+ÞŸ_p¤¬ªÐ`‚ç¾_ýí›…Ý%7ye€7 V€#¡2í] n0ðF °@€ âsÖC`Œþð¦ pï¢þ«²€`ô·€Ÿ½Ô€£¿¼ùÜ™ƒz`Œþ€`ô·€ @€0€`tÈ€£C À` @€Ñß "ÀŽÞ™žžé{AºÝŠÞY²ÆP,:äM¹™¿Ò['cflee .'Ò}¤.uý)%¡Ûɶð¢ÏÙÝõz[Œâ•çûe§x–üYðÕÚfÅ1Ǩ6¡½Ë[_ô­Ï6Öíëz4~g@„¼>{Ý>mfûß ý-ÎG@4¸÷©ÛÈŒ£¾¿Îçk¶û;è…ažr>.œñþG/fkØXëÊ®`iCa™pð—‰ÜLÅ'©v ;ÿv¼ë>7ì&WECÎ?fê³èq]irWLô7Ú»x®™^·™ÞG‚É|¶ç@Ô˜í+‚õ·8«`ÇǼµ}elŒô¹==pùµ4>@€+®—òµâ¥ñ`Û]§Ðÿê¬û¿t@NyÉ¿ –6T}`º“À­DŠ˜ùlt¢óÜXvØñJ,7ù‹PójÀ.—‹TàgÚõiߟ˙–ÛϾ˜ªÿ}l*#îí]ô’&ÇëRh…ô¶Îs¦ûÛp2DY€ÙþW§¿Åùˆ‚_hT|QÊïñ~ÿ\âö °ãf©µ‘&ÊÏÅC€ ›MGéà¦m('B!²w6¥$r+È3]%Z"‰—ð‘ â¶‹SÂä–ó×#+¨oÊê‘ìÌÅ=±¸ K€ö&þ›0Æbi›«pÅ ˆº+û_ýþç#`µœä ÿhwû!•^ÇM#ßqïc£O€7ì°6îݤzl9ÍyoÜ5›#¢D§¥ZÿÞä`LêÅb)œ2ɉNþA8µë÷¶˜=úu‘í]|’ËüJfжçe¦#é8¢*ÀÊþ7¤þç#`•ì¥üs~3àö'Û~® ¿6%ÅO€3vë>Ïùs`Ë?¸ƒÙ‘nì`ñÖaNCsÃÎ?¨·T›.¤Ä®2âßÞö–ê䫊¯ěȿÁé]Vô¿¡õ·8ÑàÊÒ¤ôÁ$Ÿ{ÿS')îz¦:‹“í$·õ˜Bè.=îëîz}ÅŠÐ+ÓÍ>a4ýEÚß+ßÌI½'æ«Åy—¤gZ-—òçË’»–_ æDX&Ûµ$Àå3O¤/éËç˺—knkíßr—;µV+^6> 6lšJþFµ~dùKË¥ü¶¾g0Üñ~aÿĹþ=}.ÞÍíü«<„Àòeåm=Ö6×w—¡˜ü¿…IëÖGüÛ»à¸pŠQðÔ†U|I€`OGb‹ó ®¸nÜX矙Ã'À‡ÓjGc-ÀEKtÚßûj“ [.;Mdw{ÏUÓk‰~ñ6ÕŒÝ/Y<šÎ¤U…¨m‰Nu¼ó#iÏ›]Ÿ ‘˜¤³JE<“pÕKicª¥edJ0‰ù×2«íßîr&‚ê"¨ °">M.8. ÍÊê‡ÉŸTY.å'`³ª3õ™õkú# då…÷$)þ‘Nñÿ£éÊ´n}ØãÝÞöËÕ™ß2Õln)KÆ &ìíCìoq>¢ ÀŽ‘Rë`+ÀŽã™&Ç¥VwÉÙí1àó ÜäøØKNý±¦‚Ψƶ»Îäÿš¸´Äõÿ3!"þç"j(D_-q;ǧçÊú̬åy™0Ùx{bèy5¢yÂäàÎí3­ïFö®‰Ê}\ÿ³Ñ‰½3‹?‰jY8%æ·wá®ÿ[•ý¿Ýà ×tÁÌÆËħ)ÀÅ·håõÃä/.7Ý'šãBfê³°Y>:­(ÞÖæW/¹õ­e¯_8×g+Óºõ÷ö.¾Å©}ó‘z\!þöö¿!÷·8«`Çþ´À©7˜ü6t½”¾–ðÌhL8¹Ïô`­8€ÉÜ[K1oø2ý÷G6KOh¦6páouyG8•iébA&D]NqšaÛi޾gçNžéQ³°Œ Q‹“Š£ÁaÞ¾ÅíüÔã®.zCíAš}ÑqzÓšrÿŒ *âeâÓ`ÛI:†©¨&b°bu‹C­!Œ3õÙâ”Ý­,_V^ßÕ”;¹õórîän=¦LëÖGÜÛ;+ÏtU¥í-šXÀjØ×ÿ†Üßâ|¬V€ûݲ©'˜¯i5vL•/äóu±`Aºå•™\­|”/U ÂäùšÈØ-yŒ8béyXTwR$¹ ¦£kÿÇÞÙÆ6qÞÜ[ædÇqDUBÓ@Q;"к€ H× ­Ú¦KX-ÑJ2V¢Ž„—†t ¬T¢¨ñÒ!A* £­Z$^ÃÔe_ö±iZÅ—NÚú¡“¦v“vÏÝÙ¾7Ûg;¶ü~•Šû^žû_tÿü¼i*'öcNµ/ !rè›à£o*¼nߢ>fj"íJt¨Õ:­YÏïQ'>6Ñ5Õ×V¿”hS¾h9ÅÇtüEñÁcVv7XÙÊ@>œÌ¿îó-Ï#€<ø5ËÒ &ö•mëÐÖCr˜žc<Xk MöþÆyÿÊ;’ìÕV>¬i7”6êMwY ÑOèß ÄOèÉãéB¤ÈZõÍ?å,À†e|4+U;ÇU5l;&¶Õ/¥—«ìÓñ§ wyœØŸ“[¯¯f†¨úäÕ¢µ¦U°¹ì&%½ßÊ:…Yü¡zwñ@€ñàdþuŸoyä'ÀVÿµð¼ý‰‘f¢ ´.[ŸÔYF— ¸|S$¿(D¦¾39æS÷ž,…(þº1&º\7ƪõu! TN¼¼á½öl7|,mvQ²¸hxi=&¶Õ/¥k 9ÄÇx|Åïô³æÖlím½¾šÂ2'¯UÕØ\v’ÞïÀ¹] W9wÈG€ ù×}¾åy—Ûü×(À'Ê|Ïèë,{«¹€ë§ÿI{ÎUm’¬Æ&oïoãäÔ"?K½"Cƒ_\0C=oŽH²\}ód.×3iirCQ¨ñcÖóg`‡ú¥àEê©âc<¾òZoÍQ€-“`Y¯Oíæ¬TJZ`sÙMé›iX(©Ü3.µ™¥:IV}§m¤p>Bdð®ä,ËÑ‹Wb²<ô¸C5ÚTÙ?W´Î8‰°S‹g6ì\?GV޼Ãï“·éCjÅ‘M¬”ݰe${ pzv’Þoº@@ñØœ]ç[žGyðü—|Ǭ=­ <õ)_rE¤øoÓ…`Ñ6ùzš ý]á95­¶¾s¢€Ö"x.²[óYk¥¤ù„ÃóÏǜ֙p3Ø(Pc^MçÏ$Àçú9 °¦Šñ±p|*©VuàD°´‰ª2 pÃbÓdÝö1ÀéØMWœ(›¸WÿmZQV„,‘Ðýõ‡Ö ËõΫõÞš`¶j%Æ•üŽåt-‚žOêd!X=m‘^µKlô¬îŠå‰ÎÂ) 8L‚%Ö®þJ3Dq~‡uo³j¶ÖÏŸ¸—ߊiâféøŠŸ‹à̹*õ©mÁ“W¨>;ëjp Uhs<'­“Z>K~h[8ƒ»ˆGiïwÃÇ’±{òž8uÈ]€­ù×m¾åy‡/YïÛ°5ŽÈÄ ê”Ê¿ês}¯Ši)—\òÙ'¥—Y ?ï|þƈ¢qiùý?*+ÿ¨Ë笧6WýdÓ°Ö²Z~&¼|ýøö­Ýwây.ü¢ypß7:–…ªªªò›„HoŒ.–¼Ÿîé¾#½±IiѺÝw¯w nßw>–¡E:S[¥àå=Nõ?1"Ô¾~XÚ{|ûÅ;Òî?{\·'êk­Ÿ5>Н¹páÔѱˆ¬µ”:ÄÇx|Å_•Ú]‘†.E„`FۤꛧºG¼?»ešK9|„³%žÓêä§_‘‰ži»¾Œ»S#T IDATœ)%¿ß¡šœ‚nŒi-Çã%À¶üë2ßò<ÈO€¼zD›†2ÁRm’JßÄ/v?äÛÖ<¡ ë $i÷; ÝTû¿« Þ 9xmÞæå#ú²8OÃù&íË„¾R¬"–ã/»¢OÙºF:»D”÷†C©ØÏ9bÎd¥‚C l×—¹ØHÒßïPWSðP­ù––Ÿ‰TÓà…`7ù–ç@^ìŠ÷ûûOu8~2Nõôû=!C©ý»ÿ&tÎØÙ?::zýxâãÃEUþôå4èyõuqï†î•{èP?c|ô˜ª*ÀòæxzÎŽŽîiNyžP†rÚ?…RßoC›TýyØä¿·bê¢ÄY °·eÛºÔëgŸoyA€Õ¦+ (ÀV ª2®°öCFËÛ¤7îåïÆú™ãS zGJ~¿çÜ–ªoÚÌ{Î7É9ö‡€\€Å 9ÎoyK€'L(žЉ·k5¥ëê î¢~÷;îâ¹ì¶$k‹×Ý#’—ËY pª|ËóÎWˆ~:vdpûé¹1‡©¨ß}'ÀîâÙs>¦Ýóxf‰9³LíÁ¥`žGp¾„»:ãÔÖ̤~÷=®ã9¿{l@ðä½—$pPzæy€ç¿çÆØ@S_ïÝ?„©ß@ñ …ÜÌó#¢~–ø¿`@€`@€`@€`2 €“``&ß À0ù  \±ótÿ© ¥LÈíßýëÛÊÊo¿ž™ý®‘§ßåF[(¿86éëý»ÏIW{ß gµÏÙÑÑ=Í~'ôAD|o þ2Õ–Óêª_Hs Çý£§ŠÃ׿zuÙ_O&¢‡Çš¤ÔW £OydòŠÔœMþ%¢ã"À;o,/ó)”m;’xïµ'|ÏÎÖ^ÎÉ—ä­ŽÂ$äöï+5²WÙl§¡MÕNYL}L~ñ±,vŸs%¦ì,‡ä-ÀûþÑç†%µv/>’¾»G¤+g}=™ÿˆÎd¸J€°%ÿ%ß<è\ñŠoâ–Ñ£Gø|ê<ÿðC¾¢ pûß*+XðᇿúÏ{ÙïŒÛ(?©¾ydpûÖ£šb.[íÝQëzïÐÊ:É{mËæ±á–ŠVUU­š‘³Û÷W*'ï¾»yùSR°²ù/Ò•³¼¬š!º°ªÊÏßO€­ù(†Þ#²lÅÂMOú–Š7^<à›·»âûÏÿÇÞ¹ÆFq]x†µÖ£awÙ‡Ìî ñ”y›°6¬ q  ¤4`°Qì 1X¡PäTšŽ%‚‘-’‡VÆŽbÙ."P„¨0a%åþE„£(,ªD+‚íÚ´©z_3;;ذëJ=ß0ãyܹäÃ7çÞsŸX¶)9ù›ÀÐC÷{-d€Í¯¤O\yßïÙ3I[ ìàÌâ{ÎßM½£üþÞj6ÄßTÄ[V(Ý@?8/&“̼fá‹Ó“&À35»íÿ¹~3ðåwlümíßÿô í_^á\¬°|)ÊÆÖÚë]–*SÙ•VÚsûúÐsNGcÅcW9<6‹ o¡š‡wã3¡'ûÙ0îí`WøzØñÄùh»~eÞ÷þôöž¦ó¯^˜Av OD÷œ_v”ìÌZázƒ}nº¡TŠ’{ÅêûÌ%ÿòrm×vçõ e¸ÕZÎûŒ‹¦ ÖµO˜´QyùrË9zÿùKÍ×{kÄùë ÂÞruï/ÝNØØNÿ ý“[ˆzšÎߨ?ÇêgT»vÃü_&K€•ø›‚x ÀŒÅLyxu¢2‡I€KON­Kβ n7`7†ì^¹ÉJB¿ìV¸öú3ް)`gþ+äVĨÜë‚e˜ö]Gm-€GÆ4B[Dw¨û°ÏŠk}7rW9NÁÎþÄÂÁ–\£U“%’k•Ûº%— ëÝ*7Àƒ¤ˆqÁO‰^‡BõH€C¾Ë—6K.r?žgWJ/Ͱ`CûX€óÉž¢šÙ_ÆîÏ`ß»A×]t>¦ù´Ÿ$ˆ 1îsX×cÿð.F´É<Ë5šO©`]üMj¼`Í€«©ûÓ­¸ð™)‡$# «+ !¾ÅVƒüwèûÖÖÛ·+Œ«Dÿõß7m2Àý.x“nfT¹ñ•³ ðzçüÕXløRË]*´™ÈgÇ56–Ž)à÷â±áŸ-G?þ 7'ãã'Ê#ùcô|,Ï[DÂè¤ãÜJŸ›/¾ßY~ìä?°çuK]û;ÊOŒˆÍ—©ž Ô%j_T­ú"Éj"åVi½)©«îhçKe<Î۬ϛØÐ>àתŮÿ¹&º*°Øz{ƒ®Sû;ô‹<r«E×)ͲFè|Ï™/:[Åæ?8`cŒý7õïùyøyÛñ´¥ãúÖ´~[0)¬¿IŒ·œˆ¿Ë§O­+·ài>ñêêô$d÷>ƒï»ƒŸåö[tf0à¡›C>¤¥–`¯Cÿ¥’©<5D­&ãe™¾ã\€‡cãBh”­ï.]œ{õ)àáXo'½xlßs4«iÅ2aFç_%©Ü8dÓýˆ?g} ‘öJ28<¿ŠÌôEØÄ]÷Ö‹8¡m`¦ú ©í™çÚHnå pCÐ<ßW+°ÆöÃ9ž·]gfâôÓ2h¼‹S½\vÏ¥,u¼4:ßs¿—Œj‘ .òR2ØÆ}cŒý7õ¯!ØL \Ù q–×©Ï Z6ÄßäÅ[ÖÄß§ôñ×(Àk>þizR²;äWæûѦp; ²ä»r3p‡ pù¯ \¹n™&þël «·F¤É¾H±«ÂRrÛ·0_"Éà빌ý›-ÚŒ8ó[ŸšŽõÏ¡Â;ŽdÆéÙŠ£ËÇÉùòÛ0Õ¥dʲ.³­(žÂ‰`;&s€{Ë KëØ±d…«bŽãº¿µZB÷`™•º¢szÍ`9‘!Vø¸]ûH3µË÷ð2À`\{/Ýl(³]‰/Àœ °"À%Q´%×°)½¸%;ÆE¯ðóæm6û!Ìï_æ‰sQIêâ¦yåPã±Áà|Hz6ÅߤÇ[ÆáÖ¸à‚^€×LŸúizŠ2À‰9ÀBbð½2À{#5AÏî9›“{‘#Y¯¤ÇÑà„ûd¥¦žÜËÉËj¸'®Ìþ­íÓÖ‹ZOåÌf(;`¡/ØLo—[%ò8RÌ Ræ 0g0`œû]O޳RV•’­»‚Øk³+Mÿ#îO€iÿ}üþùý…ýQɲÐUäˆXE°Hµ›ão²ã-€Ok›ò°iÁ£þ’ ÉÈ»Õ*ÐB¢ ô=3ÀÂÎAѳ=qVí–·lì6œãÚÞ Øâƒà„àfÇc¼ 0^%éc"q踮 ´eÿ¢ºjÕæ*ÐÊã9¾dèw¤F”xŒpÂo’ŸæTfü|¥k·g`i)«ÜjÑ^€Ñób¯Í,fU£,©TºÄß·Ë[öÏ—I²ÊùÃ:ÀL‚›ãoòã-üß pá iO–Û ðâgÒ–-HUX]˜l<$8Ë Bþ51ësEŠðÚÂw¬¯AÎÕ5"Z¯„ NÇ›*[ÁÒ 0^ØOý›­)Ì–ÎÀ«ëÖïž A=£‘ÝÜu€'”îÉ‘ðe;k‚Mêa]3ªÅµXaù`Î:ÀT€‘ð’Ì1òuœhÍ÷(¹`¾{Ù`å¼Í.–¼—w½Åž€åje7wß.lê_æ'ìUxÕÁÙ ÀüO°9þ&=Þp醴º] 8OC,ÆŒþ¤!zk²²V€qâwèûÖÖÛ·èêGÎ2À‚PºBT—5B·p× V.‰JRÖsÖÇ‘{Sùv‚^€…YÈ|ëÛÕŽÓÙ¾Æ*ÐxÙàš¶¾xìÏlÙ£Ì1tAKùÎp_œÍ60»Rôœ}ç÷ýúÑüÌyÝR×þŽÆ#bóeÁqøP(r3ûwzgLj¸q›ZKßßpŽT°ÊoÌ£ëé hCûáÏéMåŸ °ÈŸÑÓž»N•Ù¸ÚësÖ‘^z·uÓ̶·7è9{¡£q׎åùû‚ž3«;|ö¯W¸ûºþûoì_Quó ›:ôGq†Úâ‹GÃ’àN€Mñ7ñ@€7¤©¼ø>òß§$~ð$ûFýãÔe€CµD_1/»0Ë þð<©€ ’ÜáŽõ5U.`l ÷¼÷¾XÒÆ·Á5f€¹'NŽ×·Ò}AÎSVAâ…öϾ&Ò=i÷¶uÓ½µ«Ü‚Ó 0>ŸÍµ]ò,¹ÛKåE×¾Éë¯Ü–#¹Îÿò×þËÞùÅFqÜqüV[ÝœÜ󭹓»”B•­Â¾‹Ä¨=P Ù[áÁVÀVù!P›C‘\c‰?v+pTE•ݨl#Åu‘‹vÂSx‹â—¶¨Hm‘еêÌîÞÝîÌìŸ[ßmúý¼X{¾ùóûÍÞÙÙY%Ÿë ›qqíÓt™¡(ÃçôQbãÀ˜øQmù^_[yz%ÍìüàÍó5H¡Ðöéf£BóÍÀ´¿÷ ƒ‡îpl±G°Ÿ³oûuÓyj®zÉ qÜ99€Ò%À6ý-ƒÞH€WAl¿Jüú¿{²î‹oÌtÅçàPlÏdvŸß®¿>qM€cñèGnù×ëÖ}á÷Ž w˜f¸Ïv?zþi‚moõ‰x8Tñ}úÿξpö˜¦”µ4~ô|ªÏá¹åðúk‡âéÌ/}zí3õéÌO·úø»lI€C§w ¥ï\ÜÚÓœ½CËÛûÛ39¢f'ì 0×¾‘§3û~’/Më?ù*ÛÞê#±üöéI##­žÏ¾–¨bä.3ïÌèÆlë<J<Î}ÀKàœýœ}ëoŒ½ŠGX—œ(Ú;± 0°ªàeRœnÚž­Åbáp(fɈ~êçÓÇÛAúÝàÝ w}õÿ›WTÙû—HÄ*r‰ÅBySºÌÚC•• ó=Áú·]¡pee¹Î ÓÙ–÷ô†{OÿaqñÂøÿ³ ˜ûW+Ï/..ÞÏ/÷ŽIÿDØý¸0ûاný«í©þ‚  °Úvfl“ó{€Ë¯·à—Q?"Û°Ù>ç/çvÄO2¸ýïúnY+Fe§râU7{‰ŠÒ´\Ž•Ù>/ôW@ñ`öˆH 0€¸¤t}½î_U«²gáß0îe²í¢?)û±ßöo4:ÒÛ㺠|̾·¶Fæò ° ˜ p 0€¸¤|þOÿ𖙊g»Ÿüðôæg»wÿãÊ $ˆñ‰ßŒÿîÃ;ê]÷[³ p û*oÆÙÕiô]D ` ðšäX×Wë¾Y­/µa7~MÞ[ü7TÕÛ“]”×þã—ð„hßú_ )H€H€$ÀkOyeõŽHÅìµGnö…W¤ýð釚Óg«^Ê3>¸}‰D€@¼¶9–XÍCR‘¨êêêZ¹TëeOòÄH€ÿ¿v^íI²4@ ½JG€—$ÀH€$À` 0PžøPwòuïrѧ÷/kƒ×Ž9—ž3çû6Ë›S3ßý`׋fKo3;ÇvÍÄ}ÕÛ0:øéŽ/Õ´4í/È?EƳ}wÚNNÇËØž—? €#êYýu°¥j@¦þâI{ÝkÇÆŸÂÕá;o޽â'¢8øËwùȶϴ÷”åŽG;·¿DžðèŸ×x•ü|*\ߣTÞÇv½¨÷Wôzôú[¼x¸Ÿh¬Þ¦wäÇ%gxKm.+Ñ;-¤vÒü—2qŸuEkýÒô‹j~p`É1œ[Ê7ŒÒï&ÿ2éîu±û£ícKE¾ªYž€µ§Èæ­¥kO´×õüð€3ÊU¢Ý4£$à5þÈôÁ_<É pÁñ§@&N2uдÖýÞöË’+ïž`y$˜+ÛìmX“ °³¾Gêô‘‘éfè9ôè/ô7 þK€£·Iã‘¥útæ•­Òã’31Kðí™f‹Zæà¹’¼¶x?Eš®+Ù,þžÇ\Ô6W>š9ÓB£·ë\¯Ç *çš–b_?,swb\ë¨/]{‚½îç‡çøÜ®Ï²'¿Òé4÷,jü‘郿x’à‚ãOA¨4 h#§ÏgÛs·_ðW¾|Óq¹„W‚)ŽG©`[s˼Qýô¿Œú=§<8Åô=édôzôú륿Áàšmª9U‡ãRÓ0KssºÕÇIòrNð¶3±SêEc™žhœ®^H5J=ŕ׋ìM¹Goáüqhµ fdx¦¹„í öºž^ãðEÛÍ… ø‡J-ÀþâI^€ Ž?1×Mšî±ú«Ÿ.ù²Ÿó—^>®—÷qïã«RæØfï2ïûëõ]}ºDÇÆÐ÷ƒè9ôè/ô7þK€¦ŒáVíÇŠZžó­ºŸ$ofGP½JHë—·³‚G7è±]™OËjZ’¦ªvH•så ¢ )­·ÇeÚÁ]@8ÿÿúaÛrŸá‰¨%lO°×õüð€ûo™-ó±^m–{Ú_<±p¡ñ§ÚfIòuÊOûm_È—÷5¡ëçëZ¾\ ¬ï¹Q¥Ÿ'¯G çÐs ¿Ðß@ú(V®Ú—óÇ¥>ßhj;•ŸÁ¸=2Ý£æ¯mV3ÃnÃ(©Ýdœ1ºP_Ô¤Ù^Þl‚-òéT\<ê& ¼?VߌqiÛãíu??¼Æà‘0Œt“Þz§€¨ \PBUR¨:t4»Æ#w˻Ұúàbß.sìªïyQ9Jä'"ôzôú8f›ü þl“ÍáêYò–±æˆÉË&«¼ðÇw.Üj¼fnBU}˜\Š«™“·RÄi&þLÿI1ÿOë’#ï;mÝÈ2[KüM7=˜ä&†õÑO¦â¦dž²—·ÌCl>P@xþWçoÜ‹µß½}~¼t‡DïÐñüxÉô{À|Êt›7®6ªÏ‘ ï,¿=‰½¼?„)×ñ¸Çwmê°¹ðEöôÌ_¸ø#Ä'á÷nÑ.žñ&[ã`Ÿ^¿ßïçû£Î-²â#îúyn‰Ê ¿5Ý~‰Y¿ )ïêIÿíúÇÕÏûO’[ËMå¯wj>3"¤­~Iûâ`®™ †eúÀ—äZI}·ŠŠ|ËMè9ôè/ô×[å pÃE}ë¬Öƒ¶3 ªJ”@…‘Gª÷êÞË.cb§­å™Ûב=ÏwëZ²Sj³:ŸJò‘5/xy[©äéËèÿXMTœ£¶(˜J»Û#,nKˆìþ Ö&2öôd ÚïÑ>?^l†¤­?_=ÿ>LtwÊÜ =Ñ^áü}ï9>š$]¿j½Ãe øÒøãùø$þÞ­úÀÇ>ÞXë— °Û÷õÔyóPó³ÌH¥}·š)Ú/Ñ#«@rå=ý!öŸÓ?‰[ý'&À¶òÔ]Ù[ ÊY#‡³×ϵ/Ø+éß–”ù™>ðã%¹‚YI}Ï1×¢]ª‡žCÏ€þBƒé¯4¾;KFvì%úc3üŒàðŬ9D_â›[¦3Ígt¨Á½ ©äûßî“ß‚§‘šl¸;“XHéÿo¯#­w‡žŽç–’ S ÂHä¯aÔ˜ÔŸL7†¹a–$§ö:χJ–æÏ{zÌØrþ`'á5-t´ö´¿ðã›ý¤éío·Ôé—RªšN3'媞×=C/(œw†óßž`¯p~´yÀè9Ot“KÍL\˜?||ï6}àã oøú ý>ë ‡éÌ™S~.¨OÔYb›Ì~‰Y’+ïé¡ÿ¼þ‰lóŸÛË«Ws—Em³º´qõsí öJú—Ò˜C3{¥ú ?, ðÊ껑’¦3ûºµ_m…žCÏ€þBê¯,nìÓŽLêy#»ÍÏ@¶q?¦ßXƒCã`¯ið`]«^“T µ˜ï(0Ö¨7Œ’VýpbT¾h©¦E ¼yÁc ö›3¡ÄÜ…ab–h—I«Ç"'¿K¬<„÷‡ª(l ¼b1Uo§§êÚïWÀò3ƃúµ=1²õ5ÌZO2ªÐµ›èhÕ:_PÐoo›§`ú@ÄQ:âô÷›Ÿ#µ|Yüq‹¿FŽc‰OÂïÝ®|<â¤þB¾¯Ï¯Ó?ñxÜÏ‚Jå(÷Î?Á~‰Yü%”âµ8mï?¯‚‹þ³%À\y¶¾N›J»±õ÷¾}Þ^Yÿz']ôA2^¶xeõݼ@Hrºz=ú ý ª¿²˜h—&™vWßá`Ë|¬ý¸ºßŒËôûz*ÊæôÉ@¹@Fsª?šÊ®%hOI"a»Þ\7i½WŸ¾s_;0J.é:º"Äу‚)m'wλ-™RùÚ$Ï ÌăÚïѾ8cLþÇÞÙµV‘¤q|›èhÈÅÜ$ Œ;؃,è9g3GÁ #Ñè`ö"“…‘…YYƒ1È…1Ä@ÖYÙ¹Jr1‚“…‘(ÄÄ— ýã¥W~ao¶ª_ëå©êêœhŽáÿCÐcwuwUu?ÿ~ž®z*<…nÎfQY0ù´¡gÇÛ¢[ýÌòÉÖ•nÂþˆ xø™ÁàXç ©Œ½ f ÚIDATöGµOúó.ëƒjOT{C¿Ôþg-C9 ë{5h~¦½î+sT=vÈÊûÒ‹¹=¤ë¿@èŸ&Àzû‰°Zž·Ç©øº£1¿êv¾+s€åëkÏ&X ú@õ—4Gkõ=u€ƒé‹ËÐsè9Ð_èïõ—t€ãˆeetŒ¯#¤ÍÖåEüíGñäÆˆ¾A÷&“R*KOŸ0ÄOòÏô>ªÚ =˜w•˜j$\ŸN¹ùÏOë±Ü>Þj|µDn Á‹—Q*A%ŽFdAów\ÿòãô>H¾ h‚éGË­‹ö•8Q_¥=â[;¿¿ú`PÐû‘Å­ådìY(åçO±?š}"XÒ=¢,Ûêø¥ö_z4~~é*Á!Àƒª«z$F ¯Æ/2Þ¢4§ÕÜ–ëo¥c°ÖÚOp€uý­Å¦y46´êv¾+Y åí£c©¥§õê/ñe¯õ=ú99q`Û¶´/ôzôúk×_rtâð3ŸëðXù=ZËÇ|GMÁ*œŒ}÷}:~’4XÔàÌx§Çj¯SF>)ê‚U¢x³éµGõù¨)z§xR†öT.”H‚%Œ=ëX0­ ²õw°|ÎÐ]Ï*˜Ñ´¡ð†eDÙ.d\C6éûè˜.ý0(@œ&‰ý•M´F •çO±?…¨ú G”e{C¿ÔþþÃ!¦$kß9b•VõHÜ!)ï/“:ižtýšþi¬·ŸøX+ßž¬g5I½«n'컚ZÜάtòiРTÉs´öZßãSñïœÆ,XÐsè9Ð_è¯]më³~(ý˜]F8}9á×$B|ò\&Èqƒ ¹*XA"`“@òÆeŠwÇ‹¾tßáɺ˜Eþv·³¿ÓˆñÎë_t~*kdvƒŒÐ‚ÉçõXS6öï^ÄØ_J @Ó©4ÉBæ ‘IÔçQyþ|Åþ8°¬zDY²7äñKíïûÃ_òA=Ÿ]Öš÷ˆ$ƒú2 F…ÎË“Ÿ"©´l_5ý«Ë0(í':ÀZùêFÉî{ÓS·öݰpܾ÷S+MëÙ_âõw‰¾GcÝŒ¢=‡žý…þÚõ×æÇ±€<"¸èøXQ»@ ßó-§±Ã©ôxüÊy6ˆƒ‰*§Å¼Ý!S¶ˆqéú»Gpóuí‚Y}%Fè0b¼è1V>8õÀl@&Ìò§/øÖ´oµ?%8+eIP©ã—Üß«T^onieûôeúJp^Þ¿ê–쫦FÎÛOþ¬–ï¿Þà3šNQÛ û®¯,´ïTš:‰Ö²¿ä/ÀÝ¢ïg…YwÐsè9Ð_èo)ýµ8À|lû‰È}/=wr€y ÕsHo1­f}>pš3S˜$#•xl~®{ì5Í‹)›4c÷³|ý‹Lé/‡ˆñùZóô¬e‘†Rç+Lã{‰¥$ì…“½í†ÑZ{éWŸI8Tûã.À‰>¨ö¤® ª~ü²ûW<¯:q»fYÇF’õ b)ÎÊï0­éŸI€…ö`½|”¿¸¾ž¼^©Ûë°ñ ðàù˜jé¥[ôÝÐsè9Ð_èo9ýµ8ÀìHÜ^§…ù°8ËÂå&PíOfÞk}VH†žCÏ€þBKê¯Ù®ÏÆž8O%5À6/;9À|¦dîGuë A —ž>~™ÄØ tòoïðrë qݼºaÝEh$~{ˆse¤ùÅbãÞ¸3`//LÞé4bœ­Hå÷ìBý ίöWaĘ5ÔG*þF-<¶\éü|D}‹"ÆZÿœðò¤FL9’§Z`ÝþåUû£Ø'£§úPQ®ˆÇwˆ@WÔëéý 1Õlð®n¿Ã}ø¯b¬‘³½?çåÅ´¥=4ûªêŸA€ÅöS×VÊs gZ!¶ö]ª/1G*Z>x6hFß&]úK]¸ô½Ò+ûâ=‡žý…þ–Ô_2 ôo«“¯n¢ë ÿ7>þý‰›\½„ /®Ml¯D=DT¸¾d"ÇìuxòÈø­àä«ZRàÌæµ‰×ó†A<ìF“¦òù|ž"“ýŪߊ‚íã­f4 ¦ý ë,Ö Õ×cÙ9Ï·‚|0|ôe‹*Oõ—Ô>{¬ï}ÿ~½Þ,­çå¡çÐs ¿Ðß’úK®¥( ¿ÅÕ½7e”>½ì–šÕðx‹€ý9sÊTá _,ÍGcèÎOn€1þ›]À_É‘U~}^Lqèý„IVµŸÈ¿ßœyüôúXÐ|ö·ôúCö?+A.@£¬Ži³©åó8„1^[vȔLJq¾Ù…ú_é/:bÕ7ipo, ˜ [¦ •8Q_«`Rýp"Nu{FW‚lœÈ¤¤š¡Ù Íþ(öI`E{¢Ùáø‡GÊí ð7é³0[÷U´ßDPþ<3 3—˜¤I<¤úô(o¯¤üÍmaH›¹=tûªèŸrütÈœØ~’L”çóÌrÉ“·ö]ª¯¶=¶×Á™ßGk_ÜúKnŸ½Õwvìé]zº]^¡çÐs ¿Ðß’úkr€›3³µ—¾ŸãËêú®pÅx}.lüzéEÔCD…ÛWa|Îðí±ÆÚ££ùºvþ[kÌ\\5,5È:ßÿ1ƒ9À‹ñý1³™îro;®ñ™GizŸÔ²TËgavÛJòå"Æì„¯VæšÓ_ ìFý‹Î/÷W¿]0Ù?O&ÏéàPÞ1œ¨¯]0‰þ¸Àla® gÓ¬3}±ó§Cû#£ÙÅ>Q,êƒ,¨º½ÉŽO °eÿøzz>X™k…Ók›ÙÖª`¿)^ú>’ÀðÌ ¢þÚ+)ŸG¹míA}•ôO9þàX8=×’ÛOv€õòÌ´Šj$m§ì»X_};³×µï–ù»ÛâÒ_Jû쩾×g£¾ › ¿{Ôý=‡žý…þ:è¯îõñ kÜÛéñÌö¯}`S¨Orüt¡Ÿÿð£ÿ¦oºÞ+Âtõ,^ÏĹyä°¿uà7¼š-N^eõI©-­…•{Ûá1Ó ú<;pãÙrw×öýqº›ý†¼î¯rú=@¡¿ï›\Ù¨¥IÆʸ³þÄøÊÜ`o±ñ•QëóÓk^õº½þ°ïìÃõXí7à}q•Ñwè9ú ý}ÿàÊðÇ×/·š3†if…ìà”“GßÚ¡ßYý`ßÙ‡ë)°ßp€÷ÁýUF„¡çè/ô÷ýs€+žçW|Ï{‡ãß»+?û¯?t§ý)¾ä×Áý=À>B; `àÝ;Àö!p€Ààp€8À`0€ 0`p€ü¿ý:¦@Ô¿µ|ƒ À À À À À À À° À À À À À À À À° À À À À À À ÀÐ ÿ€- 0 { fmt.Println("The following options are unused:") sort.Strings(unusedOptions) for _, s := range unusedOptions { fmt.Printf("%s (value: %v)\n", s, config.GlobalSettings[s]) } fmt.Printf("These options will be removed from %s\n", filepath.Join(config.ConfigDir, "settings.json")) if shouldContinue() { for _, s := range unusedOptions { delete(config.GlobalSettings, s) } err := config.OverwriteSettings(filepath.Join(config.ConfigDir, "settings.json")) if err != nil { fmt.Println("Error writing settings.json file: " + err.Error()) } fmt.Println("Removed unused options") fmt.Print("\n\n") } } // detect incorrectly formatted buffer/ files files, err := ioutil.ReadDir(filepath.Join(config.ConfigDir, "buffers")) if err == nil { var badFiles []string var buffer buffer.SerializedBuffer for _, f := range files { fname := filepath.Join(config.ConfigDir, "buffers", f.Name()) file, e := os.Open(fname) if e == nil { decoder := gob.NewDecoder(file) err = decoder.Decode(&buffer) if err != nil && f.Name() != "history" { badFiles = append(badFiles, fname) } file.Close() } } if len(badFiles) > 0 { fmt.Printf("Detected %d files with an invalid format in %s\n", len(badFiles), filepath.Join(config.ConfigDir, "buffers")) fmt.Println("These files store cursor and undo history.") fmt.Printf("Removing badly formatted files in %s\n", filepath.Join(config.ConfigDir, "buffers")) if shouldContinue() { removed := 0 for _, f := range badFiles { err := os.Remove(f) if err != nil { fmt.Println(err) continue } removed++ } if removed == 0 { fmt.Println("Failed to remove files") } else { fmt.Printf("Removed %d badly formatted files\n", removed) } fmt.Print("\n\n") } } } // detect plugins/ directory plugins := filepath.Join(config.ConfigDir, "plugins") if stat, err := os.Stat(plugins); err == nil && stat.IsDir() { fmt.Printf("Found directory %s\n", plugins) fmt.Printf("Plugins should now be stored in %s\n", filepath.Join(config.ConfigDir, "plug")) fmt.Printf("Removing %s\n", plugins) if shouldContinue() { os.RemoveAll(plugins) } fmt.Print("\n\n") } fmt.Println("Done cleaning") } micro-2.0.14/cmd/micro/debug.go0000664000175000017510000000117114663411671015542 0ustar nileshnileshpackage main import ( "log" "os" "github.com/zyedidia/micro/v2/internal/util" ) // NullWriter simply sends writes into the void type NullWriter struct{} // Write is empty func (NullWriter) Write(data []byte) (n int, err error) { return 0, nil } // InitLog sets up the debug log system for micro if it has been enabled by compile-time variables func InitLog() { if util.Debug == "ON" { f, err := os.OpenFile("log.txt", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666) if err != nil { log.Fatalf("error opening file: %v", err) } log.SetOutput(f) log.Println("Micro started") } else { log.SetOutput(NullWriter{}) } } micro-2.0.14/cmd/micro/initlua.go0000664000175000017510000001615714663411671016133 0ustar nileshnileshpackage main import ( "log" "time" lua "github.com/yuin/gopher-lua" luar "layeh.com/gopher-luar" "github.com/zyedidia/micro/v2/internal/action" "github.com/zyedidia/micro/v2/internal/buffer" "github.com/zyedidia/micro/v2/internal/config" "github.com/zyedidia/micro/v2/internal/display" ulua "github.com/zyedidia/micro/v2/internal/lua" "github.com/zyedidia/micro/v2/internal/screen" "github.com/zyedidia/micro/v2/internal/shell" "github.com/zyedidia/micro/v2/internal/util" ) func init() { ulua.L = lua.NewState() ulua.L.SetGlobal("import", luar.New(ulua.L, LuaImport)) } // LuaImport is meant to be called from lua by a plugin and will import the given micro package func LuaImport(pkg string) *lua.LTable { switch pkg { case "micro": return luaImportMicro() case "micro/shell": return luaImportMicroShell() case "micro/buffer": return luaImportMicroBuffer() case "micro/config": return luaImportMicroConfig() case "micro/util": return luaImportMicroUtil() default: return ulua.Import(pkg) } } func luaImportMicro() *lua.LTable { pkg := ulua.L.NewTable() ulua.L.SetField(pkg, "TermMessage", luar.New(ulua.L, screen.TermMessage)) ulua.L.SetField(pkg, "TermError", luar.New(ulua.L, screen.TermError)) ulua.L.SetField(pkg, "InfoBar", luar.New(ulua.L, action.GetInfoBar)) ulua.L.SetField(pkg, "Log", luar.New(ulua.L, log.Println)) ulua.L.SetField(pkg, "SetStatusInfoFn", luar.New(ulua.L, display.SetStatusInfoFnLua)) ulua.L.SetField(pkg, "CurPane", luar.New(ulua.L, func() *action.BufPane { return action.MainTab().CurPane() })) ulua.L.SetField(pkg, "CurTab", luar.New(ulua.L, action.MainTab)) ulua.L.SetField(pkg, "Tabs", luar.New(ulua.L, func() *action.TabList { return action.Tabs })) ulua.L.SetField(pkg, "After", luar.New(ulua.L, func(t time.Duration, f func()) { time.AfterFunc(t, func() { timerChan <- f }) })) return pkg } func luaImportMicroConfig() *lua.LTable { pkg := ulua.L.NewTable() ulua.L.SetField(pkg, "MakeCommand", luar.New(ulua.L, action.MakeCommand)) ulua.L.SetField(pkg, "FileComplete", luar.New(ulua.L, buffer.FileComplete)) ulua.L.SetField(pkg, "HelpComplete", luar.New(ulua.L, action.HelpComplete)) ulua.L.SetField(pkg, "OptionComplete", luar.New(ulua.L, action.OptionComplete)) ulua.L.SetField(pkg, "OptionValueComplete", luar.New(ulua.L, action.OptionValueComplete)) ulua.L.SetField(pkg, "NoComplete", luar.New(ulua.L, nil)) ulua.L.SetField(pkg, "TryBindKey", luar.New(ulua.L, action.TryBindKey)) ulua.L.SetField(pkg, "Reload", luar.New(ulua.L, action.ReloadConfig)) ulua.L.SetField(pkg, "AddRuntimeFileFromMemory", luar.New(ulua.L, config.PluginAddRuntimeFileFromMemory)) ulua.L.SetField(pkg, "AddRuntimeFilesFromDirectory", luar.New(ulua.L, config.PluginAddRuntimeFilesFromDirectory)) ulua.L.SetField(pkg, "AddRuntimeFile", luar.New(ulua.L, config.PluginAddRuntimeFile)) ulua.L.SetField(pkg, "ListRuntimeFiles", luar.New(ulua.L, config.PluginListRuntimeFiles)) ulua.L.SetField(pkg, "ReadRuntimeFile", luar.New(ulua.L, config.PluginReadRuntimeFile)) ulua.L.SetField(pkg, "NewRTFiletype", luar.New(ulua.L, config.NewRTFiletype)) ulua.L.SetField(pkg, "RTColorscheme", luar.New(ulua.L, config.RTColorscheme)) ulua.L.SetField(pkg, "RTSyntax", luar.New(ulua.L, config.RTSyntax)) ulua.L.SetField(pkg, "RTHelp", luar.New(ulua.L, config.RTHelp)) ulua.L.SetField(pkg, "RTPlugin", luar.New(ulua.L, config.RTPlugin)) ulua.L.SetField(pkg, "RegisterCommonOption", luar.New(ulua.L, config.RegisterCommonOptionPlug)) ulua.L.SetField(pkg, "RegisterGlobalOption", luar.New(ulua.L, config.RegisterGlobalOptionPlug)) ulua.L.SetField(pkg, "GetGlobalOption", luar.New(ulua.L, config.GetGlobalOption)) ulua.L.SetField(pkg, "SetGlobalOption", luar.New(ulua.L, action.SetGlobalOption)) ulua.L.SetField(pkg, "SetGlobalOptionNative", luar.New(ulua.L, action.SetGlobalOptionNative)) ulua.L.SetField(pkg, "ConfigDir", luar.New(ulua.L, config.ConfigDir)) return pkg } func luaImportMicroShell() *lua.LTable { pkg := ulua.L.NewTable() ulua.L.SetField(pkg, "ExecCommand", luar.New(ulua.L, shell.ExecCommand)) ulua.L.SetField(pkg, "RunCommand", luar.New(ulua.L, shell.RunCommand)) ulua.L.SetField(pkg, "RunBackgroundShell", luar.New(ulua.L, shell.RunBackgroundShell)) ulua.L.SetField(pkg, "RunInteractiveShell", luar.New(ulua.L, shell.RunInteractiveShell)) ulua.L.SetField(pkg, "JobStart", luar.New(ulua.L, shell.JobStart)) ulua.L.SetField(pkg, "JobSpawn", luar.New(ulua.L, shell.JobSpawn)) ulua.L.SetField(pkg, "JobStop", luar.New(ulua.L, shell.JobStop)) ulua.L.SetField(pkg, "JobSend", luar.New(ulua.L, shell.JobSend)) ulua.L.SetField(pkg, "RunTermEmulator", luar.New(ulua.L, action.RunTermEmulator)) ulua.L.SetField(pkg, "TermEmuSupported", luar.New(ulua.L, action.TermEmuSupported)) return pkg } func luaImportMicroBuffer() *lua.LTable { pkg := ulua.L.NewTable() ulua.L.SetField(pkg, "NewMessage", luar.New(ulua.L, buffer.NewMessage)) ulua.L.SetField(pkg, "NewMessageAtLine", luar.New(ulua.L, buffer.NewMessageAtLine)) ulua.L.SetField(pkg, "MTInfo", luar.New(ulua.L, buffer.MTInfo)) ulua.L.SetField(pkg, "MTWarning", luar.New(ulua.L, buffer.MTWarning)) ulua.L.SetField(pkg, "MTError", luar.New(ulua.L, buffer.MTError)) ulua.L.SetField(pkg, "Loc", luar.New(ulua.L, func(x, y int) buffer.Loc { return buffer.Loc{x, y} })) ulua.L.SetField(pkg, "SLoc", luar.New(ulua.L, func(line, row int) display.SLoc { return display.SLoc{line, row} })) ulua.L.SetField(pkg, "BTDefault", luar.New(ulua.L, buffer.BTDefault.Kind)) ulua.L.SetField(pkg, "BTHelp", luar.New(ulua.L, buffer.BTHelp.Kind)) ulua.L.SetField(pkg, "BTLog", luar.New(ulua.L, buffer.BTLog.Kind)) ulua.L.SetField(pkg, "BTScratch", luar.New(ulua.L, buffer.BTScratch.Kind)) ulua.L.SetField(pkg, "BTRaw", luar.New(ulua.L, buffer.BTRaw.Kind)) ulua.L.SetField(pkg, "BTInfo", luar.New(ulua.L, buffer.BTInfo.Kind)) ulua.L.SetField(pkg, "NewBuffer", luar.New(ulua.L, func(text, path string) *buffer.Buffer { return buffer.NewBufferFromString(text, path, buffer.BTDefault) })) ulua.L.SetField(pkg, "NewBufferFromFile", luar.New(ulua.L, func(path string) (*buffer.Buffer, error) { return buffer.NewBufferFromFile(path, buffer.BTDefault) })) ulua.L.SetField(pkg, "ByteOffset", luar.New(ulua.L, buffer.ByteOffset)) ulua.L.SetField(pkg, "Log", luar.New(ulua.L, buffer.WriteLog)) ulua.L.SetField(pkg, "LogBuf", luar.New(ulua.L, buffer.GetLogBuf)) return pkg } func luaImportMicroUtil() *lua.LTable { pkg := ulua.L.NewTable() ulua.L.SetField(pkg, "RuneAt", luar.New(ulua.L, util.LuaRuneAt)) ulua.L.SetField(pkg, "GetLeadingWhitespace", luar.New(ulua.L, util.LuaGetLeadingWhitespace)) ulua.L.SetField(pkg, "IsWordChar", luar.New(ulua.L, util.LuaIsWordChar)) ulua.L.SetField(pkg, "String", luar.New(ulua.L, util.String)) ulua.L.SetField(pkg, "Unzip", luar.New(ulua.L, util.Unzip)) ulua.L.SetField(pkg, "Version", luar.New(ulua.L, util.Version)) ulua.L.SetField(pkg, "SemVersion", luar.New(ulua.L, util.SemVersion)) ulua.L.SetField(pkg, "HttpRequest", luar.New(ulua.L, util.HttpRequest)) ulua.L.SetField(pkg, "CharacterCountInString", luar.New(ulua.L, util.CharacterCountInString)) ulua.L.SetField(pkg, "RuneStr", luar.New(ulua.L, func(r rune) string { return string(r) })) return pkg } micro-2.0.14/cmd/micro/micro.go0000664000175000017510000003023114663411671015564 0ustar nileshnileshpackage main import ( "flag" "fmt" "io" "io/ioutil" "log" "os" "os/signal" "regexp" "runtime" "runtime/pprof" "sort" "strconv" "syscall" "time" "github.com/go-errors/errors" isatty "github.com/mattn/go-isatty" lua "github.com/yuin/gopher-lua" "github.com/zyedidia/micro/v2/internal/action" "github.com/zyedidia/micro/v2/internal/buffer" "github.com/zyedidia/micro/v2/internal/clipboard" "github.com/zyedidia/micro/v2/internal/config" "github.com/zyedidia/micro/v2/internal/screen" "github.com/zyedidia/micro/v2/internal/shell" "github.com/zyedidia/micro/v2/internal/util" "github.com/zyedidia/tcell/v2" ) var ( // Command line flags flagVersion = flag.Bool("version", false, "Show the version number and information") flagConfigDir = flag.String("config-dir", "", "Specify a custom location for the configuration directory") flagOptions = flag.Bool("options", false, "Show all option help") flagDebug = flag.Bool("debug", false, "Enable debug mode (prints debug info to ./log.txt)") flagProfile = flag.Bool("profile", false, "Enable CPU profiling (writes profile info to ./micro.prof)") flagPlugin = flag.String("plugin", "", "Plugin command") flagClean = flag.Bool("clean", false, "Clean configuration directory") optionFlags map[string]*string sighup chan os.Signal timerChan chan func() ) func InitFlags() { flag.Usage = func() { fmt.Println("Usage: micro [OPTIONS] [FILE]...") fmt.Println("-clean") fmt.Println(" \tCleans the configuration directory") fmt.Println("-config-dir dir") fmt.Println(" \tSpecify a custom location for the configuration directory") fmt.Println("[FILE]:LINE:COL (if the `parsecursor` option is enabled)") fmt.Println("+LINE:COL") fmt.Println(" \tSpecify a line and column to start the cursor at when opening a buffer") fmt.Println("-options") fmt.Println(" \tShow all option help") fmt.Println("-debug") fmt.Println(" \tEnable debug mode (enables logging to ./log.txt)") fmt.Println("-profile") fmt.Println(" \tEnable CPU profiling (writes profile info to ./micro.prof") fmt.Println(" \tso it can be analyzed later with \"go tool pprof micro.prof\")") fmt.Println("-version") fmt.Println(" \tShow the version number and information") fmt.Print("\nMicro's plugins can be managed at the command line with the following commands.\n") fmt.Println("-plugin install [PLUGIN]...") fmt.Println(" \tInstall plugin(s)") fmt.Println("-plugin remove [PLUGIN]...") fmt.Println(" \tRemove plugin(s)") fmt.Println("-plugin update [PLUGIN]...") fmt.Println(" \tUpdate plugin(s) (if no argument is given, updates all plugins)") fmt.Println("-plugin search [PLUGIN]...") fmt.Println(" \tSearch for a plugin") fmt.Println("-plugin list") fmt.Println(" \tList installed plugins") fmt.Println("-plugin available") fmt.Println(" \tList available plugins") fmt.Print("\nMicro's options can also be set via command line arguments for quick\nadjustments. For real configuration, please use the settings.json\nfile (see 'help options').\n\n") fmt.Println("-option value") fmt.Println(" \tSet `option` to `value` for this session") fmt.Println(" \tFor example: `micro -syntax off file.c`") fmt.Println("\nUse `micro -options` to see the full list of configuration options") } optionFlags = make(map[string]*string) for k, v := range config.DefaultAllSettings() { optionFlags[k] = flag.String(k, "", fmt.Sprintf("The %s option. Default value: '%v'.", k, v)) } flag.Parse() if *flagVersion { // If -version was passed fmt.Println("Version:", util.Version) fmt.Println("Commit hash:", util.CommitHash) fmt.Println("Compiled on", util.CompileDate) os.Exit(0) } if *flagOptions { // If -options was passed var keys []string m := config.DefaultAllSettings() for k := range m { keys = append(keys, k) } sort.Strings(keys) for _, k := range keys { v := m[k] fmt.Printf("-%s value\n", k) fmt.Printf(" \tDefault value: '%v'\n", v) } os.Exit(0) } if util.Debug == "OFF" && *flagDebug { util.Debug = "ON" } } // DoPluginFlags parses and executes any flags that require LoadAllPlugins (-plugin and -clean) func DoPluginFlags() { if *flagClean || *flagPlugin != "" { config.LoadAllPlugins() if *flagPlugin != "" { args := flag.Args() config.PluginCommand(os.Stdout, *flagPlugin, args) } else if *flagClean { CleanConfig() } os.Exit(0) } } // LoadInput determines which files should be loaded into buffers // based on the input stored in flag.Args() func LoadInput(args []string) []*buffer.Buffer { // There are a number of ways micro should start given its input // 1. If it is given a files in flag.Args(), it should open those // 2. If there is no input file and the input is not a terminal, that means // something is being piped in and the stdin should be opened in an // empty buffer // 3. If there is no input file and the input is a terminal, an empty buffer // should be opened var filename string var input []byte var err error buffers := make([]*buffer.Buffer, 0, len(args)) btype := buffer.BTDefault if !isatty.IsTerminal(os.Stdout.Fd()) { btype = buffer.BTStdout } files := make([]string, 0, len(args)) flagStartPos := buffer.Loc{-1, -1} flagr := regexp.MustCompile(`^\+(\d+)(?::(\d+))?$`) for _, a := range args { match := flagr.FindStringSubmatch(a) if len(match) == 3 && match[2] != "" { line, err := strconv.Atoi(match[1]) if err != nil { screen.TermMessage(err) continue } col, err := strconv.Atoi(match[2]) if err != nil { screen.TermMessage(err) continue } flagStartPos = buffer.Loc{col - 1, line - 1} } else if len(match) == 3 && match[2] == "" { line, err := strconv.Atoi(match[1]) if err != nil { screen.TermMessage(err) continue } flagStartPos = buffer.Loc{0, line - 1} } else { files = append(files, a) } } if len(files) > 0 { // Option 1 // We go through each file and load it for i := 0; i < len(files); i++ { buf, err := buffer.NewBufferFromFileAtLoc(files[i], btype, flagStartPos) if err != nil { screen.TermMessage(err) continue } // If the file didn't exist, input will be empty, and we'll open an empty buffer buffers = append(buffers, buf) } } else if !isatty.IsTerminal(os.Stdin.Fd()) { // Option 2 // The input is not a terminal, so something is being piped in // and we should read from stdin input, err = ioutil.ReadAll(os.Stdin) if err != nil { screen.TermMessage("Error reading from stdin: ", err) input = []byte{} } buffers = append(buffers, buffer.NewBufferFromStringAtLoc(string(input), filename, btype, flagStartPos)) } else { // Option 3, just open an empty buffer buffers = append(buffers, buffer.NewBufferFromStringAtLoc(string(input), filename, btype, flagStartPos)) } return buffers } func main() { defer func() { if util.Stdout.Len() > 0 { fmt.Fprint(os.Stdout, util.Stdout.String()) } os.Exit(0) }() var err error InitFlags() if *flagProfile { f, err := os.Create("micro.prof") if err != nil { log.Fatal("error creating CPU profile: ", err) } if err := pprof.StartCPUProfile(f); err != nil { log.Fatal("error starting CPU profile: ", err) } defer pprof.StopCPUProfile() } InitLog() err = config.InitConfigDir(*flagConfigDir) if err != nil { screen.TermMessage(err) } config.InitRuntimeFiles(true) config.InitPlugins() err = config.ReadSettings() if err != nil { screen.TermMessage(err) } err = config.InitGlobalSettings() if err != nil { screen.TermMessage(err) } // flag options for k, v := range optionFlags { if *v != "" { nativeValue, err := config.GetNativeValue(k, config.DefaultAllSettings()[k], *v) if err != nil { screen.TermMessage(err) continue } if err = config.OptionIsValid(k, nativeValue); err != nil { screen.TermMessage(err) continue } config.GlobalSettings[k] = nativeValue config.VolatileSettings[k] = true } } DoPluginFlags() err = screen.Init() if err != nil { fmt.Println(err) fmt.Println("Fatal: Micro could not initialize a Screen.") os.Exit(1) } m := clipboard.SetMethod(config.GetGlobalOption("clipboard").(string)) clipErr := clipboard.Initialize(m) defer func() { if err := recover(); err != nil { if screen.Screen != nil { screen.Screen.Fini() } if e, ok := err.(*lua.ApiError); ok { fmt.Println("Lua API error:", e) } else { fmt.Println("Micro encountered an error:", errors.Wrap(err, 2).ErrorStack(), "\nIf you can reproduce this error, please report it at https://github.com/zyedidia/micro/issues") } // backup all open buffers for _, b := range buffer.OpenBuffers { b.Backup() } os.Exit(1) } }() err = config.LoadAllPlugins() if err != nil { screen.TermMessage(err) } action.InitBindings() action.InitCommands() err = config.InitColorscheme() if err != nil { screen.TermMessage(err) } err = config.RunPluginFn("preinit") if err != nil { screen.TermMessage(err) } action.InitGlobals() buffer.SetMessager(action.InfoBar) args := flag.Args() b := LoadInput(args) if len(b) == 0 { // No buffers to open screen.Screen.Fini() runtime.Goexit() } action.InitTabs(b) err = config.RunPluginFn("init") if err != nil { screen.TermMessage(err) } err = config.RunPluginFn("postinit") if err != nil { screen.TermMessage(err) } if clipErr != nil { log.Println(clipErr, " or change 'clipboard' option") } config.StartAutoSave() if a := config.GetGlobalOption("autosave").(float64); a > 0 { config.SetAutoTime(a) } screen.Events = make(chan tcell.Event) util.Sigterm = make(chan os.Signal, 1) sighup = make(chan os.Signal, 1) signal.Notify(util.Sigterm, syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGABRT) signal.Notify(sighup, syscall.SIGHUP) timerChan = make(chan func()) // Here is the event loop which runs in a separate thread go func() { for { screen.Lock() e := screen.Screen.PollEvent() screen.Unlock() if e != nil { screen.Events <- e } } }() // clear the drawchan so we don't redraw excessively // if someone requested a redraw before we started displaying for len(screen.DrawChan()) > 0 { <-screen.DrawChan() } // wait for initial resize event select { case event := <-screen.Events: action.Tabs.HandleEvent(event) case <-time.After(10 * time.Millisecond): // time out after 10ms } for { DoEvent() } } // DoEvent runs the main action loop of the editor func DoEvent() { var event tcell.Event // Display everything screen.Screen.Fill(' ', config.DefStyle) screen.Screen.HideCursor() action.Tabs.Display() for _, ep := range action.MainTab().Panes { ep.Display() } action.MainTab().Display() action.InfoBar.Display() screen.Screen.Show() // Check for new events select { case f := <-shell.Jobs: // If a new job has finished while running in the background we should execute the callback f.Function(f.Output, f.Args) case <-config.Autosave: for _, b := range buffer.OpenBuffers { b.AutoSave() } case <-shell.CloseTerms: action.Tabs.CloseTerms() case event = <-screen.Events: case <-screen.DrawChan(): for len(screen.DrawChan()) > 0 { <-screen.DrawChan() } case f := <-timerChan: f() case <-sighup: for _, b := range buffer.OpenBuffers { if !b.Modified() { b.Fini() } } os.Exit(0) case <-util.Sigterm: for _, b := range buffer.OpenBuffers { if !b.Modified() { b.Fini() } } if screen.Screen != nil { screen.Screen.Fini() } os.Exit(0) } if e, ok := event.(*tcell.EventError); ok { log.Println("tcell event error: ", e.Error()) if e.Err() == io.EOF { // shutdown due to terminal closing/becoming inaccessible for _, b := range buffer.OpenBuffers { if !b.Modified() { b.Fini() } } if screen.Screen != nil { screen.Screen.Fini() } os.Exit(0) } return } if event != nil { _, resize := event.(*tcell.EventResize) if resize { action.InfoBar.HandleEvent(event) action.Tabs.HandleEvent(event) } else if action.InfoBar.HasPrompt { action.InfoBar.HandleEvent(event) } else { action.Tabs.HandleEvent(event) } } err := config.RunPluginFn("onAnyEvent") if err != nil { screen.TermMessage(err) } } micro-2.0.14/cmd/micro/micro_test.go0000664000175000017510000001623314663411671016631 0ustar nileshnileshpackage main import ( "fmt" "io/ioutil" "log" "os" "testing" "github.com/go-errors/errors" "github.com/stretchr/testify/assert" "github.com/zyedidia/micro/v2/internal/action" "github.com/zyedidia/micro/v2/internal/buffer" "github.com/zyedidia/micro/v2/internal/config" "github.com/zyedidia/micro/v2/internal/screen" "github.com/zyedidia/tcell/v2" ) var tempDir string var sim tcell.SimulationScreen func init() { screen.Events = make(chan tcell.Event, 8) } func startup(args []string) (tcell.SimulationScreen, error) { var err error tempDir, err = ioutil.TempDir("", "micro_test") if err != nil { return nil, err } err = config.InitConfigDir(tempDir) if err != nil { return nil, err } config.InitRuntimeFiles(true) config.InitPlugins() err = config.ReadSettings() if err != nil { return nil, err } err = config.InitGlobalSettings() if err != nil { return nil, err } s, err := screen.InitSimScreen() if err != nil { return nil, err } defer func() { if err := recover(); err != nil { screen.Screen.Fini() fmt.Println("Micro encountered an error:", err) // backup all open buffers for _, b := range buffer.OpenBuffers { b.Backup() } // Print the stack trace too log.Fatalf(errors.Wrap(err, 2).ErrorStack()) } }() err = config.LoadAllPlugins() if err != nil { screen.TermMessage(err) } action.InitBindings() action.InitCommands() err = config.InitColorscheme() if err != nil { return nil, err } b := LoadInput(args) if len(b) == 0 { return nil, errors.New("No buffers opened") } action.InitTabs(b) action.InitGlobals() err = config.RunPluginFn("init") if err != nil { return nil, err } s.InjectResize() handleEvent() return s, nil } func cleanup() { os.RemoveAll(tempDir) } func handleEvent() { screen.Lock() e := screen.Screen.PollEvent() screen.Unlock() if e != nil { screen.Events <- e } for len(screen.DrawChan()) > 0 || len(screen.Events) > 0 { DoEvent() } } func injectKey(key tcell.Key, r rune, mod tcell.ModMask) { sim.InjectKey(key, r, mod) handleEvent() } func injectMouse(x, y int, buttons tcell.ButtonMask, mod tcell.ModMask) { sim.InjectMouse(x, y, buttons, mod) handleEvent() } func injectString(str string) { // the tcell simulation screen event channel can only handle // 10 events at once, so we need to divide up the key events // into chunks of 10 and handle the 10 events before sending // another chunk of events iters := len(str) / 10 extra := len(str) % 10 for i := 0; i < iters; i++ { s := i * 10 e := i*10 + 10 sim.InjectKeyBytes([]byte(str[s:e])) for i := 0; i < 10; i++ { handleEvent() } } sim.InjectKeyBytes([]byte(str[len(str)-extra:])) for i := 0; i < extra; i++ { handleEvent() } } func openFile(file string) { injectKey(tcell.KeyCtrlE, rune(tcell.KeyCtrlE), tcell.ModCtrl) injectString(fmt.Sprintf("open %s", file)) injectKey(tcell.KeyEnter, rune(tcell.KeyEnter), tcell.ModNone) } func findBuffer(file string) *buffer.Buffer { var buf *buffer.Buffer for _, b := range buffer.OpenBuffers { if b.Path == file { buf = b } } return buf } func createTestFile(name string, content string) (string, error) { testf, err := ioutil.TempFile("", name) if err != nil { return "", err } if _, err := testf.Write([]byte(content)); err != nil { return "", err } if err := testf.Close(); err != nil { return "", err } return testf.Name(), nil } func TestMain(m *testing.M) { var err error sim, err = startup([]string{}) if err != nil { log.Fatalln(err) } retval := m.Run() cleanup() os.Exit(retval) } func TestSimpleEdit(t *testing.T) { file, err := createTestFile("micro_simple_edit_test", "base content") if err != nil { t.Error(err) return } defer os.Remove(file) openFile(file) if findBuffer(file) == nil { t.Errorf("Could not find buffer %s", file) return } injectKey(tcell.KeyEnter, rune(tcell.KeyEnter), tcell.ModNone) injectKey(tcell.KeyUp, 0, tcell.ModNone) injectString("first line") // test both kinds of backspace for i := 0; i < len("ne"); i++ { injectKey(tcell.KeyBackspace, rune(tcell.KeyBackspace), tcell.ModNone) } for i := 0; i < len(" li"); i++ { injectKey(tcell.KeyBackspace2, rune(tcell.KeyBackspace2), tcell.ModNone) } injectString("foobar") injectKey(tcell.KeyCtrlS, rune(tcell.KeyCtrlS), tcell.ModCtrl) data, err := ioutil.ReadFile(file) if err != nil { t.Error(err) return } assert.Equal(t, "firstfoobar\nbase content\n", string(data)) } func TestMouse(t *testing.T) { file, err := createTestFile("micro_mouse_test", "base content") if err != nil { t.Error(err) return } defer os.Remove(file) openFile(file) if findBuffer(file) == nil { t.Errorf("Could not find buffer %s", file) return } // buffer: // base content // the selections need to happen at different locations to avoid a double click injectMouse(3, 0, tcell.Button1, tcell.ModNone) injectKey(tcell.KeyLeft, 0, tcell.ModNone) injectMouse(0, 0, tcell.ButtonNone, tcell.ModNone) injectString("secondline") // buffer: // secondlinebase content injectKey(tcell.KeyEnter, rune(tcell.KeyEnter), tcell.ModNone) // buffer: // secondline // base content injectMouse(2, 0, tcell.Button1, tcell.ModNone) injectMouse(0, 0, tcell.ButtonNone, tcell.ModNone) injectKey(tcell.KeyEnter, rune(tcell.KeyEnter), tcell.ModNone) // buffer: // // secondline // base content injectKey(tcell.KeyUp, 0, tcell.ModNone) injectString("firstline") // buffer: // firstline // secondline // base content injectKey(tcell.KeyCtrlS, rune(tcell.KeyCtrlS), tcell.ModCtrl) data, err := ioutil.ReadFile(file) if err != nil { t.Error(err) return } assert.Equal(t, "firstline\nsecondline\nbase content\n", string(data)) } var srTestStart = `foo foo foofoofoo ErnleÈe foo æðelen ` var srTest2 = `test_string test_string test_stringtest_stringtest_string ErnleÈe test_string æðelen ` var srTest3 = `test_foo test_string test_footest_stringtest_foo ErnleÈe test_string æðelen ` func TestSearchAndReplace(t *testing.T) { file, err := createTestFile("micro_search_replace_test", srTestStart) if err != nil { t.Error(err) return } defer os.Remove(file) openFile(file) if findBuffer(file) == nil { t.Errorf("Could not find buffer %s", file) return } injectKey(tcell.KeyCtrlE, rune(tcell.KeyCtrlE), tcell.ModCtrl) injectString(fmt.Sprintf("replaceall %s %s", "foo", "test_string")) injectKey(tcell.KeyEnter, rune(tcell.KeyEnter), tcell.ModNone) injectKey(tcell.KeyCtrlS, rune(tcell.KeyCtrlS), tcell.ModCtrl) data, err := ioutil.ReadFile(file) if err != nil { t.Error(err) return } assert.Equal(t, srTest2, string(data)) injectKey(tcell.KeyCtrlE, rune(tcell.KeyCtrlE), tcell.ModCtrl) injectString(fmt.Sprintf("replace %s %s", "string", "foo")) injectKey(tcell.KeyEnter, rune(tcell.KeyEnter), tcell.ModNone) injectString("ynyny") injectKey(tcell.KeyEscape, 0, tcell.ModNone) injectKey(tcell.KeyCtrlS, rune(tcell.KeyCtrlS), tcell.ModCtrl) data, err = ioutil.ReadFile(file) if err != nil { t.Error(err) return } assert.Equal(t, srTest3, string(data)) } func TestMultiCursor(t *testing.T) { // TODO } func TestSettingsPersistence(t *testing.T) { // TODO } // more tests (rendering, tabs, plugins)? micro-2.0.14/data/0000775000175000017510000000000014663411671013162 5ustar nileshnileshmicro-2.0.14/data/io.github.zyedidia.micro.metainfo.xml0000664000175000017510000000420114663411671022303 0ustar nileshnilesh io.github.zyedidia.micro micro.desktop Micro Text Editor A modern and intuitive terminal-based text editor

micro is a terminal-based text editor that aims to be easy to use and intuitive, while also taking advantage of the capabilities of modern terminals. It comes as a single, batteries-included, static binary with no dependencies; you can download and use it right now!

As its name indicates, micro aims to be somewhat of a successor to the nano editor by being easy to install and use. It strives to be enjoyable as a full-time editor for people who prefer to work in a terminal, or those who regularly edit files over SSH.

MIT MIT Development TextEditor micro com.github.zyedidia.micro Zachary Yedidia Micro Text Editor editing its source code https://raw.githubusercontent.com/zyedidia/micro/master/assets/micro-solarized.png https://micro-editor.github.io https://github.com/zyedidia/micro/issues https://micro-editor.github.io/about.html https://micro-editor.github.io/about.html https://github.com/zyedidia https://github.com/zyedidia/micro https://github.com/zyedidia/micro#contributing
micro-2.0.14/data/micro.json0000664000175000017510000004134214663411671015172 0ustar nileshnilesh{ "$comment": "https://github.com/zyedidia/micro", "$schema": "http://json-schema.org/draft-07/schema#", "title": "options", "description": "A micro editor config schema", "type": "object", "properties": { "autoindent": { "description": "Whether to use the same indentation as a previous line\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "boolean", "default": true }, "autosave": { "description": "A delay between automatic saves\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "integer", "minimum": 0, "default": 0 }, "autosu": { "description": "Whether attempt to use super user privileges\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "boolean", "default": false }, "backup": { "description": "Whether to backup all open buffers\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "boolean", "default": true }, "backupdir": { "description": "A directory to store backups\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "string", "default": "" }, "basename": { "description": "Whether to show a basename instead of a full path\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "boolean", "default": false }, "clipboard": { "description": "A way to access the system clipboard\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "string", "enum": [ "external", "terminal", "internal" ], "default": "external" }, "colorcolumn": { "description": "A position to display a column\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "integer", "minimum": 0, "default": 0 }, "colorscheme": { "description": "A color scheme\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "string", "enum": [ "atom-dark", "bubblegum", "cmc-16", "cmc-tc", "darcula", "default", "dracula-tc", "dukedark-tc", "dukelight-tc", "dukeubuntu-tc", "geany", "gotham", "gruvbox", "gruvbox-tc", "material-tc", "monokai-dark", "monokai", "one-dark", "railscast", "simple", "solarized", "solarized-tc", "sunny-day", "twilight", "zenburn" ], "default": "default" }, "cursorline": { "description": "Whether to highlight a line with a cursor with a different color\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "boolean", "default": true }, "diffgutter": { "description": "Whether to display diff inticators before lines\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "boolean", "default": false }, "divchars": { "description": "Divider chars for vertical and horizontal splits\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "string", "default": "|-" }, "divreverse": { "description": "Whether to use inversed color scheme colors for splits\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "boolean", "default": true }, "encoding": { "description": "An encoding used to open and save files\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "string", "default": "utf-8" }, "eofnewline": { "description": "Whether to add a missing trailing new line\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "boolean", "default": true }, "fastdirty": { "description": "Whether to use a fast algorithm to determine whether a file is changed\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "boolean", "default": false }, "fileformat": { "description": "A line ending format\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "string", "enum": [ "unix", "dos" ], "default": "unix" }, "filetype": { "description": "A filetype for the current buffer\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "string", "default": "unknown" }, "hlsearch": { "description": "Whether to highlight all instances of a searched text after a successful search\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "boolean", "default": false }, "incsearch": { "description": "Whether to enable an incremental search in `Find` prompt\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "boolean", "default": true }, "ignorecase": { "description": "Whether to perform case-insensitive searches\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "boolean", "default": true }, "indentchar": { "description": "An indentation character\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "string", "maxLength": 1, "default": " " }, "infobar": { "description": "Whether to enable a line at the bottom where messages are printed\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "boolean", "default": true }, "keepautoindent": { "description": "Whether add a whitespace while using autoindent\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "boolean", "default": false }, "keymenu": { "description": "Whether to display nano-style key menu at the bottom\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "boolean", "default": false }, "matchbrace": { "description": "Whether to show matching braces\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "boolean", "default": true }, "matchbracestyle": { "description": "Whether to underline or highlight matching braces\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "string", "enum": [ "underline", "highlight" ], "default": "underline" }, "mkparents": { "description": "Whether to create missing directories\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "boolean", "default": false }, "mouse": { "description": "Whether to enable mouse support\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "boolean", "default": true }, "paste": { "description": "Whether to treat characters sent from the terminal in a single chunk as a paste event\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "boolean", "default": false }, "parsecursor": { "description": "Whether to extract a line number and a column to open files with from file names\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "boolean", "default": false }, "permbackup": { "description": "Whether to permanently save backups\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "boolean", "default": false }, "pluginchannels": { "description": "A file with list of plugin channels\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "string", "default": "https://raw.githubusercontent.com/micro-editor/plugin-channel/master/channel.json" }, "pluginrepos": { "description": "Plugin repositories\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "array", "uniqueItems": true, "items": { "description": "A pluging repository\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "string" }, "default": [] }, "readonly": { "description": "Whether to forbid buffer editing\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "boolean", "default": false }, "rmtrailingws": { "description": "Whether to remove trailing whitespaces\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "boolean", "default": false }, "ruler": { "description": "Whether to display line numbers\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "boolean", "default": true }, "relativeruler": { "description": "Whether to display relative line numbers\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "boolean", "default": false }, "savecursor": { "description": "Whether to save cursor position in files\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "boolean", "default": false }, "savehistory": { "description": "Whether to save command history between closing and re-opening editor\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "boolean", "default": true }, "saveundo": { "description": "Whether to save undo after closing file\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "boolean", "default": false }, "scrollbar": { "description": "Whether to save undo after closing file\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "boolean", "default": false }, "scrollmargin": { "description": "A margin at which a view starts scrolling when a cursor approaches an edge of a view\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "integer", "default": 3 }, "scrollspeed": { "description": "Line count to scroll for one scroll event\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "integer", "default": 2 }, "smartpaste": { "description": "Whether to add a leading whitespace while pasting multiple lines\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "boolean", "default": true }, "softwrap": { "description": "Whether to wrap long lines\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "boolean", "default": false }, "splitbottom": { "description": "Whether to create a new horizontal split below the current one\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "boolean", "default": true }, "splitright": { "description": "Whether to create a new vertical split right of the current one\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "boolean", "default": true }, "statusformatl": { "description": "Format string of left-justified part of the statusline\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "string", "default": "$(filename) $(modified)($(line),$(col)) $(status.paste)| ft:$(opt:filetype) | $(opt:fileformat) | $(opt:encoding)" }, "statusformatr": { "description": "Format string of right-justified part of the statusline\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "string", "default": "$(bind:ToggleKeyMenu): bindings, $(bind:ToggleHelp): help" }, "statusline": { "description": "Whether to display a status line\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "boolean", "default": true }, "sucmd": { "description": "A super user command\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "string", "default": "sudo", "examples": [ "sudo", "doas" ] }, "syntax": { "description": "Whether to enable a syntax highlighting\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "boolean", "default": true }, "tabmovement": { "description": "Whether to navigate spaces at the beginning of lines as if they are tabs\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "boolean", "default": false }, "tabhighlight": { "description": "Whether to invert tab character colors\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "boolean", "default": false }, "tabreverse": { "description": "Whether to reverse tab bar colors\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "boolean", "default": true }, "tabsize": { "description": "A tab size\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "integer", "default": 4 }, "tabstospaces": { "description": "Whether to use spaces instead of tabs\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "boolean", "default": false }, "useprimary": { "description": "Whether to use primary clipboard to copy selections in the background\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "boolean", "default": true }, "wordwrap": { "description": "Whether to wrap long lines by words\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "boolean", "default": false }, "xterm": { "description": "Whether to assume that the current terminal is `xterm`\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options", "type": "boolean", "default": false } }, "additionalProperties": false } micro-2.0.14/go.mod0000664000175000017510000000213414663411671013357 0ustar nileshnileshmodule github.com/zyedidia/micro/v2 require ( github.com/blang/semver v3.5.1+incompatible github.com/dustin/go-humanize v1.0.0 github.com/go-errors/errors v1.0.1 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 github.com/mattn/go-isatty v0.0.11 github.com/mattn/go-runewidth v0.0.7 github.com/mitchellh/go-homedir v1.1.0 github.com/sergi/go-diff v1.1.0 github.com/stretchr/testify v1.4.0 github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb github.com/zyedidia/clipper v0.1.1 github.com/zyedidia/glob v0.0.0-20170209203856-dd4023a66dc3 github.com/zyedidia/json5 v0.0.0-20200102012142-2da050b1a98d github.com/zyedidia/tcell/v2 v2.0.10 // indirect github.com/zyedidia/terminal v0.0.0-20230315200948-4b3bcf6dddef golang.org/x/text v0.3.8 gopkg.in/yaml.v2 v2.2.8 layeh.com/gopher-luar v1.0.7 ) replace github.com/kballard/go-shellquote => github.com/zyedidia/go-shellquote v0.0.0-20200613203517-eccd813c0655 replace github.com/mattn/go-runewidth => github.com/zyedidia/go-runewidth v0.0.12 replace layeh.com/gopher-luar => github.com/layeh/gopher-luar v1.0.7 go 1.16 micro-2.0.14/go.sum0000664000175000017510000002335614663411671013415 0ustar nileshnileshgithub.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/layeh/gopher-luar v1.0.7 h1:wnfZhYiJM748y1A4qYBfcFeMY9HWbdERny+ZL0f/jWc= github.com/layeh/gopher-luar v1.0.7/go.mod h1:TPnIVCZ2RJBndm7ohXyaqfhzjlZ+OA2SZR/YwL8tECk= github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac= github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/xo/terminfo v0.0.0-20200218205459-454e5b68f9e8 h1:woqigIZtZUZxws1zZA99nAvuz2mQrxtWsuZSR9c8I/A= github.com/xo/terminfo v0.0.0-20200218205459-454e5b68f9e8/go.mod h1:6Yhx5ZJl5942QrNRWLwITArVT9okUXc5c3brgWJMoDc= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/gopher-lua v0.0.0-20190206043414-8bfc7677f583/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ= github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb h1:ZkM6LRnq40pR1Ox0hTHlnpkcOTuFIDQpZ1IN8rKKhX0= github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ= github.com/zyedidia/clipper v0.1.1 h1:HBgguFNDq/QmSQKBnhy4sMKzILINr139VEgAhftOUTw= github.com/zyedidia/clipper v0.1.1/go.mod h1:7YApPNiiTZTXdKKZG92G50qj6mnWEX975Sdu65J7YpQ= github.com/zyedidia/glob v0.0.0-20170209203856-dd4023a66dc3 h1:oMHjjTLfGXVuyOQBYj5/td9WC0mw4g1xDBPovIqmHew= github.com/zyedidia/glob v0.0.0-20170209203856-dd4023a66dc3/go.mod h1:YKbIYP//Eln8eDgAJGI3IDvR3s4Tv9Z9TGIOumiyQ5c= github.com/zyedidia/go-runewidth v0.0.12 h1:aHWj8qL3aH7caRzoPBJXe1pEaZBXHpKtfTuiBo5p74Q= github.com/zyedidia/go-runewidth v0.0.12/go.mod h1:vF8djYdLmG8BJaUZ4CznFYCJ3pFR8m4B4VinTvTTarU= github.com/zyedidia/go-shellquote v0.0.0-20200613203517-eccd813c0655 h1:Z3RhH6hvcSx7eX6Q/pP6YVsgea/1eMDG99vtWwi3nK4= github.com/zyedidia/go-shellquote v0.0.0-20200613203517-eccd813c0655/go.mod h1:1sTqqO+kcYzZp43M5VsJe1tns9IzlSeC9jB6c2+o/5Y= github.com/zyedidia/json5 v0.0.0-20200102012142-2da050b1a98d h1:zmDMkh22zXOB7gz8jFaI4GpI7llsPgzm38/jG0UgxjE= github.com/zyedidia/json5 v0.0.0-20200102012142-2da050b1a98d/go.mod h1:NDJSTTYWivnza6zkRapeX2/LwhKPEMQ7bJxqgDVT78I= github.com/zyedidia/poller v1.0.1 h1:Tt9S3AxAjXwWGNiC2TUdRJkQDZSzCBNVQ4xXiQ7440s= github.com/zyedidia/poller v1.0.1/go.mod h1:vZXJOHGDcuK08GXhF6IAY0ZFd2WcgOR5DOTp84Uk5eE= github.com/zyedidia/tcell/v2 v2.0.9 h1:FxXRkE62N0GPHES7EMLtp2rteYqC9r1kVid8vJN1kOE= github.com/zyedidia/tcell/v2 v2.0.9/go.mod h1:i4NNlquIQXFeNecrOgxDQQJdu+7LmTi3g62asvmwUws= github.com/zyedidia/tcell/v2 v2.0.10-0.20221007181625-f562052bccb8 h1:53ULv4mmLyQDnqbjVxanckP57WSreWHwTmlLJrJEutY= github.com/zyedidia/tcell/v2 v2.0.10-0.20221007181625-f562052bccb8/go.mod h1:i4NNlquIQXFeNecrOgxDQQJdu+7LmTi3g62asvmwUws= github.com/zyedidia/tcell/v2 v2.0.10-0.20230320201625-54f6acdada4a h1:W4TWa++Wk6uRGxZoxr2nPX1TpIEl+Wxv0mTtocG4TYc= github.com/zyedidia/tcell/v2 v2.0.10-0.20230320201625-54f6acdada4a/go.mod h1:i4NNlquIQXFeNecrOgxDQQJdu+7LmTi3g62asvmwUws= github.com/zyedidia/tcell/v2 v2.0.10-0.20230831153116-061c5b2c7260 h1:SCAmAacT5BxZsmOFdFy5zwwi6nj1MjA60gydjKdTgXo= github.com/zyedidia/tcell/v2 v2.0.10-0.20230831153116-061c5b2c7260/go.mod h1:i4NNlquIQXFeNecrOgxDQQJdu+7LmTi3g62asvmwUws= github.com/zyedidia/tcell/v2 v2.0.10 h1:6fbbYAx/DYc9A//4jU1OeBrxtc9qJxYCZXCtGQbtTWU= github.com/zyedidia/tcell/v2 v2.0.10/go.mod h1:i4NNlquIQXFeNecrOgxDQQJdu+7LmTi3g62asvmwUws= github.com/zyedidia/terminal v0.0.0-20230315200948-4b3bcf6dddef h1:LeB4Qs0Tss4r/Qh8pfsTTqagDYHysfKJLYzAH3MVfu0= github.com/zyedidia/terminal v0.0.0-20230315200948-4b3bcf6dddef/go.mod h1:zeb8MJdcCObFKVvur3n2B4BANIPuo2Q8r4iiNs9Enx0= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= micro-2.0.14/internal/0000775000175000017510000000000014663411671014065 5ustar nileshnileshmicro-2.0.14/internal/action/0000775000175000017510000000000014663411671015342 5ustar nileshnileshmicro-2.0.14/internal/action/actions.go0000664000175000017510000014620714663411671017343 0ustar nileshnileshpackage action import ( "errors" "fmt" "io/fs" "os" "regexp" "runtime" "strings" "time" shellquote "github.com/kballard/go-shellquote" "github.com/zyedidia/micro/v2/internal/buffer" "github.com/zyedidia/micro/v2/internal/clipboard" "github.com/zyedidia/micro/v2/internal/config" "github.com/zyedidia/micro/v2/internal/display" "github.com/zyedidia/micro/v2/internal/screen" "github.com/zyedidia/micro/v2/internal/shell" "github.com/zyedidia/micro/v2/internal/util" "github.com/zyedidia/tcell/v2" ) // ScrollUp is not an action func (h *BufPane) ScrollUp(n int) { v := h.GetView() v.StartLine = h.Scroll(v.StartLine, -n) h.SetView(v) } // ScrollDown is not an action func (h *BufPane) ScrollDown(n int) { v := h.GetView() v.StartLine = h.Scroll(v.StartLine, n) h.SetView(v) } // ScrollAdjust can be used to shift the view so that the last line is at the // bottom if the user has scrolled past the last line. func (h *BufPane) ScrollAdjust() { v := h.GetView() end := h.SLocFromLoc(h.Buf.End()) if h.Diff(v.StartLine, end) < h.BufView().Height-1 { v.StartLine = h.Scroll(end, -h.BufView().Height+1) } h.SetView(v) } // MousePress is the event that should happen when a normal click happens // This is almost always bound to left click func (h *BufPane) MousePress(e *tcell.EventMouse) bool { b := h.Buf mx, my := e.Position() // ignore click on the status line if my >= h.BufView().Y+h.BufView().Height { return false } mouseLoc := h.LocFromVisual(buffer.Loc{mx, my}) h.Cursor.Loc = mouseLoc if b.NumCursors() > 1 { b.ClearCursors() h.Relocate() h.Cursor = h.Buf.GetActiveCursor() h.Cursor.Loc = mouseLoc } if time.Since(h.lastClickTime)/time.Millisecond < config.DoubleClickThreshold && (mouseLoc.X == h.lastLoc.X && mouseLoc.Y == h.lastLoc.Y) { if h.doubleClick { // Triple click h.lastClickTime = time.Now() h.tripleClick = true h.doubleClick = false h.Cursor.SelectLine() h.Cursor.CopySelection(clipboard.PrimaryReg) } else { // Double click h.lastClickTime = time.Now() h.doubleClick = true h.tripleClick = false h.Cursor.SelectWord() h.Cursor.CopySelection(clipboard.PrimaryReg) } } else { h.doubleClick = false h.tripleClick = false h.lastClickTime = time.Now() h.Cursor.OrigSelection[0] = h.Cursor.Loc h.Cursor.CurSelection[0] = h.Cursor.Loc h.Cursor.CurSelection[1] = h.Cursor.Loc } h.Cursor.StoreVisualX() h.lastLoc = mouseLoc h.Relocate() return true } func (h *BufPane) MouseDrag(e *tcell.EventMouse) bool { mx, my := e.Position() // ignore drag on the status line if my >= h.BufView().Y+h.BufView().Height { return false } h.Cursor.Loc = h.LocFromVisual(buffer.Loc{mx, my}) if h.tripleClick { h.Cursor.AddLineToSelection() } else if h.doubleClick { h.Cursor.AddWordToSelection() } else { h.Cursor.SelectTo(h.Cursor.Loc) } h.Cursor.StoreVisualX() h.Relocate() return true } func (h *BufPane) MouseRelease(e *tcell.EventMouse) bool { // We could finish the selection based on the release location as in the // commented out code below, to allow text selections even in a terminal // that doesn't support mouse motion events. But when the mouse click is // within the scroll margin, that would cause a scroll and selection // even for a simple mouse click, which is not good. // if !h.doubleClick && !h.tripleClick { // mx, my := e.Position() // h.Cursor.Loc = h.LocFromVisual(buffer.Loc{mx, my}) // h.Cursor.SetSelectionEnd(h.Cursor.Loc) // } if h.Cursor.HasSelection() { h.Cursor.CopySelection(clipboard.PrimaryReg) } return true } // ScrollUpAction scrolls the view up func (h *BufPane) ScrollUpAction() bool { h.ScrollUp(util.IntOpt(h.Buf.Settings["scrollspeed"])) return true } // ScrollDownAction scrolls the view up func (h *BufPane) ScrollDownAction() bool { h.ScrollDown(util.IntOpt(h.Buf.Settings["scrollspeed"])) return true } // Center centers the view on the cursor func (h *BufPane) Center() bool { v := h.GetView() v.StartLine = h.Scroll(h.SLocFromLoc(h.Cursor.Loc), -h.BufView().Height/2) h.SetView(v) h.ScrollAdjust() return true } // MoveCursorUp is not an action func (h *BufPane) MoveCursorUp(n int) { if !h.Buf.Settings["softwrap"].(bool) { h.Cursor.UpN(n) } else { vloc := h.VLocFromLoc(h.Cursor.Loc) sloc := h.Scroll(vloc.SLoc, -n) if sloc == vloc.SLoc { // we are at the beginning of buffer h.Cursor.Loc = h.Buf.Start() h.Cursor.LastVisualX = 0 } else { vloc.SLoc = sloc vloc.VisualX = h.Cursor.LastVisualX h.Cursor.Loc = h.LocFromVLoc(vloc) } } } // MoveCursorDown is not an action func (h *BufPane) MoveCursorDown(n int) { if !h.Buf.Settings["softwrap"].(bool) { h.Cursor.DownN(n) } else { vloc := h.VLocFromLoc(h.Cursor.Loc) sloc := h.Scroll(vloc.SLoc, n) if sloc == vloc.SLoc { // we are at the end of buffer h.Cursor.Loc = h.Buf.End() vloc = h.VLocFromLoc(h.Cursor.Loc) h.Cursor.LastVisualX = vloc.VisualX } else { vloc.SLoc = sloc vloc.VisualX = h.Cursor.LastVisualX h.Cursor.Loc = h.LocFromVLoc(vloc) } } } // CursorUp moves the cursor up func (h *BufPane) CursorUp() bool { h.Cursor.Deselect(true) h.MoveCursorUp(1) h.Relocate() return true } // CursorDown moves the cursor down func (h *BufPane) CursorDown() bool { h.Cursor.Deselect(false) h.MoveCursorDown(1) h.Relocate() return true } // CursorLeft moves the cursor left func (h *BufPane) CursorLeft() bool { if h.Cursor.HasSelection() { h.Cursor.Deselect(true) } else { tabstospaces := h.Buf.Settings["tabstospaces"].(bool) tabmovement := h.Buf.Settings["tabmovement"].(bool) if tabstospaces && tabmovement { tabsize := int(h.Buf.Settings["tabsize"].(float64)) line := h.Buf.LineBytes(h.Cursor.Y) if h.Cursor.X-tabsize >= 0 && util.IsSpaces(line[h.Cursor.X-tabsize:h.Cursor.X]) && util.IsBytesWhitespace(line[0:h.Cursor.X-tabsize]) { for i := 0; i < tabsize; i++ { h.Cursor.Left() } } else { h.Cursor.Left() } } else { h.Cursor.Left() } } h.Relocate() return true } // CursorRight moves the cursor right func (h *BufPane) CursorRight() bool { if h.Cursor.HasSelection() { h.Cursor.Deselect(false) h.Cursor.Right() } else { tabstospaces := h.Buf.Settings["tabstospaces"].(bool) tabmovement := h.Buf.Settings["tabmovement"].(bool) if tabstospaces && tabmovement { tabsize := int(h.Buf.Settings["tabsize"].(float64)) line := h.Buf.LineBytes(h.Cursor.Y) if h.Cursor.X+tabsize < util.CharacterCount(line) && util.IsSpaces(line[h.Cursor.X:h.Cursor.X+tabsize]) && util.IsBytesWhitespace(line[0:h.Cursor.X]) { for i := 0; i < tabsize; i++ { h.Cursor.Right() } } else { h.Cursor.Right() } } else { h.Cursor.Right() } } h.Relocate() return true } // WordRight moves the cursor one word to the right func (h *BufPane) WordRight() bool { h.Cursor.Deselect(false) h.Cursor.WordRight() h.Relocate() return true } // WordLeft moves the cursor one word to the left func (h *BufPane) WordLeft() bool { h.Cursor.Deselect(true) h.Cursor.WordLeft() h.Relocate() return true } // SubWordRight moves the cursor one sub-word to the right func (h *BufPane) SubWordRight() bool { h.Cursor.Deselect(false) h.Cursor.SubWordRight() h.Relocate() return true } // SubWordLeft moves the cursor one sub-word to the left func (h *BufPane) SubWordLeft() bool { h.Cursor.Deselect(true) h.Cursor.SubWordLeft() h.Relocate() return true } // SelectUp selects up one line func (h *BufPane) SelectUp() bool { if !h.Cursor.HasSelection() { h.Cursor.OrigSelection[0] = h.Cursor.Loc } h.MoveCursorUp(1) h.Cursor.SelectTo(h.Cursor.Loc) h.Relocate() return true } // SelectDown selects down one line func (h *BufPane) SelectDown() bool { if !h.Cursor.HasSelection() { h.Cursor.OrigSelection[0] = h.Cursor.Loc } h.MoveCursorDown(1) h.Cursor.SelectTo(h.Cursor.Loc) h.Relocate() return true } // SelectLeft selects the character to the left of the cursor func (h *BufPane) SelectLeft() bool { loc := h.Cursor.Loc count := h.Buf.End() if loc.GreaterThan(count) { loc = count } if !h.Cursor.HasSelection() { h.Cursor.OrigSelection[0] = loc } h.Cursor.Left() h.Cursor.SelectTo(h.Cursor.Loc) h.Relocate() return true } // SelectRight selects the character to the right of the cursor func (h *BufPane) SelectRight() bool { loc := h.Cursor.Loc count := h.Buf.End() if loc.GreaterThan(count) { loc = count } if !h.Cursor.HasSelection() { h.Cursor.OrigSelection[0] = loc } h.Cursor.Right() h.Cursor.SelectTo(h.Cursor.Loc) h.Relocate() return true } // SelectWordRight selects the word to the right of the cursor func (h *BufPane) SelectWordRight() bool { if !h.Cursor.HasSelection() { h.Cursor.OrigSelection[0] = h.Cursor.Loc } h.Cursor.WordRight() h.Cursor.SelectTo(h.Cursor.Loc) h.Relocate() return true } // SelectWordLeft selects the word to the left of the cursor func (h *BufPane) SelectWordLeft() bool { if !h.Cursor.HasSelection() { h.Cursor.OrigSelection[0] = h.Cursor.Loc } h.Cursor.WordLeft() h.Cursor.SelectTo(h.Cursor.Loc) h.Relocate() return true } // SelectSubWordRight selects the sub-word to the right of the cursor func (h *BufPane) SelectSubWordRight() bool { if !h.Cursor.HasSelection() { h.Cursor.OrigSelection[0] = h.Cursor.Loc } h.Cursor.SubWordRight() h.Cursor.SelectTo(h.Cursor.Loc) h.Relocate() return true } // SelectSubWordLeft selects the sub-word to the left of the cursor func (h *BufPane) SelectSubWordLeft() bool { if !h.Cursor.HasSelection() { h.Cursor.OrigSelection[0] = h.Cursor.Loc } h.Cursor.SubWordLeft() h.Cursor.SelectTo(h.Cursor.Loc) h.Relocate() return true } // StartOfText moves the cursor to the start of the text of the line func (h *BufPane) StartOfText() bool { h.Cursor.Deselect(true) h.Cursor.StartOfText() h.Relocate() return true } // StartOfTextToggle toggles the cursor between the start of the text of the line // and the start of the line func (h *BufPane) StartOfTextToggle() bool { h.Cursor.Deselect(true) if h.Cursor.IsStartOfText() { h.Cursor.Start() } else { h.Cursor.StartOfText() } h.Relocate() return true } // StartOfLine moves the cursor to the start of the line func (h *BufPane) StartOfLine() bool { h.Cursor.Deselect(true) h.Cursor.Start() h.Relocate() return true } // EndOfLine moves the cursor to the end of the line func (h *BufPane) EndOfLine() bool { h.Cursor.Deselect(true) h.Cursor.End() h.Relocate() return true } // SelectLine selects the entire current line func (h *BufPane) SelectLine() bool { h.Cursor.SelectLine() h.Relocate() return true } // SelectToStartOfText selects to the start of the text on the current line func (h *BufPane) SelectToStartOfText() bool { if !h.Cursor.HasSelection() { h.Cursor.OrigSelection[0] = h.Cursor.Loc } h.Cursor.StartOfText() h.Cursor.SelectTo(h.Cursor.Loc) h.Relocate() return true } // SelectToStartOfTextToggle toggles the selection between the start of the text // on the current line and the start of the line func (h *BufPane) SelectToStartOfTextToggle() bool { if !h.Cursor.HasSelection() { h.Cursor.OrigSelection[0] = h.Cursor.Loc } if h.Cursor.IsStartOfText() { h.Cursor.Start() } else { h.Cursor.StartOfText() } h.Cursor.SelectTo(h.Cursor.Loc) h.Relocate() return true } // SelectToStartOfLine selects to the start of the current line func (h *BufPane) SelectToStartOfLine() bool { if !h.Cursor.HasSelection() { h.Cursor.OrigSelection[0] = h.Cursor.Loc } h.Cursor.Start() h.Cursor.SelectTo(h.Cursor.Loc) h.Relocate() return true } // SelectToEndOfLine selects to the end of the current line func (h *BufPane) SelectToEndOfLine() bool { if !h.Cursor.HasSelection() { h.Cursor.OrigSelection[0] = h.Cursor.Loc } h.Cursor.End() h.Cursor.SelectTo(h.Cursor.Loc) h.Relocate() return true } func (h *BufPane) paragraphPrevious() { var line int // Skip to the first non-empty line for line = h.Cursor.Y; line > 0; line-- { if len(h.Buf.LineBytes(line)) != 0 { break } } // Find the first empty line for ; line > 0; line-- { if len(h.Buf.LineBytes(line)) == 0 { h.Cursor.X = 0 h.Cursor.Y = line break } } // If no empty line was found, move the cursor to the start of the buffer if line == 0 { h.Cursor.Loc = h.Buf.Start() } } func (h *BufPane) paragraphNext() { var line int // Skip to the first non-empty line for line = h.Cursor.Y; line < h.Buf.LinesNum(); line++ { if len(h.Buf.LineBytes(line)) != 0 { break } } // Find the first empty line for ; line < h.Buf.LinesNum(); line++ { if len(h.Buf.LineBytes(line)) == 0 { h.Cursor.X = 0 h.Cursor.Y = line break } } // If no empty line was found, move the cursor to the end of the buffer if line == h.Buf.LinesNum() { h.Cursor.Loc = h.Buf.End() } } // ParagraphPrevious moves the cursor to the first empty line that comes before // the paragraph closest to the cursor, or beginning of the buffer if there // isn't a paragraph func (h *BufPane) ParagraphPrevious() bool { h.Cursor.Deselect(true) h.paragraphPrevious() h.Relocate() return true } // ParagraphNext moves the cursor to the first empty line that comes after the // paragraph closest to the cursor, or end of the buffer if there isn't a // paragraph func (h *BufPane) ParagraphNext() bool { h.Cursor.Deselect(true) h.paragraphNext() h.Relocate() return true } // SelectToParagraphPrevious selects to the first empty line that comes before // the paragraph closest to the cursor, or beginning of the buffer if there // isn't a paragraph func (h *BufPane) SelectToParagraphPrevious() bool { if !h.Cursor.HasSelection() { h.Cursor.OrigSelection[0] = h.Cursor.Loc } h.paragraphPrevious() h.Cursor.SelectTo(h.Cursor.Loc) h.Relocate() return true } // SelectToParagraphNext selects to the first empty line that comes after the // paragraph closest to the cursor, or end of the buffer if there isn't a // paragraph func (h *BufPane) SelectToParagraphNext() bool { if !h.Cursor.HasSelection() { h.Cursor.OrigSelection[0] = h.Cursor.Loc } h.paragraphNext() h.Cursor.SelectTo(h.Cursor.Loc) h.Relocate() return true } // Retab changes all tabs to spaces or all spaces to tabs depending // on the user's settings func (h *BufPane) Retab() bool { h.Buf.Retab() h.Relocate() return true } // CursorStart moves the cursor to the start of the buffer func (h *BufPane) CursorStart() bool { h.Cursor.Deselect(true) h.Cursor.X = 0 h.Cursor.Y = 0 h.Cursor.StoreVisualX() h.Relocate() return true } // CursorEnd moves the cursor to the end of the buffer func (h *BufPane) CursorEnd() bool { h.Cursor.Deselect(true) h.Cursor.Loc = h.Buf.End() h.Cursor.StoreVisualX() h.Relocate() return true } // SelectToStart selects the text from the cursor to the start of the buffer func (h *BufPane) SelectToStart() bool { if !h.Cursor.HasSelection() { h.Cursor.OrigSelection[0] = h.Cursor.Loc } h.CursorStart() h.Cursor.SelectTo(h.Buf.Start()) h.Relocate() return true } // SelectToEnd selects the text from the cursor to the end of the buffer func (h *BufPane) SelectToEnd() bool { if !h.Cursor.HasSelection() { h.Cursor.OrigSelection[0] = h.Cursor.Loc } h.CursorEnd() h.Cursor.SelectTo(h.Buf.End()) h.Relocate() return true } // InsertNewline inserts a newline plus possible some whitespace if autoindent is on func (h *BufPane) InsertNewline() bool { // Insert a newline if h.Cursor.HasSelection() { h.Cursor.DeleteSelection() h.Cursor.ResetSelection() } ws := util.GetLeadingWhitespace(h.Buf.LineBytes(h.Cursor.Y)) cx := h.Cursor.X h.Buf.Insert(h.Cursor.Loc, "\n") // h.Cursor.Right() if h.Buf.Settings["autoindent"].(bool) { if cx < len(ws) { ws = ws[0:cx] } h.Buf.Insert(h.Cursor.Loc, string(ws)) // for i := 0; i < len(ws); i++ { // h.Cursor.Right() // } // Remove the whitespaces if keepautoindent setting is off if util.IsSpacesOrTabs(h.Buf.LineBytes(h.Cursor.Y-1)) && !h.Buf.Settings["keepautoindent"].(bool) { line := h.Buf.LineBytes(h.Cursor.Y - 1) h.Buf.Remove(buffer.Loc{X: 0, Y: h.Cursor.Y - 1}, buffer.Loc{X: util.CharacterCount(line), Y: h.Cursor.Y - 1}) } } h.Cursor.LastVisualX = h.Cursor.GetVisualX() h.Relocate() return true } // Backspace deletes the previous character func (h *BufPane) Backspace() bool { if h.Cursor.HasSelection() { h.Cursor.DeleteSelection() h.Cursor.ResetSelection() } else if h.Cursor.Loc.GreaterThan(h.Buf.Start()) { // We have to do something a bit hacky here because we want to // delete the line by first moving left and then deleting backwards // but the undo redo would place the cursor in the wrong place // So instead we move left, save the position, move back, delete // and restore the position // If the user is using spaces instead of tabs and they are deleting // whitespace at the start of the line, we should delete as if it's a // tab (tabSize number of spaces) lineStart := util.SliceStart(h.Buf.LineBytes(h.Cursor.Y), h.Cursor.X) tabSize := int(h.Buf.Settings["tabsize"].(float64)) if h.Buf.Settings["tabstospaces"].(bool) && util.IsSpaces(lineStart) && len(lineStart) != 0 && util.CharacterCount(lineStart)%tabSize == 0 { loc := h.Cursor.Loc h.Buf.Remove(loc.Move(-tabSize, h.Buf), loc) } else { loc := h.Cursor.Loc h.Buf.Remove(loc.Move(-1, h.Buf), loc) } } h.Cursor.LastVisualX = h.Cursor.GetVisualX() h.Relocate() return true } // DeleteWordRight deletes the word to the right of the cursor func (h *BufPane) DeleteWordRight() bool { h.SelectWordRight() if h.Cursor.HasSelection() { h.Cursor.DeleteSelection() h.Cursor.ResetSelection() } h.Relocate() return true } // DeleteWordLeft deletes the word to the left of the cursor func (h *BufPane) DeleteWordLeft() bool { h.SelectWordLeft() if h.Cursor.HasSelection() { h.Cursor.DeleteSelection() h.Cursor.ResetSelection() } h.Relocate() return true } // DeleteSubWordRight deletes the sub-word to the right of the cursor func (h *BufPane) DeleteSubWordRight() bool { h.SelectSubWordRight() if h.Cursor.HasSelection() { h.Cursor.DeleteSelection() h.Cursor.ResetSelection() } h.Relocate() return true } // DeleteSubWordLeft deletes the sub-word to the left of the cursor func (h *BufPane) DeleteSubWordLeft() bool { h.SelectSubWordLeft() if h.Cursor.HasSelection() { h.Cursor.DeleteSelection() h.Cursor.ResetSelection() } h.Relocate() return true } // Delete deletes the next character func (h *BufPane) Delete() bool { if h.Cursor.HasSelection() { h.Cursor.DeleteSelection() h.Cursor.ResetSelection() } else { loc := h.Cursor.Loc if loc.LessThan(h.Buf.End()) { h.Buf.Remove(loc, loc.Move(1, h.Buf)) } } h.Relocate() return true } // IndentSelection indents the current selection func (h *BufPane) IndentSelection() bool { if h.Cursor.HasSelection() { start := h.Cursor.CurSelection[0] end := h.Cursor.CurSelection[1] if end.Y < start.Y { start, end = end, start h.Cursor.SetSelectionStart(start) h.Cursor.SetSelectionEnd(end) } startY := start.Y endY := end.Move(-1, h.Buf).Y endX := end.Move(-1, h.Buf).X tabsize := int(h.Buf.Settings["tabsize"].(float64)) indentsize := len(h.Buf.IndentString(tabsize)) for y := startY; y <= endY; y++ { if len(h.Buf.LineBytes(y)) > 0 { h.Buf.Insert(buffer.Loc{X: 0, Y: y}, h.Buf.IndentString(tabsize)) if y == startY && start.X > 0 { h.Cursor.SetSelectionStart(start.Move(indentsize, h.Buf)) } if y == endY { h.Cursor.SetSelectionEnd(buffer.Loc{X: endX + indentsize + 1, Y: endY}) } } } h.Buf.RelocateCursors() h.Relocate() return true } return false } // IndentLine moves the current line forward one indentation func (h *BufPane) IndentLine() bool { if h.Cursor.HasSelection() { return false } tabsize := int(h.Buf.Settings["tabsize"].(float64)) indentstr := h.Buf.IndentString(tabsize) h.Buf.Insert(buffer.Loc{X: 0, Y: h.Cursor.Y}, indentstr) h.Buf.RelocateCursors() h.Relocate() return true } // OutdentLine moves the current line back one indentation func (h *BufPane) OutdentLine() bool { if h.Cursor.HasSelection() { return false } for x := 0; x < len(h.Buf.IndentString(util.IntOpt(h.Buf.Settings["tabsize"]))); x++ { if len(util.GetLeadingWhitespace(h.Buf.LineBytes(h.Cursor.Y))) == 0 { break } h.Buf.Remove(buffer.Loc{X: 0, Y: h.Cursor.Y}, buffer.Loc{X: 1, Y: h.Cursor.Y}) } h.Buf.RelocateCursors() h.Relocate() return true } // OutdentSelection takes the current selection and moves it back one indent level func (h *BufPane) OutdentSelection() bool { if h.Cursor.HasSelection() { start := h.Cursor.CurSelection[0] end := h.Cursor.CurSelection[1] if end.Y < start.Y { start, end = end, start h.Cursor.SetSelectionStart(start) h.Cursor.SetSelectionEnd(end) } startY := start.Y endY := end.Move(-1, h.Buf).Y for y := startY; y <= endY; y++ { for x := 0; x < len(h.Buf.IndentString(util.IntOpt(h.Buf.Settings["tabsize"]))); x++ { if len(util.GetLeadingWhitespace(h.Buf.LineBytes(y))) == 0 { break } h.Buf.Remove(buffer.Loc{X: 0, Y: y}, buffer.Loc{X: 1, Y: y}) } } h.Buf.RelocateCursors() h.Relocate() return true } return false } // Autocomplete cycles the suggestions and performs autocompletion if there are suggestions func (h *BufPane) Autocomplete() bool { b := h.Buf if h.Cursor.HasSelection() { return false } if h.Cursor.X == 0 { return false } r := h.Cursor.RuneUnder(h.Cursor.X) prev := h.Cursor.RuneUnder(h.Cursor.X - 1) if !util.IsAutocomplete(prev) || util.IsWordChar(r) { // don't autocomplete if cursor is within a word return false } if b.HasSuggestions { b.CycleAutocomplete(true) return true } return b.Autocomplete(buffer.BufferComplete) } // CycleAutocompleteBack cycles back in the autocomplete suggestion list func (h *BufPane) CycleAutocompleteBack() bool { if h.Cursor.HasSelection() { return false } if h.Buf.HasSuggestions { h.Buf.CycleAutocomplete(false) return true } return false } // InsertTab inserts a tab or spaces func (h *BufPane) InsertTab() bool { b := h.Buf indent := b.IndentString(util.IntOpt(b.Settings["tabsize"])) tabBytes := len(indent) bytesUntilIndent := tabBytes - (h.Cursor.GetVisualX() % tabBytes) b.Insert(h.Cursor.Loc, indent[:bytesUntilIndent]) h.Relocate() return true } // SaveAll saves all open buffers func (h *BufPane) SaveAll() bool { for _, b := range buffer.OpenBuffers { b.Save() } return true } // SaveCB performs a save and does a callback at the very end (after all prompts have been resolved) func (h *BufPane) SaveCB(action string, callback func()) bool { // If this is an empty buffer, ask for a filename if h.Buf.Path == "" { h.SaveAsCB(action, callback) } else { noPrompt := h.saveBufToFile(h.Buf.Path, action, callback) if noPrompt { return true } } return false } // Save the buffer to disk func (h *BufPane) Save() bool { return h.SaveCB("Save", nil) } // SaveAsCB performs a save as and does a callback at the very end (after all prompts have been resolved) // The callback is only called if the save was successful func (h *BufPane) SaveAsCB(action string, callback func()) bool { InfoBar.Prompt("Filename: ", "", "Save", nil, func(resp string, canceled bool) { if !canceled { // the filename might or might not be quoted, so unquote first then join the strings. args, err := shellquote.Split(resp) if err != nil { InfoBar.Error("Error parsing arguments: ", err) return } if len(args) == 0 { InfoBar.Error("No filename given") return } filename := strings.Join(args, " ") fileinfo, err := os.Stat(filename) if err != nil { if errors.Is(err, fs.ErrNotExist) || errors.Is(err, fs.ErrPermission) { noPrompt := h.saveBufToFile(filename, action, callback) if noPrompt { h.completeAction(action) return } } } else { InfoBar.YNPrompt( fmt.Sprintf("The file %s already exists in the directory, would you like to overwrite? Y/n", fileinfo.Name()), func(yes, canceled bool) { if yes && !canceled { noPrompt := h.saveBufToFile(filename, action, callback) if noPrompt { h.completeAction(action) } } }, ) } } }) return false } // SaveAs saves the buffer to disk with the given name func (h *BufPane) SaveAs() bool { return h.SaveAsCB("SaveAs", nil) } // This function saves the buffer to `filename` and changes the buffer's path and name // to `filename` if the save is successful // The callback is only called if the save was successful func (h *BufPane) saveBufToFile(filename string, action string, callback func()) bool { err := h.Buf.SaveAs(filename) if err != nil { if errors.Is(err, fs.ErrPermission) { saveWithSudo := func() { err = h.Buf.SaveAsWithSudo(filename) if err != nil { InfoBar.Error(err) } else { h.Buf.Path = filename h.Buf.SetName(filename) InfoBar.Message("Saved " + filename) if callback != nil { callback() } } } if h.Buf.Settings["autosu"].(bool) { saveWithSudo() } else { InfoBar.YNPrompt( fmt.Sprintf("Permission denied. Do you want to save this file using %s? (y,n)", config.GlobalSettings["sucmd"].(string)), func(yes, canceled bool) { if yes && !canceled { saveWithSudo() h.completeAction(action) } }, ) return false } } else { InfoBar.Error(err) } } else { h.Buf.Path = filename h.Buf.SetName(filename) InfoBar.Message("Saved " + filename) if callback != nil { callback() } } return true } // Find opens a prompt and searches forward for the input func (h *BufPane) Find() bool { return h.find(true) } // FindLiteral is the same as Find() but does not support regular expressions func (h *BufPane) FindLiteral() bool { return h.find(false) } // Search searches for a given string/regex in the buffer and selects the next // match if a match is found // This function behaves the same way as Find and FindLiteral actions: // it affects the buffer's LastSearch and LastSearchRegex (saved searches) // for use with FindNext and FindPrevious, and turns HighlightSearch on or off // according to hlsearch setting func (h *BufPane) Search(str string, useRegex bool, searchDown bool) error { match, found, err := h.Buf.FindNext(str, h.Buf.Start(), h.Buf.End(), h.Cursor.Loc, searchDown, useRegex) if err != nil { return err } if found { h.Cursor.SetSelectionStart(match[0]) h.Cursor.SetSelectionEnd(match[1]) h.Cursor.OrigSelection[0] = h.Cursor.CurSelection[0] h.Cursor.OrigSelection[1] = h.Cursor.CurSelection[1] h.GotoLoc(h.Cursor.CurSelection[1]) h.Buf.LastSearch = str h.Buf.LastSearchRegex = useRegex h.Buf.HighlightSearch = h.Buf.Settings["hlsearch"].(bool) } else { h.Cursor.ResetSelection() } return nil } func (h *BufPane) find(useRegex bool) bool { h.searchOrig = h.Cursor.Loc prompt := "Find: " if useRegex { prompt = "Find (regex): " } var eventCallback func(resp string) if h.Buf.Settings["incsearch"].(bool) { eventCallback = func(resp string) { match, found, _ := h.Buf.FindNext(resp, h.Buf.Start(), h.Buf.End(), h.searchOrig, true, useRegex) if found { h.Cursor.SetSelectionStart(match[0]) h.Cursor.SetSelectionEnd(match[1]) h.Cursor.OrigSelection[0] = h.Cursor.CurSelection[0] h.Cursor.OrigSelection[1] = h.Cursor.CurSelection[1] h.GotoLoc(match[1]) } else { h.GotoLoc(h.searchOrig) h.Cursor.ResetSelection() } } } findCallback := func(resp string, canceled bool) { // Finished callback if !canceled { match, found, err := h.Buf.FindNext(resp, h.Buf.Start(), h.Buf.End(), h.searchOrig, true, useRegex) if err != nil { InfoBar.Error(err) } if found { h.Cursor.SetSelectionStart(match[0]) h.Cursor.SetSelectionEnd(match[1]) h.Cursor.OrigSelection[0] = h.Cursor.CurSelection[0] h.Cursor.OrigSelection[1] = h.Cursor.CurSelection[1] h.GotoLoc(h.Cursor.CurSelection[1]) h.Buf.LastSearch = resp h.Buf.LastSearchRegex = useRegex h.Buf.HighlightSearch = h.Buf.Settings["hlsearch"].(bool) } else { h.Cursor.ResetSelection() InfoBar.Message("No matches found") } } else { h.Cursor.ResetSelection() } } pattern := string(h.Cursor.GetSelection()) if useRegex && pattern != "" { pattern = regexp.QuoteMeta(pattern) } if eventCallback != nil && pattern != "" { eventCallback(pattern) } InfoBar.Prompt(prompt, pattern, "Find", eventCallback, findCallback) if pattern != "" { InfoBar.SelectAll() } return true } // ToggleHighlightSearch toggles highlighting all instances of the last used search term func (h *BufPane) ToggleHighlightSearch() bool { h.Buf.HighlightSearch = !h.Buf.HighlightSearch return true } // UnhighlightSearch unhighlights all instances of the last used search term func (h *BufPane) UnhighlightSearch() bool { if !h.Buf.HighlightSearch { return false } h.Buf.HighlightSearch = false return true } // ResetSearch resets the last used search term func (h *BufPane) ResetSearch() bool { if h.Buf.LastSearch != "" { h.Buf.LastSearch = "" return true } return false } // FindNext searches forwards for the last used search term func (h *BufPane) FindNext() bool { if h.Buf.LastSearch == "" { return false } // If the cursor is at the start of a selection and we search we want // to search from the end of the selection in the case that // the selection is a search result in which case we wouldn't move at // at all which would be bad searchLoc := h.Cursor.Loc if h.Cursor.HasSelection() { searchLoc = h.Cursor.CurSelection[1] } match, found, err := h.Buf.FindNext(h.Buf.LastSearch, h.Buf.Start(), h.Buf.End(), searchLoc, true, h.Buf.LastSearchRegex) if err != nil { InfoBar.Error(err) } if found { h.Cursor.SetSelectionStart(match[0]) h.Cursor.SetSelectionEnd(match[1]) h.Cursor.OrigSelection[0] = h.Cursor.CurSelection[0] h.Cursor.OrigSelection[1] = h.Cursor.CurSelection[1] h.GotoLoc(h.Cursor.CurSelection[1]) } else { h.Cursor.ResetSelection() } return true } // FindPrevious searches backwards for the last used search term func (h *BufPane) FindPrevious() bool { if h.Buf.LastSearch == "" { return false } // If the cursor is at the end of a selection and we search we want // to search from the beginning of the selection in the case that // the selection is a search result in which case we wouldn't move at // at all which would be bad searchLoc := h.Cursor.Loc if h.Cursor.HasSelection() { searchLoc = h.Cursor.CurSelection[0] } match, found, err := h.Buf.FindNext(h.Buf.LastSearch, h.Buf.Start(), h.Buf.End(), searchLoc, false, h.Buf.LastSearchRegex) if err != nil { InfoBar.Error(err) } if found { h.Cursor.SetSelectionStart(match[0]) h.Cursor.SetSelectionEnd(match[1]) h.Cursor.OrigSelection[0] = h.Cursor.CurSelection[0] h.Cursor.OrigSelection[1] = h.Cursor.CurSelection[1] h.GotoLoc(h.Cursor.CurSelection[1]) } else { h.Cursor.ResetSelection() } return true } // DiffNext searches forward until the beginning of the next block of diffs func (h *BufPane) DiffNext() bool { cur := h.Cursor.Loc.Y dl, err := h.Buf.FindNextDiffLine(cur, true) if err != nil { return false } h.GotoLoc(buffer.Loc{0, dl}) return true } // DiffPrevious searches forward until the end of the previous block of diffs func (h *BufPane) DiffPrevious() bool { cur := h.Cursor.Loc.Y dl, err := h.Buf.FindNextDiffLine(cur, false) if err != nil { return false } h.GotoLoc(buffer.Loc{0, dl}) return true } // Undo undoes the last action func (h *BufPane) Undo() bool { if !h.Buf.Undo() { return false } InfoBar.Message("Undid action") h.Relocate() return true } // Redo redoes the last action func (h *BufPane) Redo() bool { if !h.Buf.Redo() { return false } InfoBar.Message("Redid action") h.Relocate() return true } // Copy the selection to the system clipboard func (h *BufPane) Copy() bool { if h.Cursor.HasSelection() { h.Cursor.CopySelection(clipboard.ClipboardReg) h.freshClip = true InfoBar.Message("Copied selection") } h.Relocate() return true } // CopyLine copies the current line to the clipboard func (h *BufPane) CopyLine() bool { if h.Cursor.HasSelection() { return false } origLoc := h.Cursor.Loc h.Cursor.SelectLine() h.Cursor.CopySelection(clipboard.ClipboardReg) h.freshClip = true InfoBar.Message("Copied line") h.Cursor.Deselect(true) h.Cursor.Loc = origLoc h.Relocate() return true } // CutLine cuts the current line to the clipboard func (h *BufPane) CutLine() bool { h.Cursor.SelectLine() if !h.Cursor.HasSelection() { return false } if h.freshClip { if h.Cursor.HasSelection() { if clip, err := clipboard.Read(clipboard.ClipboardReg); err != nil { InfoBar.Error(err) } else { clipboard.WriteMulti(clip+string(h.Cursor.GetSelection()), clipboard.ClipboardReg, h.Cursor.Num, h.Buf.NumCursors()) } } } else if time.Since(h.lastCutTime)/time.Second > 10*time.Second || !h.freshClip { h.Copy() } h.freshClip = true h.lastCutTime = time.Now() h.Cursor.DeleteSelection() h.Cursor.ResetSelection() InfoBar.Message("Cut line") h.Relocate() return true } // Cut the selection to the system clipboard func (h *BufPane) Cut() bool { if h.Cursor.HasSelection() { h.Cursor.CopySelection(clipboard.ClipboardReg) h.Cursor.DeleteSelection() h.Cursor.ResetSelection() h.freshClip = true InfoBar.Message("Cut selection") h.Relocate() return true } return h.CutLine() } // DuplicateLine duplicates the current line or selection func (h *BufPane) DuplicateLine() bool { var infoMessage = "Duplicated line" if h.Cursor.HasSelection() { infoMessage = "Duplicated selection" h.Buf.Insert(h.Cursor.CurSelection[1], string(h.Cursor.GetSelection())) } else { h.Cursor.End() h.Buf.Insert(h.Cursor.Loc, "\n"+string(h.Buf.LineBytes(h.Cursor.Y))) // h.Cursor.Right() } InfoBar.Message(infoMessage) h.Relocate() return true } // DeleteLine deletes the current line func (h *BufPane) DeleteLine() bool { h.Cursor.SelectLine() if !h.Cursor.HasSelection() { return false } h.Cursor.DeleteSelection() h.Cursor.ResetSelection() InfoBar.Message("Deleted line") h.Relocate() return true } // MoveLinesUp moves up the current line or selected lines if any func (h *BufPane) MoveLinesUp() bool { if h.Cursor.HasSelection() { if h.Cursor.CurSelection[0].Y == 0 { InfoBar.Message("Cannot move further up") return false } start := h.Cursor.CurSelection[0].Y end := h.Cursor.CurSelection[1].Y sel := 1 if start > end { end, start = start, end sel = 0 } compensate := false if h.Cursor.CurSelection[sel].X != 0 { end++ } else { compensate = true } h.Buf.MoveLinesUp( start, end, ) if compensate { h.Cursor.CurSelection[sel].Y -= 1 } } else { if h.Cursor.Loc.Y == 0 { InfoBar.Message("Cannot move further up") return false } h.Buf.MoveLinesUp( h.Cursor.Loc.Y, h.Cursor.Loc.Y+1, ) } h.Relocate() return true } // MoveLinesDown moves down the current line or selected lines if any func (h *BufPane) MoveLinesDown() bool { if h.Cursor.HasSelection() { if h.Cursor.CurSelection[1].Y >= h.Buf.LinesNum() { InfoBar.Message("Cannot move further down") return false } start := h.Cursor.CurSelection[0].Y end := h.Cursor.CurSelection[1].Y sel := 1 if start > end { end, start = start, end sel = 0 } if h.Cursor.CurSelection[sel].X != 0 { end++ } h.Buf.MoveLinesDown( start, end, ) } else { if h.Cursor.Loc.Y >= h.Buf.LinesNum()-1 { InfoBar.Message("Cannot move further down") return false } h.Buf.MoveLinesDown( h.Cursor.Loc.Y, h.Cursor.Loc.Y+1, ) } h.Relocate() return true } // Paste whatever is in the system clipboard into the buffer // Delete and paste if the user has a selection func (h *BufPane) Paste() bool { clip, err := clipboard.ReadMulti(clipboard.ClipboardReg, h.Cursor.Num, h.Buf.NumCursors()) if err != nil { InfoBar.Error(err) } else { h.paste(clip) } h.Relocate() return true } // PastePrimary pastes from the primary clipboard (only use on linux) func (h *BufPane) PastePrimary() bool { clip, err := clipboard.ReadMulti(clipboard.PrimaryReg, h.Cursor.Num, h.Buf.NumCursors()) if err != nil { InfoBar.Error(err) } else { h.paste(clip) } h.Relocate() return true } func (h *BufPane) paste(clip string) { if h.Buf.Settings["smartpaste"].(bool) { if h.Cursor.X > 0 { leadingPasteWS := string(util.GetLeadingWhitespace([]byte(clip))) if leadingPasteWS != " " && strings.Contains(clip, "\n"+leadingPasteWS) { leadingWS := string(util.GetLeadingWhitespace(h.Buf.LineBytes(h.Cursor.Y))) clip = strings.TrimPrefix(clip, leadingPasteWS) clip = strings.ReplaceAll(clip, "\n"+leadingPasteWS, "\n"+leadingWS) } } } if h.Cursor.HasSelection() { h.Cursor.DeleteSelection() h.Cursor.ResetSelection() } h.Buf.Insert(h.Cursor.Loc, clip) // h.Cursor.Loc = h.Cursor.Loc.Move(Count(clip), h.Buf) h.freshClip = false InfoBar.Message("Pasted clipboard") } // JumpToMatchingBrace moves the cursor to the matching brace if it is // currently on a brace func (h *BufPane) JumpToMatchingBrace() bool { matchingBrace, left, found := h.Buf.FindMatchingBrace(h.Cursor.Loc) if found { if h.Buf.Settings["matchbraceleft"].(bool) { if left { h.Cursor.GotoLoc(matchingBrace) } else { h.Cursor.GotoLoc(matchingBrace.Move(1, h.Buf)) } } else { h.Cursor.GotoLoc(matchingBrace) } h.Relocate() return true } return false } // SelectAll selects the entire buffer func (h *BufPane) SelectAll() bool { h.Cursor.SetSelectionStart(h.Buf.Start()) h.Cursor.SetSelectionEnd(h.Buf.End()) // Put the cursor at the beginning h.Cursor.X = 0 h.Cursor.Y = 0 h.Relocate() return true } // OpenFile opens a new file in the buffer func (h *BufPane) OpenFile() bool { InfoBar.Prompt("> ", "open ", "Open", nil, func(resp string, canceled bool) { if !canceled { h.HandleCommand(resp) } }) return true } // JumpLine asks the user to enter a line number to jump to func (h *BufPane) JumpLine() bool { InfoBar.Prompt("> ", "goto ", "Command", nil, func(resp string, canceled bool) { if !canceled { h.HandleCommand(resp) } }) return true } // Start moves the viewport to the start of the buffer func (h *BufPane) Start() bool { v := h.GetView() v.StartLine = display.SLoc{0, 0} h.SetView(v) return true } // End moves the viewport to the end of the buffer func (h *BufPane) End() bool { v := h.GetView() v.StartLine = h.Scroll(h.SLocFromLoc(h.Buf.End()), -h.BufView().Height+1) h.SetView(v) return true } // PageUp scrolls the view up a page func (h *BufPane) PageUp() bool { h.ScrollUp(h.BufView().Height) return true } // PageDown scrolls the view down a page func (h *BufPane) PageDown() bool { h.ScrollDown(h.BufView().Height) h.ScrollAdjust() return true } // SelectPageUp selects up one page func (h *BufPane) SelectPageUp() bool { if !h.Cursor.HasSelection() { h.Cursor.OrigSelection[0] = h.Cursor.Loc } h.MoveCursorUp(h.BufView().Height) h.Cursor.SelectTo(h.Cursor.Loc) h.Relocate() return true } // SelectPageDown selects down one page func (h *BufPane) SelectPageDown() bool { if !h.Cursor.HasSelection() { h.Cursor.OrigSelection[0] = h.Cursor.Loc } h.MoveCursorDown(h.BufView().Height) h.Cursor.SelectTo(h.Cursor.Loc) h.Relocate() return true } // CursorPageUp places the cursor a page up func (h *BufPane) CursorPageUp() bool { h.Cursor.Deselect(true) if h.Cursor.HasSelection() { h.Cursor.Loc = h.Cursor.CurSelection[0] h.Cursor.ResetSelection() h.Cursor.StoreVisualX() } h.MoveCursorUp(h.BufView().Height) h.Relocate() return true } // CursorPageDown places the cursor a page up func (h *BufPane) CursorPageDown() bool { h.Cursor.Deselect(false) if h.Cursor.HasSelection() { h.Cursor.Loc = h.Cursor.CurSelection[1] h.Cursor.ResetSelection() h.Cursor.StoreVisualX() } h.MoveCursorDown(h.BufView().Height) h.Relocate() return true } // HalfPageUp scrolls the view up half a page func (h *BufPane) HalfPageUp() bool { h.ScrollUp(h.BufView().Height / 2) return true } // HalfPageDown scrolls the view down half a page func (h *BufPane) HalfPageDown() bool { h.ScrollDown(h.BufView().Height / 2) h.ScrollAdjust() return true } // ToggleDiffGutter turns the diff gutter off and on func (h *BufPane) ToggleDiffGutter() bool { if !h.Buf.Settings["diffgutter"].(bool) { h.Buf.Settings["diffgutter"] = true h.Buf.UpdateDiff() InfoBar.Message("Enabled diff gutter") } else { h.Buf.Settings["diffgutter"] = false InfoBar.Message("Disabled diff gutter") } return true } // ToggleRuler turns line numbers off and on func (h *BufPane) ToggleRuler() bool { if !h.Buf.Settings["ruler"].(bool) { h.Buf.Settings["ruler"] = true InfoBar.Message("Enabled ruler") } else { h.Buf.Settings["ruler"] = false InfoBar.Message("Disabled ruler") } return true } // ClearStatus clears the infobar. It is an alias for ClearInfo. func (h *BufPane) ClearStatus() bool { return h.ClearInfo() } // ToggleHelp toggles the help screen func (h *BufPane) ToggleHelp() bool { if h.Buf.Type == buffer.BTHelp { h.Quit() } else { h.openHelp("help") } return true } // ToggleKeyMenu toggles the keymenu option and resizes all tabs func (h *BufPane) ToggleKeyMenu() bool { config.GlobalSettings["keymenu"] = !config.GetGlobalOption("keymenu").(bool) Tabs.Resize() return true } // ShellMode opens a terminal to run a shell command func (h *BufPane) ShellMode() bool { InfoBar.Prompt("$ ", "", "Shell", nil, func(resp string, canceled bool) { if !canceled { // The true here is for openTerm to make the command interactive shell.RunInteractiveShell(resp, true, false) } }) return true } // CommandMode lets the user enter a command func (h *BufPane) CommandMode() bool { InfoBar.Prompt("> ", "", "Command", nil, func(resp string, canceled bool) { if !canceled { h.HandleCommand(resp) } }) return true } // ToggleOverwriteMode lets the user toggle the text overwrite mode func (h *BufPane) ToggleOverwriteMode() bool { h.isOverwriteMode = !h.isOverwriteMode return true } // Escape leaves current mode func (h *BufPane) Escape() bool { return true } // Deselect deselects on the current cursor func (h *BufPane) Deselect() bool { if !h.Cursor.HasSelection() { return false } h.Cursor.Deselect(true) return true } // ClearInfo clears the infobar func (h *BufPane) ClearInfo() bool { if InfoBar.Msg == "" { return false } InfoBar.Message("") return true } // ForceQuit closes the current tab or view even if there are unsaved changes // (no prompt) func (h *BufPane) ForceQuit() bool { h.Buf.Close() if len(MainTab().Panes) > 1 { h.Unsplit() } else if len(Tabs.List) > 1 { Tabs.RemoveTab(h.splitID) } else { screen.Screen.Fini() InfoBar.Close() runtime.Goexit() } return true } // Quit this will close the current tab or view that is open func (h *BufPane) Quit() bool { if h.Buf.Modified() { if config.GlobalSettings["autosave"].(float64) > 0 { // autosave on means we automatically save when quitting h.SaveCB("Quit", func() { h.ForceQuit() }) } else { InfoBar.YNPrompt("Save changes to "+h.Buf.GetName()+" before closing? (y,n,esc)", func(yes, canceled bool) { if !canceled && !yes { h.ForceQuit() } else if !canceled && yes { h.SaveCB("Quit", func() { h.ForceQuit() }) } }) } } else { h.ForceQuit() } return true } // QuitAll quits the whole editor; all splits and tabs func (h *BufPane) QuitAll() bool { anyModified := false for _, b := range buffer.OpenBuffers { if b.Modified() { anyModified = true break } } quit := func() { buffer.CloseOpenBuffers() screen.Screen.Fini() InfoBar.Close() runtime.Goexit() } if anyModified { InfoBar.YNPrompt("Quit micro? (all open buffers will be closed without saving)", func(yes, canceled bool) { if !canceled && yes { quit() } }) } else { quit() } return true } // AddTab adds a new tab with an empty buffer func (h *BufPane) AddTab() bool { width, height := screen.Screen.Size() iOffset := config.GetInfoBarOffset() b := buffer.NewBufferFromString("", "", buffer.BTDefault) tp := NewTabFromBuffer(0, 0, width, height-iOffset, b) Tabs.AddTab(tp) Tabs.SetActive(len(Tabs.List) - 1) return true } // PreviousTab switches to the previous tab in the tab list func (h *BufPane) PreviousTab() bool { tabsLen := len(Tabs.List) if tabsLen == 1 { return false } a := Tabs.Active() + tabsLen Tabs.SetActive((a - 1) % tabsLen) return true } // NextTab switches to the next tab in the tab list func (h *BufPane) NextTab() bool { tabsLen := len(Tabs.List) if tabsLen == 1 { return false } a := Tabs.Active() Tabs.SetActive((a + 1) % tabsLen) return true } // VSplitAction opens an empty vertical split func (h *BufPane) VSplitAction() bool { h.VSplitBuf(buffer.NewBufferFromString("", "", buffer.BTDefault)) return true } // HSplitAction opens an empty horizontal split func (h *BufPane) HSplitAction() bool { h.HSplitBuf(buffer.NewBufferFromString("", "", buffer.BTDefault)) return true } // Unsplit closes all splits in the current tab except the active one func (h *BufPane) Unsplit() bool { tab := h.tab n := tab.GetNode(h.splitID) ok := n.Unsplit() if ok { tab.RemovePane(tab.GetPane(h.splitID)) tab.Resize() tab.SetActive(len(tab.Panes) - 1) return true } return false } // NextSplit changes the view to the next split func (h *BufPane) NextSplit() bool { if len(h.tab.Panes) == 1 { return false } a := h.tab.active if a < len(h.tab.Panes)-1 { a++ } else { a = 0 } h.tab.SetActive(a) return true } // PreviousSplit changes the view to the previous split func (h *BufPane) PreviousSplit() bool { if len(h.tab.Panes) == 1 { return false } a := h.tab.active if a > 0 { a-- } else { a = len(h.tab.Panes) - 1 } h.tab.SetActive(a) return true } var curmacro []interface{} var recordingMacro bool // ToggleMacro toggles recording of a macro func (h *BufPane) ToggleMacro() bool { recordingMacro = !recordingMacro if recordingMacro { curmacro = []interface{}{} InfoBar.Message("Recording") } else { InfoBar.Message("Stopped recording") } h.Relocate() return true } // PlayMacro plays back the most recently recorded macro func (h *BufPane) PlayMacro() bool { if recordingMacro { return false } for _, action := range curmacro { switch t := action.(type) { case rune: h.DoRuneInsert(t) case BufKeyAction: t(h) } } h.Relocate() return true } // SpawnMultiCursor creates a new multiple cursor at the next occurrence of the current selection or current word func (h *BufPane) SpawnMultiCursor() bool { spawner := h.Buf.GetCursor(h.Buf.NumCursors() - 1) if !spawner.HasSelection() { spawner.SelectWord() h.multiWord = true h.Relocate() return true } sel := spawner.GetSelection() searchStart := spawner.CurSelection[1] search := string(sel) search = regexp.QuoteMeta(search) if h.multiWord { search = "\\b" + search + "\\b" } match, found, err := h.Buf.FindNext(search, h.Buf.Start(), h.Buf.End(), searchStart, true, true) if err != nil { InfoBar.Error(err) } if found { c := buffer.NewCursor(h.Buf, buffer.Loc{}) c.SetSelectionStart(match[0]) c.SetSelectionEnd(match[1]) c.OrigSelection[0] = c.CurSelection[0] c.OrigSelection[1] = c.CurSelection[1] c.Loc = c.CurSelection[1] h.Buf.AddCursor(c) h.Buf.SetCurCursor(h.Buf.NumCursors() - 1) h.Buf.MergeCursors() } else { InfoBar.Message("No matches found") } h.Relocate() return true } // SpawnMultiCursorUpN is not an action func (h *BufPane) SpawnMultiCursorUpN(n int) bool { lastC := h.Buf.GetCursor(h.Buf.NumCursors() - 1) var c *buffer.Cursor if !h.Buf.Settings["softwrap"].(bool) { if n > 0 && lastC.Y == 0 { return false } if n < 0 && lastC.Y+1 == h.Buf.LinesNum() { return false } h.Buf.DeselectCursors() c = buffer.NewCursor(h.Buf, buffer.Loc{lastC.X, lastC.Y - n}) c.LastVisualX = lastC.LastVisualX c.X = c.GetCharPosInLine(h.Buf.LineBytes(c.Y), c.LastVisualX) c.Relocate() } else { vloc := h.VLocFromLoc(lastC.Loc) sloc := h.Scroll(vloc.SLoc, -n) if sloc == vloc.SLoc { return false } h.Buf.DeselectCursors() vloc.SLoc = sloc vloc.VisualX = lastC.LastVisualX c = buffer.NewCursor(h.Buf, h.LocFromVLoc(vloc)) c.LastVisualX = lastC.LastVisualX } h.Buf.AddCursor(c) h.Buf.SetCurCursor(h.Buf.NumCursors() - 1) h.Buf.MergeCursors() h.Relocate() return true } // SpawnMultiCursorUp creates additional cursor, at the same X (if possible), one Y less. func (h *BufPane) SpawnMultiCursorUp() bool { return h.SpawnMultiCursorUpN(1) } // SpawnMultiCursorDown creates additional cursor, at the same X (if possible), one Y more. func (h *BufPane) SpawnMultiCursorDown() bool { return h.SpawnMultiCursorUpN(-1) } // SpawnMultiCursorSelect adds a cursor at the beginning of each line of a selection func (h *BufPane) SpawnMultiCursorSelect() bool { // Avoid cases where multiple cursors already exist, that would create problems if h.Buf.NumCursors() > 1 { return false } var startLine int var endLine int a, b := h.Cursor.CurSelection[0].Y, h.Cursor.CurSelection[1].Y if a > b { startLine, endLine = b, a } else { startLine, endLine = a, b } if h.Cursor.HasSelection() { h.Cursor.ResetSelection() h.Cursor.GotoLoc(buffer.Loc{0, startLine}) for i := startLine; i <= endLine; i++ { c := buffer.NewCursor(h.Buf, buffer.Loc{0, i}) c.StoreVisualX() h.Buf.AddCursor(c) } h.Buf.MergeCursors() } else { return false } InfoBar.Message("Added cursors from selection") return true } // MouseMultiCursor is a mouse action which puts a new cursor at the mouse position, // or removes a cursor if it is already there func (h *BufPane) MouseMultiCursor(e *tcell.EventMouse) bool { b := h.Buf mx, my := e.Position() // ignore click on the status line if my >= h.BufView().Y+h.BufView().Height { return false } mouseLoc := h.LocFromVisual(buffer.Loc{X: mx, Y: my}) if h.Buf.NumCursors() > 1 { cursors := h.Buf.GetCursors() for _, c := range cursors { if c.Loc == mouseLoc { h.Buf.RemoveCursor(c.Num) return true } } } c := buffer.NewCursor(b, mouseLoc) b.AddCursor(c) b.MergeCursors() return true } // SkipMultiCursor moves the current multiple cursor to the next available position func (h *BufPane) SkipMultiCursor() bool { lastC := h.Buf.GetCursor(h.Buf.NumCursors() - 1) if !lastC.HasSelection() { return false } sel := lastC.GetSelection() searchStart := lastC.CurSelection[1] search := string(sel) search = regexp.QuoteMeta(search) if h.multiWord { search = "\\b" + search + "\\b" } match, found, err := h.Buf.FindNext(search, h.Buf.Start(), h.Buf.End(), searchStart, true, true) if err != nil { InfoBar.Error(err) } if found { lastC.SetSelectionStart(match[0]) lastC.SetSelectionEnd(match[1]) lastC.OrigSelection[0] = lastC.CurSelection[0] lastC.OrigSelection[1] = lastC.CurSelection[1] lastC.Loc = lastC.CurSelection[1] h.Buf.MergeCursors() h.Buf.SetCurCursor(h.Buf.NumCursors() - 1) } else { InfoBar.Message("No matches found") } h.Relocate() return true } // RemoveMultiCursor removes the latest multiple cursor func (h *BufPane) RemoveMultiCursor() bool { if h.Buf.NumCursors() > 1 { h.Buf.RemoveCursor(h.Buf.NumCursors() - 1) h.Buf.SetCurCursor(h.Buf.NumCursors() - 1) h.Buf.UpdateCursors() } else if h.multiWord { h.multiWord = false h.Cursor.Deselect(true) } else { return false } h.Relocate() return true } // RemoveAllMultiCursors removes all cursors except the base cursor func (h *BufPane) RemoveAllMultiCursors() bool { if h.Buf.NumCursors() > 1 || h.multiWord { h.Buf.ClearCursors() h.multiWord = false } else { return false } h.Relocate() return true } // None is an action that does nothing func (h *BufPane) None() bool { return true } micro-2.0.14/internal/action/actions_other.go0000664000175000017510000000023014663411671020525 0ustar nileshnilesh// +build plan9 nacl windows package action func (*BufPane) Suspend() bool { InfoBar.Error("Suspend is only supported on BSD/Linux") return false } micro-2.0.14/internal/action/actions_posix.go0000664000175000017510000000115014663411671020550 0ustar nileshnilesh// +build linux darwin dragonfly solaris openbsd netbsd freebsd package action import ( "syscall" "github.com/zyedidia/micro/v2/internal/screen" ) // Suspend sends micro to the background. This is the same as pressing CtrlZ in most unix programs. // This only works on linux and has no default binding. // This code was adapted from the suspend code in nsf/godit func (*BufPane) Suspend() bool { screenb := screen.TempFini() // suspend the process pid := syscall.Getpid() err := syscall.Kill(pid, syscall.SIGSTOP) if err != nil { screen.TermMessage(err) } screen.TempStart(screenb) return false } micro-2.0.14/internal/action/bindings.go0000664000175000017510000003072514663411671017475 0ustar nileshnileshpackage action import ( "encoding/json" "errors" "fmt" "io/ioutil" "os" "path/filepath" "regexp" "strings" "unicode" "github.com/zyedidia/json5" "github.com/zyedidia/micro/v2/internal/config" "github.com/zyedidia/micro/v2/internal/screen" "github.com/zyedidia/tcell/v2" ) var Binder = map[string]func(e Event, action string){ "command": InfoMapEvent, "buffer": BufMapEvent, "terminal": TermMapEvent, } func createBindingsIfNotExist(fname string) { if _, e := os.Stat(fname); os.IsNotExist(e) { ioutil.WriteFile(fname, []byte("{}"), 0644) } } // InitBindings intializes the bindings map by reading from bindings.json func InitBindings() { var parsed map[string]interface{} filename := filepath.Join(config.ConfigDir, "bindings.json") createBindingsIfNotExist(filename) if _, e := os.Stat(filename); e == nil { input, err := ioutil.ReadFile(filename) if err != nil { screen.TermMessage("Error reading bindings.json file: " + err.Error()) return } err = json5.Unmarshal(input, &parsed) if err != nil { screen.TermMessage("Error reading bindings.json:", err.Error()) } } for p, bind := range Binder { defaults := DefaultBindings(p) for k, v := range defaults { BindKey(k, v, bind) } } for k, v := range parsed { switch val := v.(type) { case string: BindKey(k, val, Binder["buffer"]) case map[string]interface{}: bind, ok := Binder[k] if !ok || bind == nil { screen.TermMessage(fmt.Sprintf("%s is not a valid pane type", k)) continue } for e, a := range val { s, ok := a.(string) if !ok { screen.TermMessage("Error reading bindings.json: non-string and non-map entry", k) } else { BindKey(e, s, bind) } } default: screen.TermMessage("Error reading bindings.json: non-string and non-map entry", k) } } } func BindKey(k, v string, bind func(e Event, a string)) { event, err := findEvent(k) if err != nil { screen.TermMessage(err) return } if strings.HasPrefix(k, "\x1b") { screen.Screen.RegisterRawSeq(k) } bind(event, v) // switch e := event.(type) { // case KeyEvent: // InfoMapKey(e, v) // case KeySequenceEvent: // InfoMapKey(e, v) // case MouseEvent: // InfoMapMouse(e, v) // case RawEvent: // InfoMapKey(e, v) // } } var r = regexp.MustCompile("<(.+?)>") func findEvents(k string) (b KeySequenceEvent, ok bool, err error) { var events []Event = nil for len(k) > 0 { groups := r.FindStringSubmatchIndex(k) if len(groups) > 3 { if events == nil { events = make([]Event, 0, 3) } e, ok := findSingleEvent(k[groups[2]:groups[3]]) if !ok { return KeySequenceEvent{}, false, errors.New("Invalid event " + k[groups[2]:groups[3]]) } events = append(events, e) k = k[groups[3]+1:] } else { return KeySequenceEvent{}, false, nil } } return KeySequenceEvent{events}, true, nil } // findSingleEvent will find binding Key 'b' using string 'k' func findSingleEvent(k string) (b Event, ok bool) { modifiers := tcell.ModNone // First, we'll strip off all the modifiers in the name and add them to the // ModMask modSearch: for { switch { case strings.HasPrefix(k, "-") && k != "-": // We optionally support dashes between modifiers k = k[1:] case strings.HasPrefix(k, "Ctrl") && k != "CtrlH": // CtrlH technically does not have a 'Ctrl' modifier because it is really backspace k = k[4:] modifiers |= tcell.ModCtrl case strings.HasPrefix(k, "Alt"): k = k[3:] modifiers |= tcell.ModAlt case strings.HasPrefix(k, "Shift"): k = k[5:] modifiers |= tcell.ModShift case strings.HasPrefix(k, "\x1b"): return RawEvent{ esc: k, }, true default: break modSearch } } if k == "" { return KeyEvent{}, false } // Control is handled in a special way, since the terminal sends explicitly // marked escape sequences for control keys // We should check for Control keys first if modifiers&tcell.ModCtrl != 0 { // see if the key is in bindingKeys with the Ctrl prefix. k = string(unicode.ToUpper(rune(k[0]))) + k[1:] if code, ok := keyEvents["Ctrl"+k]; ok { return KeyEvent{ code: code, mod: modifiers, }, true } } // See if we can find the key in bindingKeys if code, ok := keyEvents[k]; ok { return KeyEvent{ code: code, mod: modifiers, }, true } var mstate MouseState = MousePress if strings.HasSuffix(k, "Drag") { k = k[:len(k)-4] mstate = MouseDrag } else if strings.HasSuffix(k, "Release") { k = k[:len(k)-7] mstate = MouseRelease } // See if we can find the key in bindingMouse if code, ok := mouseEvents[k]; ok { return MouseEvent{ btn: code, mod: modifiers, state: mstate, }, true } // If we were given one character, then we've got a rune. if len(k) == 1 { return KeyEvent{ code: tcell.KeyRune, mod: modifiers, r: rune(k[0]), }, true } // We don't know what happened. return KeyEvent{}, false } func findEvent(k string) (Event, error) { var event Event event, ok, err := findEvents(k) if err != nil { return nil, err } if !ok { event, ok = findSingleEvent(k) if !ok { return nil, errors.New(k + " is not a bindable event") } } return event, nil } func eventsEqual(e1 Event, e2 Event) bool { seq1, ok1 := e1.(KeySequenceEvent) seq2, ok2 := e2.(KeySequenceEvent) if ok1 && ok2 { if len(seq1.keys) != len(seq2.keys) { return false } for i := 0; i < len(seq1.keys); i++ { if seq1.keys[i] != seq2.keys[i] { return false } } return true } return e1 == e2 } // TryBindKey tries to bind a key by writing to config.ConfigDir/bindings.json // Returns true if the keybinding already existed and a possible error func TryBindKey(k, v string, overwrite bool) (bool, error) { var e error var parsed map[string]interface{} filename := filepath.Join(config.ConfigDir, "bindings.json") createBindingsIfNotExist(filename) if _, e = os.Stat(filename); e == nil { input, err := ioutil.ReadFile(filename) if err != nil { return false, errors.New("Error reading bindings.json file: " + err.Error()) } err = json5.Unmarshal(input, &parsed) if err != nil { return false, errors.New("Error reading bindings.json: " + err.Error()) } key, err := findEvent(k) if err != nil { return false, err } found := false var ev string for ev = range parsed { if e, err := findEvent(ev); err == nil { if eventsEqual(e, key) { found = true break } } } if found { if overwrite { parsed[ev] = v } else { return true, nil } } else { parsed[k] = v } BindKey(k, v, Binder["buffer"]) txt, _ := json.MarshalIndent(parsed, "", " ") return true, ioutil.WriteFile(filename, append(txt, '\n'), 0644) } return false, e } // UnbindKey removes the binding for a key from the bindings.json file func UnbindKey(k string) error { var e error var parsed map[string]interface{} filename := filepath.Join(config.ConfigDir, "bindings.json") createBindingsIfNotExist(filename) if _, e = os.Stat(filename); e == nil { input, err := ioutil.ReadFile(filename) if err != nil { return errors.New("Error reading bindings.json file: " + err.Error()) } err = json5.Unmarshal(input, &parsed) if err != nil { return errors.New("Error reading bindings.json: " + err.Error()) } key, err := findEvent(k) if err != nil { return err } for ev := range parsed { if e, err := findEvent(ev); err == nil { if eventsEqual(e, key) { delete(parsed, ev) break } } } if strings.HasPrefix(k, "\x1b") { screen.Screen.UnregisterRawSeq(k) } defaults := DefaultBindings("buffer") if a, ok := defaults[k]; ok { BindKey(k, a, Binder["buffer"]) } else if _, ok := config.Bindings["buffer"][k]; ok { BufUnmap(key) delete(config.Bindings["buffer"], k) } txt, _ := json.MarshalIndent(parsed, "", " ") return ioutil.WriteFile(filename, append(txt, '\n'), 0644) } return e } var mouseEvents = map[string]tcell.ButtonMask{ "MouseLeft": tcell.ButtonPrimary, "MouseMiddle": tcell.ButtonMiddle, "MouseRight": tcell.ButtonSecondary, "MouseWheelUp": tcell.WheelUp, "MouseWheelDown": tcell.WheelDown, "MouseWheelLeft": tcell.WheelLeft, "MouseWheelRight": tcell.WheelRight, } var keyEvents = map[string]tcell.Key{ "Up": tcell.KeyUp, "Down": tcell.KeyDown, "Right": tcell.KeyRight, "Left": tcell.KeyLeft, "UpLeft": tcell.KeyUpLeft, "UpRight": tcell.KeyUpRight, "DownLeft": tcell.KeyDownLeft, "DownRight": tcell.KeyDownRight, "Center": tcell.KeyCenter, "PageUp": tcell.KeyPgUp, "PageDown": tcell.KeyPgDn, "Home": tcell.KeyHome, "End": tcell.KeyEnd, "Insert": tcell.KeyInsert, "Delete": tcell.KeyDelete, "Help": tcell.KeyHelp, "Exit": tcell.KeyExit, "Clear": tcell.KeyClear, "Cancel": tcell.KeyCancel, "Print": tcell.KeyPrint, "Pause": tcell.KeyPause, "Backtab": tcell.KeyBacktab, "F1": tcell.KeyF1, "F2": tcell.KeyF2, "F3": tcell.KeyF3, "F4": tcell.KeyF4, "F5": tcell.KeyF5, "F6": tcell.KeyF6, "F7": tcell.KeyF7, "F8": tcell.KeyF8, "F9": tcell.KeyF9, "F10": tcell.KeyF10, "F11": tcell.KeyF11, "F12": tcell.KeyF12, "F13": tcell.KeyF13, "F14": tcell.KeyF14, "F15": tcell.KeyF15, "F16": tcell.KeyF16, "F17": tcell.KeyF17, "F18": tcell.KeyF18, "F19": tcell.KeyF19, "F20": tcell.KeyF20, "F21": tcell.KeyF21, "F22": tcell.KeyF22, "F23": tcell.KeyF23, "F24": tcell.KeyF24, "F25": tcell.KeyF25, "F26": tcell.KeyF26, "F27": tcell.KeyF27, "F28": tcell.KeyF28, "F29": tcell.KeyF29, "F30": tcell.KeyF30, "F31": tcell.KeyF31, "F32": tcell.KeyF32, "F33": tcell.KeyF33, "F34": tcell.KeyF34, "F35": tcell.KeyF35, "F36": tcell.KeyF36, "F37": tcell.KeyF37, "F38": tcell.KeyF38, "F39": tcell.KeyF39, "F40": tcell.KeyF40, "F41": tcell.KeyF41, "F42": tcell.KeyF42, "F43": tcell.KeyF43, "F44": tcell.KeyF44, "F45": tcell.KeyF45, "F46": tcell.KeyF46, "F47": tcell.KeyF47, "F48": tcell.KeyF48, "F49": tcell.KeyF49, "F50": tcell.KeyF50, "F51": tcell.KeyF51, "F52": tcell.KeyF52, "F53": tcell.KeyF53, "F54": tcell.KeyF54, "F55": tcell.KeyF55, "F56": tcell.KeyF56, "F57": tcell.KeyF57, "F58": tcell.KeyF58, "F59": tcell.KeyF59, "F60": tcell.KeyF60, "F61": tcell.KeyF61, "F62": tcell.KeyF62, "F63": tcell.KeyF63, "F64": tcell.KeyF64, "CtrlSpace": tcell.KeyCtrlSpace, "CtrlA": tcell.KeyCtrlA, "CtrlB": tcell.KeyCtrlB, "CtrlC": tcell.KeyCtrlC, "CtrlD": tcell.KeyCtrlD, "CtrlE": tcell.KeyCtrlE, "CtrlF": tcell.KeyCtrlF, "CtrlG": tcell.KeyCtrlG, "CtrlH": tcell.KeyCtrlH, "CtrlI": tcell.KeyCtrlI, "CtrlJ": tcell.KeyCtrlJ, "CtrlK": tcell.KeyCtrlK, "CtrlL": tcell.KeyCtrlL, "CtrlM": tcell.KeyCtrlM, "CtrlN": tcell.KeyCtrlN, "CtrlO": tcell.KeyCtrlO, "CtrlP": tcell.KeyCtrlP, "CtrlQ": tcell.KeyCtrlQ, "CtrlR": tcell.KeyCtrlR, "CtrlS": tcell.KeyCtrlS, "CtrlT": tcell.KeyCtrlT, "CtrlU": tcell.KeyCtrlU, "CtrlV": tcell.KeyCtrlV, "CtrlW": tcell.KeyCtrlW, "CtrlX": tcell.KeyCtrlX, "CtrlY": tcell.KeyCtrlY, "CtrlZ": tcell.KeyCtrlZ, "CtrlLeftSq": tcell.KeyCtrlLeftSq, "CtrlBackslash": tcell.KeyCtrlBackslash, "CtrlRightSq": tcell.KeyCtrlRightSq, "CtrlCarat": tcell.KeyCtrlCarat, "CtrlUnderscore": tcell.KeyCtrlUnderscore, "Tab": tcell.KeyTab, "Esc": tcell.KeyEsc, "Escape": tcell.KeyEscape, "Enter": tcell.KeyEnter, "Backspace": tcell.KeyBackspace2, "OldBackspace": tcell.KeyBackspace, // I renamed these keys to PageUp and PageDown but I don't want to break someone's keybindings "PgUp": tcell.KeyPgUp, "PgDown": tcell.KeyPgDn, } micro-2.0.14/internal/action/bufpane.go0000664000175000017510000006733714663411671017331 0ustar nileshnileshpackage action import ( "strings" "time" luar "layeh.com/gopher-luar" lua "github.com/yuin/gopher-lua" "github.com/zyedidia/micro/v2/internal/buffer" "github.com/zyedidia/micro/v2/internal/config" "github.com/zyedidia/micro/v2/internal/display" ulua "github.com/zyedidia/micro/v2/internal/lua" "github.com/zyedidia/micro/v2/internal/screen" "github.com/zyedidia/micro/v2/internal/util" "github.com/zyedidia/tcell/v2" ) type BufAction interface{} // BufKeyAction represents an action bound to a key. type BufKeyAction func(*BufPane) bool // BufMouseAction is an action that must be bound to a mouse event. type BufMouseAction func(*BufPane, *tcell.EventMouse) bool // BufBindings stores the bindings for the buffer pane type. var BufBindings *KeyTree // BufKeyActionGeneral makes a general pane action from a BufKeyAction. func BufKeyActionGeneral(a BufKeyAction) PaneKeyAction { return func(p Pane) bool { return a(p.(*BufPane)) } } // BufMouseActionGeneral makes a general pane mouse action from a BufKeyAction. func BufMouseActionGeneral(a BufMouseAction) PaneMouseAction { return func(p Pane, me *tcell.EventMouse) bool { return a(p.(*BufPane), me) } } func init() { BufBindings = NewKeyTree() } // LuaAction makes an action from a lua function. It returns either a BufKeyAction // or a BufMouseAction depending on the event type. func LuaAction(fn string, k Event) BufAction { luaFn := strings.Split(fn, ".") if len(luaFn) <= 1 { return nil } plName, plFn := luaFn[0], luaFn[1] pl := config.FindPlugin(plName) if pl == nil { return nil } var action BufAction switch k.(type) { case KeyEvent, KeySequenceEvent, RawEvent: action = BufKeyAction(func(h *BufPane) bool { val, err := pl.Call(plFn, luar.New(ulua.L, h)) if err != nil { screen.TermMessage(err) } if v, ok := val.(lua.LBool); !ok { return false } else { return bool(v) } }) case MouseEvent: action = BufMouseAction(func(h *BufPane, te *tcell.EventMouse) bool { val, err := pl.Call(plFn, luar.New(ulua.L, h), luar.New(ulua.L, te)) if err != nil { screen.TermMessage(err) } if v, ok := val.(lua.LBool); !ok { return false } else { return bool(v) } }) } return action } // BufMapEvent maps an event to an action func BufMapEvent(k Event, action string) { config.Bindings["buffer"][k.Name()] = action var actionfns []BufAction var names []string var types []byte for i := 0; ; i++ { if action == "" { break } // TODO: fix problem when complex bindings have these // characters (escape them?) idx := strings.IndexAny(action, "&|,") a := action if idx >= 0 { a = action[:idx] types = append(types, action[idx]) action = action[idx+1:] } else { types = append(types, ' ') action = "" } var afn BufAction if strings.HasPrefix(a, "command:") { a = strings.SplitN(a, ":", 2)[1] afn = CommandAction(a) names = append(names, "") } else if strings.HasPrefix(a, "command-edit:") { a = strings.SplitN(a, ":", 2)[1] afn = CommandEditAction(a) names = append(names, "") } else if strings.HasPrefix(a, "lua:") { a = strings.SplitN(a, ":", 2)[1] afn = LuaAction(a, k) if afn == nil { screen.TermMessage("Lua Error:", a, "does not exist") continue } split := strings.SplitN(a, ".", 2) if len(split) > 1 { a = strings.Title(split[0]) + strings.Title(split[1]) } else { a = strings.Title(a) } names = append(names, a) } else if f, ok := BufKeyActions[a]; ok { afn = f names = append(names, a) } else if f, ok := BufMouseActions[a]; ok { afn = f names = append(names, a) } else { screen.TermMessage("Error in bindings: action", a, "does not exist") continue } actionfns = append(actionfns, afn) } bufAction := func(h *BufPane, te *tcell.EventMouse) bool { for i, a := range actionfns { var success bool if _, ok := MultiActions[names[i]]; ok { success = true for _, c := range h.Buf.GetCursors() { h.Buf.SetCurCursor(c.Num) h.Cursor = c success = success && h.execAction(a, names[i], te) } } else { h.Buf.SetCurCursor(0) h.Cursor = h.Buf.GetActiveCursor() success = h.execAction(a, names[i], te) } // if the action changed the current pane, update the reference h = MainTab().CurPane() if h == nil { // stop, in case the current pane is not a BufPane break } if (!success && types[i] == '&') || (success && types[i] == '|') { break } } return true } switch e := k.(type) { case KeyEvent, KeySequenceEvent, RawEvent: BufBindings.RegisterKeyBinding(e, BufKeyActionGeneral(func(h *BufPane) bool { return bufAction(h, nil) })) case MouseEvent: BufBindings.RegisterMouseBinding(e, BufMouseActionGeneral(bufAction)) } } // BufUnmap unmaps a key or mouse event from any action func BufUnmap(k Event) { // TODO // delete(BufKeyBindings, k) // // switch e := k.(type) { // case MouseEvent: // delete(BufMouseBindings, e) // } } // The BufPane connects the buffer and the window // It provides a cursor (or multiple) and defines a set of actions // that can be taken on the buffer // The ActionHandler can access the window for necessary info about // visual positions for mouse clicks and scrolling type BufPane struct { display.BWindow // Buf is the buffer this BufPane views Buf *buffer.Buffer // Bindings stores the association of key events and actions bindings *KeyTree // Cursor is the currently active buffer cursor Cursor *buffer.Cursor // Since tcell doesn't differentiate between a mouse press event // and a mouse move event with button pressed (nor between a mouse // release event and a mouse move event with no buttons pressed), // we need to keep track of whether or not the mouse was previously // pressed, to determine mouse release and mouse drag events. // Moreover, since in case of a release event tcell doesn't tell us // which button was released, we need to keep track of which // (possibly multiple) buttons were pressed previously. mousePressed map[MouseEvent]bool // We need to keep track of insert key press toggle isOverwriteMode bool // This stores when the last click was // This is useful for detecting double and triple clicks lastClickTime time.Time lastLoc buffer.Loc // lastCutTime stores when the last ctrl+k was issued. // It is used for clearing the clipboard to replace it with fresh cut lines. lastCutTime time.Time // freshClip returns true if the clipboard has never been pasted. freshClip bool // Was the last mouse event actually a double click? // Useful for detecting triple clicks -- if a double click is detected // but the last mouse event was actually a double click, it's a triple click doubleClick bool // Same here, just to keep track for mouse move events tripleClick bool // Should the current multiple cursor selection search based on word or // based on selection (false for selection, true for word) multiWord bool splitID uint64 tab *Tab // remember original location of a search in case the search is canceled searchOrig buffer.Loc // The pane may not yet be fully initialized after its creation // since we may not know the window geometry yet. In such case we finish // its initialization a bit later, after the initial resize. initialized bool } func newBufPane(buf *buffer.Buffer, win display.BWindow, tab *Tab) *BufPane { h := new(BufPane) h.Buf = buf h.BWindow = win h.tab = tab h.Cursor = h.Buf.GetActiveCursor() h.mousePressed = make(map[MouseEvent]bool) return h } // NewBufPane creates a new buffer pane with the given window. func NewBufPane(buf *buffer.Buffer, win display.BWindow, tab *Tab) *BufPane { h := newBufPane(buf, win, tab) h.finishInitialize() return h } // NewBufPaneFromBuf constructs a new pane from the given buffer and automatically // creates a buf window. func NewBufPaneFromBuf(buf *buffer.Buffer, tab *Tab) *BufPane { w := display.NewBufWindow(0, 0, 0, 0, buf) h := newBufPane(buf, w, tab) // Postpone finishing initializing the pane until we know the actual geometry // of the buf window. return h } // TODO: make sure splitID and tab are set before finishInitialize is called func (h *BufPane) finishInitialize() { h.initialRelocate() h.initialized = true err := config.RunPluginFn("onBufPaneOpen", luar.New(ulua.L, h)) if err != nil { screen.TermMessage(err) } } // Resize resizes the pane func (h *BufPane) Resize(width, height int) { h.BWindow.Resize(width, height) if !h.initialized { h.finishInitialize() } } // SetTab sets this pane's tab. func (h *BufPane) SetTab(t *Tab) { h.tab = t } // Tab returns this pane's tab. func (h *BufPane) Tab() *Tab { return h.tab } func (h *BufPane) ResizePane(size int) { n := h.tab.GetNode(h.splitID) n.ResizeSplit(size) h.tab.Resize() } // PluginCB calls all plugin callbacks with a certain name and displays an // error if there is one and returns the aggregate boolean response func (h *BufPane) PluginCB(cb string) bool { b, err := config.RunPluginFnBool(h.Buf.Settings, cb, luar.New(ulua.L, h)) if err != nil { screen.TermMessage(err) } return b } // PluginCBRune is the same as PluginCB but also passes a rune to the plugins func (h *BufPane) PluginCBRune(cb string, r rune) bool { b, err := config.RunPluginFnBool(h.Buf.Settings, cb, luar.New(ulua.L, h), luar.New(ulua.L, string(r))) if err != nil { screen.TermMessage(err) } return b } func (h *BufPane) resetMouse() { for me := range h.mousePressed { delete(h.mousePressed, me) } } // OpenBuffer opens the given buffer in this pane. func (h *BufPane) OpenBuffer(b *buffer.Buffer) { h.Buf.Close() h.Buf = b h.BWindow.SetBuffer(b) h.Cursor = b.GetActiveCursor() h.Resize(h.GetView().Width, h.GetView().Height) h.initialRelocate() // Set mouseReleased to true because we assume the mouse is not being // pressed when the editor is opened h.resetMouse() // Set isOverwriteMode to false, because we assume we are in the default // mode when editor is opened h.isOverwriteMode = false h.lastClickTime = time.Time{} } // GotoLoc moves the cursor to a new location and adjusts the view accordingly. // Use GotoLoc when the new location may be far away from the current location. func (h *BufPane) GotoLoc(loc buffer.Loc) { sloc := h.SLocFromLoc(loc) d := h.Diff(h.SLocFromLoc(h.Cursor.Loc), sloc) h.Cursor.GotoLoc(loc) // If the new location is far away from the previous one, // ensure the cursor is at 25% of the window height height := h.BufView().Height if util.Abs(d) >= height { v := h.GetView() v.StartLine = h.Scroll(sloc, -height/4) h.ScrollAdjust() v.StartCol = 0 } h.Relocate() } func (h *BufPane) initialRelocate() { sloc := h.SLocFromLoc(h.Cursor.Loc) height := h.BufView().Height // If the initial cursor location is far away from the beginning // of the buffer, ensure the cursor is at 25% of the window height v := h.GetView() if h.Diff(display.SLoc{0, 0}, sloc) < height { v.StartLine = display.SLoc{0, 0} } else { v.StartLine = h.Scroll(sloc, -height/4) h.ScrollAdjust() } v.StartCol = 0 h.Relocate() } // ID returns this pane's split id. func (h *BufPane) ID() uint64 { return h.splitID } // SetID sets the split ID of this pane. func (h *BufPane) SetID(i uint64) { h.splitID = i } // Name returns the BufPane's name. func (h *BufPane) Name() string { n := h.Buf.GetName() if h.Buf.Modified() { n += " +" } return n } // ReOpen reloads the file opened in the bufpane from disk func (h *BufPane) ReOpen() { h.Buf.ReOpen() h.Relocate() } func (h *BufPane) getReloadSetting() string { reloadSetting := h.Buf.Settings["reload"] return reloadSetting.(string) } // HandleEvent executes the tcell event properly func (h *BufPane) HandleEvent(event tcell.Event) { if h.Buf.ExternallyModified() && !h.Buf.ReloadDisabled { reload := h.getReloadSetting() if reload == "prompt" { InfoBar.YNPrompt("The file on disk has changed. Reload file? (y,n,esc)", func(yes, canceled bool) { if canceled { h.Buf.DisableReload() } if !yes || canceled { h.Buf.UpdateModTime() } else { h.ReOpen() } }) } else if reload == "auto" { h.ReOpen() } else if reload == "disabled" { h.Buf.DisableReload() } else { InfoBar.Message("Invalid reload setting") } } switch e := event.(type) { case *tcell.EventRaw: re := RawEvent{ esc: e.EscSeq(), } h.DoKeyEvent(re) case *tcell.EventPaste: h.paste(e.Text()) h.Relocate() case *tcell.EventKey: ke := keyEvent(e) done := h.DoKeyEvent(ke) if !done && e.Key() == tcell.KeyRune { h.DoRuneInsert(e.Rune()) } case *tcell.EventMouse: if e.Buttons() != tcell.ButtonNone { me := MouseEvent{ btn: e.Buttons(), mod: metaToAlt(e.Modifiers()), state: MousePress, } isDrag := len(h.mousePressed) > 0 if e.Buttons() & ^(tcell.WheelUp|tcell.WheelDown|tcell.WheelLeft|tcell.WheelRight) != tcell.ButtonNone { h.mousePressed[me] = true } if isDrag { me.state = MouseDrag } h.DoMouseEvent(me, e) } else { // Mouse event with no click - mouse was just released. // If there were multiple mouse buttons pressed, we don't know which one // was actually released, so we assume they all were released. pressed := len(h.mousePressed) > 0 for me := range h.mousePressed { delete(h.mousePressed, me) me.state = MouseRelease h.DoMouseEvent(me, e) } if !pressed { // Propagate the mouse release in case the press wasn't for this BufPane Tabs.ResetMouse() } } } h.Buf.MergeCursors() if h.IsActive() { // Display any gutter messages for this line c := h.Buf.GetActiveCursor() none := true for _, m := range h.Buf.Messages { if c.Y == m.Start.Y || c.Y == m.End.Y { InfoBar.GutterMessage(m.Msg) none = false break } } if none && InfoBar.HasGutter { InfoBar.ClearGutter() } } cursors := h.Buf.GetCursors() for _, c := range cursors { if c.NewTrailingWsY != c.Y && (!c.HasSelection() || (c.NewTrailingWsY != c.CurSelection[0].Y && c.NewTrailingWsY != c.CurSelection[1].Y)) { c.NewTrailingWsY = -1 } } } // Bindings returns the current bindings tree for this buffer. func (h *BufPane) Bindings() *KeyTree { if h.bindings != nil { return h.bindings } return BufBindings } // DoKeyEvent executes a key event by finding the action it is bound // to and executing it (possibly multiple times for multiple cursors). // Returns true if the action was executed OR if there are more keys // remaining to process before executing an action (if this is a key // sequence event). Returns false if no action found. func (h *BufPane) DoKeyEvent(e Event) bool { binds := h.Bindings() action, more := binds.NextEvent(e, nil) if action != nil && !more { action(h) binds.ResetEvents() return true } else if action == nil && !more { binds.ResetEvents() } return more } func (h *BufPane) execAction(action BufAction, name string, te *tcell.EventMouse) bool { if name != "Autocomplete" && name != "CycleAutocompleteBack" { h.Buf.HasSuggestions = false } if !h.PluginCB("pre" + name) { return false } var success bool switch a := action.(type) { case BufKeyAction: success = a(h) case BufMouseAction: success = a(h, te) } success = success && h.PluginCB("on"+name) if _, ok := MultiActions[name]; ok { if recordingMacro { if name != "ToggleMacro" && name != "PlayMacro" { curmacro = append(curmacro, action) } } } return success } func (h *BufPane) completeAction(action string) { h.PluginCB("on" + action) } func (h *BufPane) HasKeyEvent(e Event) bool { // TODO return true // _, ok := BufKeyBindings[e] // return ok } // DoMouseEvent executes a mouse event by finding the action it is bound // to and executing it func (h *BufPane) DoMouseEvent(e MouseEvent, te *tcell.EventMouse) bool { binds := h.Bindings() action, _ := binds.NextEvent(e, te) if action != nil { action(h) binds.ResetEvents() return true } // TODO return false // if action, ok := BufMouseBindings[e]; ok { // if action(h, te) { // h.Relocate() // } // return true // } else if h.HasKeyEvent(e) { // return h.DoKeyEvent(e) // } // return false } // DoRuneInsert inserts a given rune into the current buffer // (possibly multiple times for multiple cursors) func (h *BufPane) DoRuneInsert(r rune) { cursors := h.Buf.GetCursors() for _, c := range cursors { // Insert a character h.Buf.SetCurCursor(c.Num) h.Cursor = c if !h.PluginCBRune("preRune", r) { continue } if c.HasSelection() { c.DeleteSelection() c.ResetSelection() } if h.isOverwriteMode { next := c.Loc next.X++ h.Buf.Replace(c.Loc, next, string(r)) } else { h.Buf.Insert(c.Loc, string(r)) } if recordingMacro { curmacro = append(curmacro, r) } h.Relocate() h.PluginCBRune("onRune", r) } } // VSplitIndex opens the given buffer in a vertical split on the given side. func (h *BufPane) VSplitIndex(buf *buffer.Buffer, right bool) *BufPane { e := NewBufPaneFromBuf(buf, h.tab) e.splitID = MainTab().GetNode(h.splitID).VSplit(right) MainTab().Panes = append(MainTab().Panes, e) MainTab().Resize() MainTab().SetActive(len(MainTab().Panes) - 1) return e } // HSplitIndex opens the given buffer in a horizontal split on the given side. func (h *BufPane) HSplitIndex(buf *buffer.Buffer, bottom bool) *BufPane { e := NewBufPaneFromBuf(buf, h.tab) e.splitID = MainTab().GetNode(h.splitID).HSplit(bottom) MainTab().Panes = append(MainTab().Panes, e) MainTab().Resize() MainTab().SetActive(len(MainTab().Panes) - 1) return e } // VSplitBuf opens the given buffer in a new vertical split. func (h *BufPane) VSplitBuf(buf *buffer.Buffer) *BufPane { return h.VSplitIndex(buf, h.Buf.Settings["splitright"].(bool)) } // HSplitBuf opens the given buffer in a new horizontal split. func (h *BufPane) HSplitBuf(buf *buffer.Buffer) *BufPane { return h.HSplitIndex(buf, h.Buf.Settings["splitbottom"].(bool)) } // Close this pane. func (h *BufPane) Close() { h.Buf.Close() } // SetActive marks this pane as active. func (h *BufPane) SetActive(b bool) { if h.IsActive() == b { return } h.BWindow.SetActive(b) if b { // Display any gutter messages for this line c := h.Buf.GetActiveCursor() none := true for _, m := range h.Buf.Messages { if c.Y == m.Start.Y || c.Y == m.End.Y { InfoBar.GutterMessage(m.Msg) none = false break } } if none && InfoBar.HasGutter { InfoBar.ClearGutter() } err := config.RunPluginFn("onSetActive", luar.New(ulua.L, h)) if err != nil { screen.TermMessage(err) } } } // BufKeyActions contains the list of all possible key actions the bufhandler could execute var BufKeyActions = map[string]BufKeyAction{ "CursorUp": (*BufPane).CursorUp, "CursorDown": (*BufPane).CursorDown, "CursorPageUp": (*BufPane).CursorPageUp, "CursorPageDown": (*BufPane).CursorPageDown, "CursorLeft": (*BufPane).CursorLeft, "CursorRight": (*BufPane).CursorRight, "CursorStart": (*BufPane).CursorStart, "CursorEnd": (*BufPane).CursorEnd, "SelectToStart": (*BufPane).SelectToStart, "SelectToEnd": (*BufPane).SelectToEnd, "SelectUp": (*BufPane).SelectUp, "SelectDown": (*BufPane).SelectDown, "SelectLeft": (*BufPane).SelectLeft, "SelectRight": (*BufPane).SelectRight, "WordRight": (*BufPane).WordRight, "WordLeft": (*BufPane).WordLeft, "SubWordRight": (*BufPane).SubWordRight, "SubWordLeft": (*BufPane).SubWordLeft, "SelectWordRight": (*BufPane).SelectWordRight, "SelectWordLeft": (*BufPane).SelectWordLeft, "SelectSubWordRight": (*BufPane).SelectSubWordRight, "SelectSubWordLeft": (*BufPane).SelectSubWordLeft, "DeleteWordRight": (*BufPane).DeleteWordRight, "DeleteWordLeft": (*BufPane).DeleteWordLeft, "DeleteSubWordRight": (*BufPane).DeleteSubWordRight, "DeleteSubWordLeft": (*BufPane).DeleteSubWordLeft, "SelectLine": (*BufPane).SelectLine, "SelectToStartOfLine": (*BufPane).SelectToStartOfLine, "SelectToStartOfText": (*BufPane).SelectToStartOfText, "SelectToStartOfTextToggle": (*BufPane).SelectToStartOfTextToggle, "SelectToEndOfLine": (*BufPane).SelectToEndOfLine, "ParagraphPrevious": (*BufPane).ParagraphPrevious, "ParagraphNext": (*BufPane).ParagraphNext, "SelectToParagraphPrevious": (*BufPane).SelectToParagraphPrevious, "SelectToParagraphNext": (*BufPane).SelectToParagraphNext, "InsertNewline": (*BufPane).InsertNewline, "Backspace": (*BufPane).Backspace, "Delete": (*BufPane).Delete, "InsertTab": (*BufPane).InsertTab, "Save": (*BufPane).Save, "SaveAll": (*BufPane).SaveAll, "SaveAs": (*BufPane).SaveAs, "Find": (*BufPane).Find, "FindLiteral": (*BufPane).FindLiteral, "FindNext": (*BufPane).FindNext, "FindPrevious": (*BufPane).FindPrevious, "DiffNext": (*BufPane).DiffNext, "DiffPrevious": (*BufPane).DiffPrevious, "Center": (*BufPane).Center, "Undo": (*BufPane).Undo, "Redo": (*BufPane).Redo, "Copy": (*BufPane).Copy, "CopyLine": (*BufPane).CopyLine, "Cut": (*BufPane).Cut, "CutLine": (*BufPane).CutLine, "DuplicateLine": (*BufPane).DuplicateLine, "DeleteLine": (*BufPane).DeleteLine, "MoveLinesUp": (*BufPane).MoveLinesUp, "MoveLinesDown": (*BufPane).MoveLinesDown, "IndentSelection": (*BufPane).IndentSelection, "OutdentSelection": (*BufPane).OutdentSelection, "Autocomplete": (*BufPane).Autocomplete, "CycleAutocompleteBack": (*BufPane).CycleAutocompleteBack, "OutdentLine": (*BufPane).OutdentLine, "IndentLine": (*BufPane).IndentLine, "Paste": (*BufPane).Paste, "PastePrimary": (*BufPane).PastePrimary, "SelectAll": (*BufPane).SelectAll, "OpenFile": (*BufPane).OpenFile, "Start": (*BufPane).Start, "End": (*BufPane).End, "PageUp": (*BufPane).PageUp, "PageDown": (*BufPane).PageDown, "SelectPageUp": (*BufPane).SelectPageUp, "SelectPageDown": (*BufPane).SelectPageDown, "HalfPageUp": (*BufPane).HalfPageUp, "HalfPageDown": (*BufPane).HalfPageDown, "StartOfText": (*BufPane).StartOfText, "StartOfTextToggle": (*BufPane).StartOfTextToggle, "StartOfLine": (*BufPane).StartOfLine, "EndOfLine": (*BufPane).EndOfLine, "ToggleHelp": (*BufPane).ToggleHelp, "ToggleKeyMenu": (*BufPane).ToggleKeyMenu, "ToggleDiffGutter": (*BufPane).ToggleDiffGutter, "ToggleRuler": (*BufPane).ToggleRuler, "ToggleHighlightSearch": (*BufPane).ToggleHighlightSearch, "UnhighlightSearch": (*BufPane).UnhighlightSearch, "ResetSearch": (*BufPane).ResetSearch, "ClearStatus": (*BufPane).ClearStatus, "ShellMode": (*BufPane).ShellMode, "CommandMode": (*BufPane).CommandMode, "ToggleOverwriteMode": (*BufPane).ToggleOverwriteMode, "Escape": (*BufPane).Escape, "Quit": (*BufPane).Quit, "QuitAll": (*BufPane).QuitAll, "ForceQuit": (*BufPane).ForceQuit, "AddTab": (*BufPane).AddTab, "PreviousTab": (*BufPane).PreviousTab, "NextTab": (*BufPane).NextTab, "NextSplit": (*BufPane).NextSplit, "PreviousSplit": (*BufPane).PreviousSplit, "Unsplit": (*BufPane).Unsplit, "VSplit": (*BufPane).VSplitAction, "HSplit": (*BufPane).HSplitAction, "ToggleMacro": (*BufPane).ToggleMacro, "PlayMacro": (*BufPane).PlayMacro, "Suspend": (*BufPane).Suspend, "ScrollUp": (*BufPane).ScrollUpAction, "ScrollDown": (*BufPane).ScrollDownAction, "SpawnMultiCursor": (*BufPane).SpawnMultiCursor, "SpawnMultiCursorUp": (*BufPane).SpawnMultiCursorUp, "SpawnMultiCursorDown": (*BufPane).SpawnMultiCursorDown, "SpawnMultiCursorSelect": (*BufPane).SpawnMultiCursorSelect, "RemoveMultiCursor": (*BufPane).RemoveMultiCursor, "RemoveAllMultiCursors": (*BufPane).RemoveAllMultiCursors, "SkipMultiCursor": (*BufPane).SkipMultiCursor, "JumpToMatchingBrace": (*BufPane).JumpToMatchingBrace, "JumpLine": (*BufPane).JumpLine, "Deselect": (*BufPane).Deselect, "ClearInfo": (*BufPane).ClearInfo, "None": (*BufPane).None, // This was changed to InsertNewline but I don't want to break backwards compatibility "InsertEnter": (*BufPane).InsertNewline, } // BufMouseActions contains the list of all possible mouse actions the bufhandler could execute var BufMouseActions = map[string]BufMouseAction{ "MousePress": (*BufPane).MousePress, "MouseDrag": (*BufPane).MouseDrag, "MouseRelease": (*BufPane).MouseRelease, "MouseMultiCursor": (*BufPane).MouseMultiCursor, } // MultiActions is a list of actions that should be executed multiple // times if there are multiple cursors (one per cursor) // Generally actions that modify global editor state like quitting or // saving should not be included in this list var MultiActions = map[string]bool{ "CursorUp": true, "CursorDown": true, "CursorPageUp": true, "CursorPageDown": true, "CursorLeft": true, "CursorRight": true, "CursorStart": true, "CursorEnd": true, "SelectToStart": true, "SelectToEnd": true, "SelectUp": true, "SelectDown": true, "SelectLeft": true, "SelectRight": true, "WordRight": true, "WordLeft": true, "SubWordRight": true, "SubWordLeft": true, "SelectWordRight": true, "SelectWordLeft": true, "SelectSubWordRight": true, "SelectSubWordLeft": true, "DeleteWordRight": true, "DeleteWordLeft": true, "DeleteSubWordRight": true, "DeleteSubWordLeft": true, "SelectLine": true, "SelectToStartOfLine": true, "SelectToStartOfText": true, "SelectToStartOfTextToggle": true, "SelectToEndOfLine": true, "ParagraphPrevious": true, "ParagraphNext": true, "InsertNewline": true, "Backspace": true, "Delete": true, "InsertTab": true, "FindNext": true, "FindPrevious": true, "CopyLine": true, "Copy": true, "Cut": true, "CutLine": true, "DuplicateLine": true, "DeleteLine": true, "MoveLinesUp": true, "MoveLinesDown": true, "IndentSelection": true, "OutdentSelection": true, "OutdentLine": true, "IndentLine": true, "Paste": true, "PastePrimary": true, "SelectPageUp": true, "SelectPageDown": true, "StartOfLine": true, "StartOfText": true, "StartOfTextToggle": true, "EndOfLine": true, "JumpToMatchingBrace": true, } micro-2.0.14/internal/action/command.go0000664000175000017510000006272414663411671017322 0ustar nileshnileshpackage action import ( "bytes" "errors" "fmt" "os" "os/exec" "path/filepath" "reflect" "regexp" "strconv" "strings" shellquote "github.com/kballard/go-shellquote" "github.com/zyedidia/micro/v2/internal/buffer" "github.com/zyedidia/micro/v2/internal/clipboard" "github.com/zyedidia/micro/v2/internal/config" "github.com/zyedidia/micro/v2/internal/screen" "github.com/zyedidia/micro/v2/internal/shell" "github.com/zyedidia/micro/v2/internal/util" ) // A Command contains information about how to execute a command // It has the action for that command as well as a completer function type Command struct { action func(*BufPane, []string) completer buffer.Completer } var commands map[string]Command func InitCommands() { commands = map[string]Command{ "set": {(*BufPane).SetCmd, OptionValueComplete}, "reset": {(*BufPane).ResetCmd, OptionValueComplete}, "setlocal": {(*BufPane).SetLocalCmd, OptionValueComplete}, "show": {(*BufPane).ShowCmd, OptionComplete}, "showkey": {(*BufPane).ShowKeyCmd, nil}, "run": {(*BufPane).RunCmd, nil}, "bind": {(*BufPane).BindCmd, nil}, "unbind": {(*BufPane).UnbindCmd, nil}, "quit": {(*BufPane).QuitCmd, nil}, "goto": {(*BufPane).GotoCmd, nil}, "jump": {(*BufPane).JumpCmd, nil}, "save": {(*BufPane).SaveCmd, nil}, "replace": {(*BufPane).ReplaceCmd, nil}, "replaceall": {(*BufPane).ReplaceAllCmd, nil}, "vsplit": {(*BufPane).VSplitCmd, buffer.FileComplete}, "hsplit": {(*BufPane).HSplitCmd, buffer.FileComplete}, "tab": {(*BufPane).NewTabCmd, buffer.FileComplete}, "help": {(*BufPane).HelpCmd, HelpComplete}, "eval": {(*BufPane).EvalCmd, nil}, "log": {(*BufPane).ToggleLogCmd, nil}, "plugin": {(*BufPane).PluginCmd, PluginComplete}, "reload": {(*BufPane).ReloadCmd, nil}, "reopen": {(*BufPane).ReopenCmd, nil}, "cd": {(*BufPane).CdCmd, buffer.FileComplete}, "pwd": {(*BufPane).PwdCmd, nil}, "open": {(*BufPane).OpenCmd, buffer.FileComplete}, "tabmove": {(*BufPane).TabMoveCmd, nil}, "tabswitch": {(*BufPane).TabSwitchCmd, nil}, "term": {(*BufPane).TermCmd, nil}, "memusage": {(*BufPane).MemUsageCmd, nil}, "retab": {(*BufPane).RetabCmd, nil}, "raw": {(*BufPane).RawCmd, nil}, "textfilter": {(*BufPane).TextFilterCmd, nil}, } } // MakeCommand is a function to easily create new commands // This can be called by plugins in Lua so that plugins can define their own commands func MakeCommand(name string, action func(bp *BufPane, args []string), completer buffer.Completer) { if action != nil { commands[name] = Command{action, completer} } } // CommandEditAction returns a bindable function that opens a prompt with // the given string and executes the command when the user presses // enter func CommandEditAction(prompt string) BufKeyAction { return func(h *BufPane) bool { InfoBar.Prompt("> ", prompt, "Command", nil, func(resp string, canceled bool) { if !canceled { MainTab().CurPane().HandleCommand(resp) } }) return false } } // CommandAction returns a bindable function which executes the // given command func CommandAction(cmd string) BufKeyAction { return func(h *BufPane) bool { MainTab().CurPane().HandleCommand(cmd) return false } } var PluginCmds = []string{"install", "remove", "update", "available", "list", "search"} // PluginCmd installs, removes, updates, lists, or searches for given plugins func (h *BufPane) PluginCmd(args []string) { if len(args) < 1 { InfoBar.Error("Not enough arguments") return } if h.Buf.Type != buffer.BTLog { h.OpenLogBuf() } config.PluginCommand(buffer.LogBuf, args[0], args[1:]) } // RetabCmd changes all spaces to tabs or all tabs to spaces // depending on the user's settings func (h *BufPane) RetabCmd(args []string) { h.Buf.Retab() } // RawCmd opens a new raw view which displays the escape sequences micro // is receiving in real-time func (h *BufPane) RawCmd(args []string) { width, height := screen.Screen.Size() iOffset := config.GetInfoBarOffset() tp := NewTabFromPane(0, 0, width, height-iOffset, NewRawPane(nil)) Tabs.AddTab(tp) Tabs.SetActive(len(Tabs.List) - 1) } // TextFilterCmd filters the selection through the command. // Selection goes to the command input. // On successful run command output replaces the current selection. func (h *BufPane) TextFilterCmd(args []string) { if len(args) == 0 { InfoBar.Error("usage: textfilter arguments") return } sel := h.Cursor.GetSelection() if len(sel) == 0 { h.Cursor.SelectWord() sel = h.Cursor.GetSelection() } var bout, berr bytes.Buffer cmd := exec.Command(args[0], args[1:]...) cmd.Stdin = strings.NewReader(string(sel)) cmd.Stderr = &berr cmd.Stdout = &bout err := cmd.Run() if err != nil { InfoBar.Error(err.Error() + " " + berr.String()) return } h.Cursor.DeleteSelection() h.Buf.Insert(h.Cursor.Loc, bout.String()) } // TabMoveCmd moves the current tab to a given index (starts at 1). The // displaced tabs are moved up. func (h *BufPane) TabMoveCmd(args []string) { if len(args) <= 0 { InfoBar.Error("Not enough arguments: provide an index, starting at 1") return } if len(args[0]) <= 0 { InfoBar.Error("Invalid argument: empty string") return } num, err := strconv.Atoi(args[0]) if err != nil { InfoBar.Error("Invalid argument: ", err) return } // Preserve sign for relative move, if one exists var shiftDirection byte if strings.Contains("-+", string([]byte{args[0][0]})) { shiftDirection = args[0][0] } // Relative positions -> absolute positions idxFrom := Tabs.Active() idxTo := 0 offset := util.Abs(num) if shiftDirection == '-' { idxTo = idxFrom - offset } else if shiftDirection == '+' { idxTo = idxFrom + offset } else { idxTo = offset - 1 } // Restrain position to within the valid range idxTo = util.Clamp(idxTo, 0, len(Tabs.List)-1) activeTab := Tabs.List[idxFrom] Tabs.RemoveTab(activeTab.Panes[0].ID()) Tabs.List = append(Tabs.List, nil) copy(Tabs.List[idxTo+1:], Tabs.List[idxTo:]) Tabs.List[idxTo] = activeTab Tabs.UpdateNames() Tabs.SetActive(idxTo) // InfoBar.Message(fmt.Sprintf("Moved tab from slot %d to %d", idxFrom+1, idxTo+1)) } // TabSwitchCmd switches to a given tab either by name or by number func (h *BufPane) TabSwitchCmd(args []string) { if len(args) > 0 { num, err := strconv.Atoi(args[0]) if err != nil { // Check for tab with this name found := false for i, t := range Tabs.List { if t.Panes[t.active].Name() == args[0] { Tabs.SetActive(i) found = true } } if !found { InfoBar.Error("Could not find tab: ", err) } } else { num-- if num >= 0 && num < len(Tabs.List) { Tabs.SetActive(num) } else { InfoBar.Error("Invalid tab index") } } } } // CdCmd changes the current working directory func (h *BufPane) CdCmd(args []string) { if len(args) > 0 { path, err := util.ReplaceHome(args[0]) if err != nil { InfoBar.Error(err) return } err = os.Chdir(path) if err != nil { InfoBar.Error(err) return } wd, _ := os.Getwd() for _, b := range buffer.OpenBuffers { if len(b.Path) > 0 { b.Path, _ = util.MakeRelative(b.AbsPath, wd) if p, _ := filepath.Abs(b.Path); !strings.Contains(p, wd) { b.Path = b.AbsPath } } } } } // MemUsageCmd prints micro's memory usage // Alloc shows how many bytes are currently in use // Sys shows how many bytes have been requested from the operating system // NumGC shows how many times the GC has been run // Note that Go commonly reserves more memory from the OS than is currently in-use/required // Additionally, even if Go returns memory to the OS, the OS does not always claim it because // there may be plenty of memory to spare func (h *BufPane) MemUsageCmd(args []string) { InfoBar.Message(util.GetMemStats()) } // PwdCmd prints the current working directory func (h *BufPane) PwdCmd(args []string) { wd, err := os.Getwd() if err != nil { InfoBar.Message(err.Error()) } else { InfoBar.Message(wd) } } // OpenCmd opens a new buffer with a given filename func (h *BufPane) OpenCmd(args []string) { if len(args) > 0 { filename := args[0] // the filename might or might not be quoted, so unquote first then join the strings. args, err := shellquote.Split(filename) if err != nil { InfoBar.Error("Error parsing args ", err) return } if len(args) == 0 { return } filename = strings.Join(args, " ") open := func() { b, err := buffer.NewBufferFromFile(filename, buffer.BTDefault) if err != nil { InfoBar.Error(err) return } h.OpenBuffer(b) } if h.Buf.Modified() { InfoBar.YNPrompt("Save changes to "+h.Buf.GetName()+" before closing? (y,n,esc)", func(yes, canceled bool) { if !canceled && !yes { open() } else if !canceled && yes { h.Save() open() } }) } else { open() } } else { InfoBar.Error("No filename") } } // ToggleLogCmd toggles the log view func (h *BufPane) ToggleLogCmd(args []string) { if h.Buf.Type != buffer.BTLog { h.OpenLogBuf() } else { h.Quit() } } // ReloadCmd reloads all files (syntax files, colorschemes, plugins...) func (h *BufPane) ReloadCmd(args []string) { reloadRuntime(true) } // ReloadConfig reloads only the configuration func ReloadConfig() { reloadRuntime(false) } func reloadRuntime(reloadPlugins bool) { if reloadPlugins { err := config.RunPluginFn("deinit") if err != nil { screen.TermMessage(err) } } config.InitRuntimeFiles(true) if reloadPlugins { config.InitPlugins() } err := config.ReadSettings() if err != nil { screen.TermMessage(err) } else { parsedSettings := config.ParsedSettings() defaultSettings := config.DefaultAllSettings() for k := range defaultSettings { if _, ok := config.VolatileSettings[k]; ok { // reload should not override volatile settings continue } if _, ok := parsedSettings[k]; ok { err = doSetGlobalOptionNative(k, parsedSettings[k]) } else { err = doSetGlobalOptionNative(k, defaultSettings[k]) } if err != nil { screen.TermMessage(err) } } } if reloadPlugins { err = config.LoadAllPlugins() if err != nil { screen.TermMessage(err) } } InitBindings() InitCommands() if reloadPlugins { err = config.RunPluginFn("preinit") if err != nil { screen.TermMessage(err) } err = config.RunPluginFn("init") if err != nil { screen.TermMessage(err) } err = config.RunPluginFn("postinit") if err != nil { screen.TermMessage(err) } } err = config.InitColorscheme() if err != nil { screen.TermMessage(err) } for _, b := range buffer.OpenBuffers { b.ReloadSettings(true) } } // ReopenCmd reopens the buffer (reload from disk) func (h *BufPane) ReopenCmd(args []string) { if h.Buf.Modified() { InfoBar.YNPrompt("Save file before reopen?", func(yes, canceled bool) { if !canceled && yes { h.Save() h.ReOpen() } else if !canceled { h.ReOpen() } }) } else { h.ReOpen() } } func (h *BufPane) openHelp(page string) error { if data, err := config.FindRuntimeFile(config.RTHelp, page).Data(); err != nil { return errors.New(fmt.Sprintf("Unable to load help text for %s: %v", page, err)) } else { helpBuffer := buffer.NewBufferFromString(string(data), page+".md", buffer.BTHelp) helpBuffer.SetName("Help " + page) helpBuffer.SetOptionNative("hltaberrors", false) helpBuffer.SetOptionNative("hltrailingws", false) if h.Buf.Type == buffer.BTHelp { h.OpenBuffer(helpBuffer) } else { h.HSplitBuf(helpBuffer) } } return nil } // HelpCmd tries to open the given help page in a horizontal split func (h *BufPane) HelpCmd(args []string) { if len(args) < 1 { // Open the default help if the user just typed "> help" h.openHelp("help") } else { if config.FindRuntimeFile(config.RTHelp, args[0]) != nil { err := h.openHelp(args[0]) if err != nil { InfoBar.Error(err) } } else { InfoBar.Error("Sorry, no help for ", args[0]) } } } // VSplitCmd opens a vertical split with file given in the first argument // If no file is given, it opens an empty buffer in a new split func (h *BufPane) VSplitCmd(args []string) { if len(args) == 0 { // Open an empty vertical split h.VSplitAction() return } buf, err := buffer.NewBufferFromFile(args[0], buffer.BTDefault) if err != nil { InfoBar.Error(err) return } h.VSplitBuf(buf) } // HSplitCmd opens a horizontal split with file given in the first argument // If no file is given, it opens an empty buffer in a new split func (h *BufPane) HSplitCmd(args []string) { if len(args) == 0 { // Open an empty horizontal split h.HSplitAction() return } buf, err := buffer.NewBufferFromFile(args[0], buffer.BTDefault) if err != nil { InfoBar.Error(err) return } h.HSplitBuf(buf) } // EvalCmd evaluates a lua expression func (h *BufPane) EvalCmd(args []string) { InfoBar.Error("Eval unsupported") } // NewTabCmd opens the given file in a new tab func (h *BufPane) NewTabCmd(args []string) { width, height := screen.Screen.Size() iOffset := config.GetInfoBarOffset() if len(args) > 0 { for _, a := range args { b, err := buffer.NewBufferFromFile(a, buffer.BTDefault) if err != nil { InfoBar.Error(err) return } tp := NewTabFromBuffer(0, 0, width, height-1-iOffset, b) Tabs.AddTab(tp) Tabs.SetActive(len(Tabs.List) - 1) } } else { b := buffer.NewBufferFromString("", "", buffer.BTDefault) tp := NewTabFromBuffer(0, 0, width, height-iOffset, b) Tabs.AddTab(tp) Tabs.SetActive(len(Tabs.List) - 1) } } func doSetGlobalOptionNative(option string, nativeValue interface{}) error { if reflect.DeepEqual(config.GlobalSettings[option], nativeValue) { return nil } config.GlobalSettings[option] = nativeValue config.ModifiedSettings[option] = true delete(config.VolatileSettings, option) if option == "colorscheme" { // LoadSyntaxFiles() config.InitColorscheme() for _, b := range buffer.OpenBuffers { b.UpdateRules() } } else if option == "infobar" || option == "keymenu" { Tabs.Resize() } else if option == "mouse" { if !nativeValue.(bool) { screen.Screen.DisableMouse() } else { screen.Screen.EnableMouse() } } else if option == "autosave" { if nativeValue.(float64) > 0 { config.SetAutoTime(nativeValue.(float64)) } else { config.SetAutoTime(0) } } else if option == "paste" { screen.Screen.SetPaste(nativeValue.(bool)) } else if option == "clipboard" { m := clipboard.SetMethod(nativeValue.(string)) err := clipboard.Initialize(m) if err != nil { return err } } else { for _, pl := range config.Plugins { if option == pl.Name { if nativeValue.(bool) && !pl.Loaded { pl.Load() _, err := pl.Call("init") if err != nil && err != config.ErrNoSuchFunction { screen.TermMessage(err) } } else if !nativeValue.(bool) && pl.Loaded { _, err := pl.Call("deinit") if err != nil && err != config.ErrNoSuchFunction { screen.TermMessage(err) } } } } } return nil } func SetGlobalOptionNative(option string, nativeValue interface{}) error { if err := config.OptionIsValid(option, nativeValue); err != nil { return err } // check for local option first... for _, s := range config.LocalSettings { if s == option { return MainTab().CurPane().Buf.SetOptionNative(option, nativeValue) } } // ...if it's not local continue with the globals... if err := doSetGlobalOptionNative(option, nativeValue); err != nil { return err } // ...at last check the buffer locals for _, b := range buffer.OpenBuffers { b.DoSetOptionNative(option, nativeValue) delete(b.LocalSettings, option) } return config.WriteSettings(filepath.Join(config.ConfigDir, "settings.json")) } func SetGlobalOption(option, value string) error { if _, ok := config.GlobalSettings[option]; !ok { return config.ErrInvalidOption } nativeValue, err := config.GetNativeValue(option, config.GlobalSettings[option], value) if err != nil { return err } return SetGlobalOptionNative(option, nativeValue) } // ResetCmd resets a setting to its default value func (h *BufPane) ResetCmd(args []string) { if len(args) < 1 { InfoBar.Error("Not enough arguments") return } option := args[0] defaults := config.DefaultAllSettings() if _, ok := defaults[option]; ok { SetGlobalOptionNative(option, defaults[option]) return } InfoBar.Error(config.ErrInvalidOption) } // SetCmd sets an option func (h *BufPane) SetCmd(args []string) { if len(args) < 2 { InfoBar.Error("Not enough arguments") return } option := args[0] value := args[1] err := SetGlobalOption(option, value) if err == config.ErrInvalidOption { err := h.Buf.SetOption(option, value) if err != nil { InfoBar.Error(err) } } else if err != nil { InfoBar.Error(err) } } // SetLocalCmd sets an option local to the buffer func (h *BufPane) SetLocalCmd(args []string) { if len(args) < 2 { InfoBar.Error("Not enough arguments") return } option := args[0] value := args[1] err := h.Buf.SetOption(option, value) if err != nil { InfoBar.Error(err) } } // ShowCmd shows the value of the given option func (h *BufPane) ShowCmd(args []string) { if len(args) < 1 { InfoBar.Error("Please provide an option to show") return } var option interface{} if opt, ok := h.Buf.Settings[args[0]]; ok { option = opt } else if opt, ok := config.GlobalSettings[args[0]]; ok { option = opt } if option == nil { InfoBar.Error(args[0], " is not a valid option") return } InfoBar.Message(option) } func parseKeyArg(arg string) string { // If this is a raw escape sequence, convert it to its raw byte form return strings.ReplaceAll(arg, "\\x1b", "\x1b") } // ShowKeyCmd displays the action that a key is bound to func (h *BufPane) ShowKeyCmd(args []string) { if len(args) < 1 { InfoBar.Error("Please provide a key to show") return } event, err := findEvent(parseKeyArg(args[0])) if err != nil { InfoBar.Error(err) return } if action, ok := config.Bindings["buffer"][event.Name()]; ok { InfoBar.Message(action) } else { InfoBar.Message(args[0], " has no binding") } } // BindCmd creates a new keybinding func (h *BufPane) BindCmd(args []string) { if len(args) < 2 { InfoBar.Error("Not enough arguments") return } _, err := TryBindKey(parseKeyArg(args[0]), args[1], true) if err != nil { InfoBar.Error(err) } } // UnbindCmd binds a key to its default action func (h *BufPane) UnbindCmd(args []string) { if len(args) < 1 { InfoBar.Error("Not enough arguments") return } err := UnbindKey(parseKeyArg(args[0])) if err != nil { InfoBar.Error(err) } } // RunCmd runs a shell command in the background func (h *BufPane) RunCmd(args []string) { runf, err := shell.RunBackgroundShell(shellquote.Join(args...)) if err != nil { InfoBar.Error(err) } else { go func() { InfoBar.Message(runf()) screen.Redraw() }() } } // QuitCmd closes the main view func (h *BufPane) QuitCmd(args []string) { h.Quit() } // GotoCmd is a command that will send the cursor to a certain // position in the buffer // For example: `goto line`, or `goto line:col` func (h *BufPane) GotoCmd(args []string) { line, col, err := h.parseLineCol(args) if err != nil { InfoBar.Error(err) return } if line < 0 { line = h.Buf.LinesNum() + 1 + line } line = util.Clamp(line-1, 0, h.Buf.LinesNum()-1) col = util.Clamp(col-1, 0, util.CharacterCount(h.Buf.LineBytes(line))) h.RemoveAllMultiCursors() h.Cursor.Deselect(true) h.GotoLoc(buffer.Loc{col, line}) } // JumpCmd is a command that will send the cursor to a certain relative // position in the buffer // For example: `jump line`, `jump -line`, or `jump -line:col` func (h *BufPane) JumpCmd(args []string) { line, col, err := h.parseLineCol(args) if err != nil { InfoBar.Error(err) return } line = h.Buf.GetActiveCursor().Y + 1 + line line = util.Clamp(line-1, 0, h.Buf.LinesNum()-1) col = util.Clamp(col-1, 0, util.CharacterCount(h.Buf.LineBytes(line))) h.RemoveAllMultiCursors() h.Cursor.Deselect(true) h.GotoLoc(buffer.Loc{col, line}) } // parseLineCol is a helper to parse the input of GotoCmd and JumpCmd func (h *BufPane) parseLineCol(args []string) (line int, col int, err error) { if len(args) <= 0 { return 0, 0, errors.New("Not enough arguments") } line, col = 0, 0 if strings.Contains(args[0], ":") { parts := strings.SplitN(args[0], ":", 2) line, err = strconv.Atoi(parts[0]) if err != nil { return 0, 0, err } col, err = strconv.Atoi(parts[1]) if err != nil { return 0, 0, err } } else { line, err = strconv.Atoi(args[0]) if err != nil { return 0, 0, err } } return line, col, nil } // SaveCmd saves the buffer optionally with an argument file name func (h *BufPane) SaveCmd(args []string) { if len(args) == 0 { h.Save() } else { h.Buf.SaveAs(args[0]) } } // ReplaceCmd runs search and replace func (h *BufPane) ReplaceCmd(args []string) { if len(args) < 2 || len(args) > 4 { // We need to find both a search and replace expression InfoBar.Error("Invalid replace statement: " + strings.Join(args, " ")) return } all := false noRegex := false foundSearch := false foundReplace := false var search string var replaceStr string for _, arg := range args { switch arg { case "-a": all = true case "-l": noRegex = true default: if !foundSearch { foundSearch = true search = arg } else if !foundReplace { foundReplace = true replaceStr = arg } else { InfoBar.Error("Invalid flag: " + arg) return } } } if noRegex { search = regexp.QuoteMeta(search) } replace := []byte(replaceStr) var regex *regexp.Regexp var err error if h.Buf.Settings["ignorecase"].(bool) { regex, err = regexp.Compile("(?im)" + search) } else { regex, err = regexp.Compile("(?m)" + search) } if err != nil { // There was an error with the user's regex InfoBar.Error(err) return } nreplaced := 0 start := h.Buf.Start() end := h.Buf.End() selection := h.Cursor.HasSelection() if selection { start = h.Cursor.CurSelection[0] end = h.Cursor.CurSelection[1] } if all { nreplaced, _ = h.Buf.ReplaceRegex(start, end, regex, replace, !noRegex) } else { inRange := func(l buffer.Loc) bool { return l.GreaterEqual(start) && l.LessEqual(end) } searchLoc := h.Cursor.Loc var doReplacement func() doReplacement = func() { locs, found, err := h.Buf.FindNext(search, start, end, searchLoc, true, true) if err != nil { InfoBar.Error(err) return } if !found || !inRange(locs[0]) || !inRange(locs[1]) { h.Cursor.ResetSelection() h.Buf.RelocateCursors() return } h.Cursor.SetSelectionStart(locs[0]) h.Cursor.SetSelectionEnd(locs[1]) h.GotoLoc(locs[0]) h.Buf.LastSearch = search h.Buf.LastSearchRegex = true h.Buf.HighlightSearch = h.Buf.Settings["hlsearch"].(bool) InfoBar.YNPrompt("Perform replacement (y,n,esc)", func(yes, canceled bool) { if !canceled && yes { _, nrunes := h.Buf.ReplaceRegex(locs[0], locs[1], regex, replace, !noRegex) searchLoc = locs[0] searchLoc.X += nrunes + locs[0].Diff(locs[1], h.Buf) if end.Y == locs[1].Y { end = end.Move(nrunes, h.Buf) } h.Cursor.Loc = searchLoc nreplaced++ } else if !canceled && !yes { searchLoc = locs[1] } else if canceled { h.Cursor.ResetSelection() h.Buf.RelocateCursors() return } doReplacement() }) } doReplacement() } h.Buf.RelocateCursors() h.Relocate() var s string if nreplaced > 1 { s = fmt.Sprintf("Replaced %d occurrences of %s", nreplaced, search) } else if nreplaced == 1 { s = fmt.Sprintf("Replaced 1 occurrence of %s", search) } else { s = fmt.Sprintf("Nothing matched %s", search) } if selection { s += " in selection" } InfoBar.Message(s) } // ReplaceAllCmd replaces search term all at once func (h *BufPane) ReplaceAllCmd(args []string) { // aliased to Replace command h.ReplaceCmd(append(args, "-a")) } // TermCmd opens a terminal in the current view func (h *BufPane) TermCmd(args []string) { ps := h.tab.Panes if !TermEmuSupported { InfoBar.Error("Terminal emulator not supported on this system") return } if len(args) == 0 { sh := os.Getenv("SHELL") if sh == "" { InfoBar.Error("Shell environment not found") return } args = []string{sh} } term := func(i int, newtab bool) { t := new(shell.Terminal) err := t.Start(args, false, true, nil, nil) if err != nil { InfoBar.Error(err) return } id := h.ID() if newtab { h.AddTab() i = 0 id = MainTab().Panes[0].ID() } else { MainTab().Panes[i].Close() } v := h.GetView() tp, err := NewTermPane(v.X, v.Y, v.Width, v.Height, t, id, MainTab()) if err != nil { InfoBar.Error(err) return } MainTab().Panes[i] = tp MainTab().SetActive(i) } // If there is only one open file we make a new tab instead of overwriting it newtab := len(MainTab().Panes) == 1 && len(Tabs.List) == 1 if newtab { term(0, true) return } for i, p := range ps { if p.ID() == h.ID() { if h.Buf.Modified() { InfoBar.YNPrompt("Save changes to "+h.Buf.GetName()+" before closing? (y,n,esc)", func(yes, canceled bool) { if !canceled && !yes { term(i, false) } else if !canceled && yes { h.Save() term(i, false) } }) } else { term(i, false) } } } } // HandleCommand handles input from the user func (h *BufPane) HandleCommand(input string) { args, err := shellquote.Split(input) if err != nil { InfoBar.Error("Error parsing args ", err) return } if len(args) == 0 { return } inputCmd := args[0] if _, ok := commands[inputCmd]; !ok { InfoBar.Error("Unknown command ", inputCmd) } else { WriteLog("> " + input + "\n") commands[inputCmd].action(h, args[1:]) WriteLog("\n") } } micro-2.0.14/internal/action/defaults.go0000664000175000017510000000071114663411671017477 0ustar nileshnileshpackage action var termdefaults = map[string]string{ "": "Exit", "": "CommandMode", "": "NextSplit", } // DefaultBindings returns a map containing micro's default keybindings func DefaultBindings(pane string) map[string]string { switch pane { case "command": return infodefaults case "buffer": return bufdefaults case "terminal": return termdefaults default: return map[string]string{} } } micro-2.0.14/internal/action/defaults_darwin.go0000664000175000017510000001353614663411671021054 0ustar nileshnileshpackage action var bufdefaults = map[string]string{ "Up": "CursorUp", "Down": "CursorDown", "Right": "CursorRight", "Left": "CursorLeft", "ShiftUp": "SelectUp", "ShiftDown": "SelectDown", "ShiftLeft": "SelectLeft", "ShiftRight": "SelectRight", "AltLeft": "WordLeft", "AltRight": "WordRight", "AltUp": "MoveLinesUp", "AltDown": "MoveLinesDown", "AltShiftRight": "SelectWordRight", "AltShiftLeft": "SelectWordLeft", "CtrlLeft": "StartOfTextToggle", "CtrlRight": "EndOfLine", "CtrlShiftLeft": "SelectToStartOfTextToggle", "ShiftHome": "SelectToStartOfTextToggle", "CtrlShiftRight": "SelectToEndOfLine", "ShiftEnd": "SelectToEndOfLine", "CtrlUp": "CursorStart", "CtrlDown": "CursorEnd", "CtrlShiftUp": "SelectToStart", "CtrlShiftDown": "SelectToEnd", "Alt-{": "ParagraphPrevious", "Alt-}": "ParagraphNext", "Enter": "InsertNewline", "CtrlH": "Backspace", "Backspace": "Backspace", "OldBackspace": "Backspace", "Alt-CtrlH": "DeleteWordLeft", "Alt-Backspace": "DeleteWordLeft", "Tab": "Autocomplete|IndentSelection|InsertTab", "Backtab": "CycleAutocompleteBack|OutdentSelection|OutdentLine", "Ctrl-o": "OpenFile", "Ctrl-s": "Save", "Ctrl-f": "Find", "Alt-F": "FindLiteral", "Ctrl-n": "FindNext", "Ctrl-p": "FindPrevious", "Alt-[": "DiffPrevious|CursorStart", "Alt-]": "DiffNext|CursorEnd", "Ctrl-z": "Undo", "Ctrl-y": "Redo", "Ctrl-c": "CopyLine|Copy", "Ctrl-x": "Cut", "Ctrl-k": "CutLine", "Ctrl-d": "DuplicateLine", "Ctrl-v": "Paste", "Ctrl-a": "SelectAll", "Ctrl-t": "AddTab", "Alt-,": "PreviousTab", "Alt-.": "NextTab", "Home": "StartOfTextToggle", "End": "EndOfLine", "CtrlHome": "CursorStart", "CtrlEnd": "CursorEnd", "PageUp": "CursorPageUp", "PageDown": "CursorPageDown", "CtrlPageUp": "PreviousTab", "CtrlPageDown": "NextTab", "ShiftPageUp": "SelectPageUp", "ShiftPageDown": "SelectPageDown", "Ctrl-g": "ToggleHelp", "Alt-g": "ToggleKeyMenu", "Ctrl-r": "ToggleRuler", "Ctrl-l": "command-edit:goto ", "Delete": "Delete", "Ctrl-b": "ShellMode", "Ctrl-q": "Quit", "Ctrl-e": "CommandMode", "Ctrl-w": "NextSplit", "Ctrl-u": "ToggleMacro", "Ctrl-j": "PlayMacro", "Insert": "ToggleOverwriteMode", // Emacs-style keybindings "Alt-f": "WordRight", "Alt-b": "WordLeft", "Alt-a": "StartOfText", "Alt-e": "EndOfLine", // "Alt-p": "CursorUp", // "Alt-n": "CursorDown", // Integration with file managers "F2": "Save", "F3": "Find", "F4": "Quit", "F7": "Find", "F10": "Quit", "Esc": "Escape,Deselect,ClearInfo,RemoveAllMultiCursors,UnhighlightSearch", // Mouse bindings "MouseWheelUp": "ScrollUp", "MouseWheelDown": "ScrollDown", "MouseLeft": "MousePress", "MouseLeftDrag": "MouseDrag", "MouseLeftRelease": "MouseRelease", "MouseMiddle": "PastePrimary", "Ctrl-MouseLeft": "MouseMultiCursor", "Alt-n": "SpawnMultiCursor", "AltShiftUp": "SpawnMultiCursorUp", "AltShiftDown": "SpawnMultiCursorDown", "Alt-m": "SpawnMultiCursorSelect", "Alt-p": "RemoveMultiCursor", "Alt-c": "RemoveAllMultiCursors", "Alt-x": "SkipMultiCursor", } var infodefaults = map[string]string{ "Up": "HistoryUp", "Down": "HistoryDown", "Right": "CursorRight", "Left": "CursorLeft", "ShiftUp": "SelectUp", "ShiftDown": "SelectDown", "ShiftLeft": "SelectLeft", "ShiftRight": "SelectRight", "AltLeft": "WordLeft", "AltRight": "WordRight", "AltUp": "CursorStart", "AltDown": "CursorEnd", "AltShiftRight": "SelectWordRight", "AltShiftLeft": "SelectWordLeft", "CtrlLeft": "StartOfTextToggle", "CtrlRight": "EndOfLine", "CtrlShiftLeft": "SelectToStartOfTextToggle", "ShiftHome": "SelectToStartOfTextToggle", "CtrlShiftRight": "SelectToEndOfLine", "ShiftEnd": "SelectToEndOfLine", "CtrlUp": "CursorStart", "CtrlDown": "CursorEnd", "CtrlShiftUp": "SelectToStart", "CtrlShiftDown": "SelectToEnd", "Enter": "ExecuteCommand", "CtrlH": "Backspace", "Backspace": "Backspace", "OldBackspace": "Backspace", "Alt-CtrlH": "DeleteWordLeft", "Alt-Backspace": "DeleteWordLeft", "Tab": "CommandComplete", "Backtab": "CycleAutocompleteBack", "Ctrl-z": "Undo", "Ctrl-y": "Redo", "Ctrl-c": "CopyLine|Copy", "Ctrl-x": "Cut", "Ctrl-k": "CutLine", "Ctrl-v": "Paste", "Home": "StartOfTextToggle", "End": "EndOfLine", "CtrlHome": "CursorStart", "CtrlEnd": "CursorEnd", "Delete": "Delete", "Ctrl-q": "AbortCommand", "Ctrl-e": "EndOfLine", "Ctrl-a": "StartOfLine", "Ctrl-w": "DeleteWordLeft", "Insert": "ToggleOverwriteMode", "Ctrl-b": "WordLeft", "Ctrl-f": "WordRight", "Ctrl-d": "DeleteWordLeft", "Ctrl-m": "ExecuteCommand", "Ctrl-n": "HistoryDown", "Ctrl-p": "HistoryUp", "Ctrl-u": "SelectToStart", // Emacs-style keybindings "Alt-f": "WordRight", "Alt-b": "WordLeft", "Alt-a": "StartOfText", "Alt-e": "EndOfLine", // Integration with file managers "F10": "AbortCommand", "Esc": "AbortCommand", // Mouse bindings "MouseWheelUp": "HistoryUp", "MouseWheelDown": "HistoryDown", "MouseLeft": "MousePress", "MouseLeftDrag": "MouseDrag", "MouseLeftRelease": "MouseRelease", "MouseMiddle": "PastePrimary", } micro-2.0.14/internal/action/defaults_other.go0000664000175000017510000001360414663411671020705 0ustar nileshnilesh//go:build !darwin // +build !darwin package action var bufdefaults = map[string]string{ "Up": "CursorUp", "Down": "CursorDown", "Right": "CursorRight", "Left": "CursorLeft", "ShiftUp": "SelectUp", "ShiftDown": "SelectDown", "ShiftLeft": "SelectLeft", "ShiftRight": "SelectRight", "CtrlLeft": "WordLeft", "CtrlRight": "WordRight", "AltUp": "MoveLinesUp", "AltDown": "MoveLinesDown", "CtrlShiftRight": "SelectWordRight", "CtrlShiftLeft": "SelectWordLeft", "AltLeft": "StartOfTextToggle", "AltRight": "EndOfLine", "AltShiftLeft": "SelectToStartOfTextToggle", "ShiftHome": "SelectToStartOfTextToggle", "AltShiftRight": "SelectToEndOfLine", "ShiftEnd": "SelectToEndOfLine", "CtrlUp": "CursorStart", "CtrlDown": "CursorEnd", "CtrlShiftUp": "SelectToStart", "CtrlShiftDown": "SelectToEnd", "Alt-{": "ParagraphPrevious", "Alt-}": "ParagraphNext", "Enter": "InsertNewline", "CtrlH": "Backspace", "Backspace": "Backspace", "OldBackspace": "Backspace", "Alt-CtrlH": "DeleteWordLeft", "Alt-Backspace": "DeleteWordLeft", "Tab": "Autocomplete|IndentSelection|InsertTab", "Backtab": "CycleAutocompleteBack|OutdentSelection|OutdentLine", "Ctrl-o": "OpenFile", "Ctrl-s": "Save", "Ctrl-f": "Find", "Alt-F": "FindLiteral", "Ctrl-n": "FindNext", "Ctrl-p": "FindPrevious", "Alt-[": "DiffPrevious|CursorStart", "Alt-]": "DiffNext|CursorEnd", "Ctrl-z": "Undo", "Ctrl-y": "Redo", "Ctrl-c": "CopyLine|Copy", "Ctrl-x": "Cut", "Ctrl-k": "CutLine", "Ctrl-d": "DuplicateLine", "Ctrl-v": "Paste", "Ctrl-a": "SelectAll", "Ctrl-t": "AddTab", "Alt-,": "PreviousTab", "Alt-.": "NextTab", "Home": "StartOfTextToggle", "End": "EndOfLine", "CtrlHome": "CursorStart", "CtrlEnd": "CursorEnd", "PageUp": "CursorPageUp", "PageDown": "CursorPageDown", "CtrlPageUp": "PreviousTab", "CtrlPageDown": "NextTab", "ShiftPageUp": "SelectPageUp", "ShiftPageDown": "SelectPageDown", "Ctrl-g": "ToggleHelp", "Alt-g": "ToggleKeyMenu", "Ctrl-r": "ToggleRuler", "Ctrl-l": "command-edit:goto ", "Delete": "Delete", "Ctrl-b": "ShellMode", "Ctrl-q": "Quit", "Ctrl-e": "CommandMode", "Ctrl-w": "NextSplit", "Ctrl-u": "ToggleMacro", "Ctrl-j": "PlayMacro", "Insert": "ToggleOverwriteMode", // Emacs-style keybindings "Alt-f": "WordRight", "Alt-b": "WordLeft", "Alt-a": "StartOfText", "Alt-e": "EndOfLine", // "Alt-p": "CursorUp", // "Alt-n": "CursorDown", // Integration with file managers "F2": "Save", "F3": "Find", "F4": "Quit", "F7": "Find", "F10": "Quit", "Esc": "Escape,Deselect,ClearInfo,RemoveAllMultiCursors,UnhighlightSearch", // Mouse bindings "MouseWheelUp": "ScrollUp", "MouseWheelDown": "ScrollDown", "MouseLeft": "MousePress", "MouseLeftDrag": "MouseDrag", "MouseLeftRelease": "MouseRelease", "MouseMiddle": "PastePrimary", "Ctrl-MouseLeft": "MouseMultiCursor", "Alt-n": "SpawnMultiCursor", "Alt-m": "SpawnMultiCursorSelect", "AltShiftUp": "SpawnMultiCursorUp", "AltShiftDown": "SpawnMultiCursorDown", "Alt-p": "RemoveMultiCursor", "Alt-c": "RemoveAllMultiCursors", "Alt-x": "SkipMultiCursor", } var infodefaults = map[string]string{ "Up": "HistoryUp", "Down": "HistoryDown", "Right": "CursorRight", "Left": "CursorLeft", "ShiftUp": "SelectUp", "ShiftDown": "SelectDown", "ShiftLeft": "SelectLeft", "ShiftRight": "SelectRight", "AltLeft": "StartOfTextToggle", "AltRight": "EndOfLine", "AltUp": "CursorStart", "AltDown": "CursorEnd", "AltShiftRight": "SelectWordRight", "AltShiftLeft": "SelectWordLeft", "CtrlLeft": "WordLeft", "CtrlRight": "WordRight", "CtrlShiftLeft": "SelectToStartOfTextToggle", "ShiftHome": "SelectToStartOfTextToggle", "CtrlShiftRight": "SelectToEndOfLine", "ShiftEnd": "SelectToEndOfLine", "CtrlUp": "CursorStart", "CtrlDown": "CursorEnd", "CtrlShiftUp": "SelectToStart", "CtrlShiftDown": "SelectToEnd", "Enter": "ExecuteCommand", "CtrlH": "Backspace", "Backspace": "Backspace", "OldBackspace": "Backspace", "Alt-CtrlH": "DeleteWordLeft", "Alt-Backspace": "DeleteWordLeft", "Tab": "CommandComplete", "Backtab": "CycleAutocompleteBack", "Ctrl-z": "Undo", "Ctrl-y": "Redo", "Ctrl-c": "CopyLine|Copy", "Ctrl-x": "Cut", "Ctrl-k": "CutLine", "Ctrl-v": "Paste", "Home": "StartOfTextToggle", "End": "EndOfLine", "CtrlHome": "CursorStart", "CtrlEnd": "CursorEnd", "Delete": "Delete", "Ctrl-q": "AbortCommand", "Ctrl-e": "EndOfLine", "Ctrl-a": "StartOfLine", "Ctrl-w": "DeleteWordLeft", "Insert": "ToggleOverwriteMode", "Ctrl-b": "WordLeft", "Ctrl-f": "WordRight", "Ctrl-d": "DeleteWordLeft", "Ctrl-m": "ExecuteCommand", "Ctrl-n": "HistoryDown", "Ctrl-p": "HistoryUp", "Ctrl-u": "SelectToStart", // Emacs-style keybindings "Alt-f": "WordRight", "Alt-b": "WordLeft", "Alt-a": "StartOfText", "Alt-e": "EndOfLine", // Integration with file managers "F10": "AbortCommand", "Esc": "AbortCommand", // Mouse bindings "MouseWheelUp": "HistoryUp", "MouseWheelDown": "HistoryDown", "MouseLeft": "MousePress", "MouseLeftDrag": "MouseDrag", "MouseLeftRelease": "MouseRelease", "MouseMiddle": "PastePrimary", } micro-2.0.14/internal/action/events.go0000664000175000017510000000677614663411671017215 0ustar nileshnileshpackage action import ( "bytes" "errors" "fmt" "strings" "github.com/zyedidia/tcell/v2" ) type Event interface { Name() string } // RawEvent is simply an escape code // We allow users to directly bind escape codes // to get around some of a limitations of terminals type RawEvent struct { esc string } func (r RawEvent) Name() string { return r.esc } // KeyEvent is a key event containing a key code, // some possible modifiers (alt, ctrl, etc...) and // a rune if it was simply a character press // Note: to be compatible with tcell events, // for ctrl keys r=code type KeyEvent struct { code tcell.Key mod tcell.ModMask r rune any bool } func metaToAlt(mod tcell.ModMask) tcell.ModMask { if mod&tcell.ModMeta != 0 { mod &= ^tcell.ModMeta mod |= tcell.ModAlt } return mod } func keyEvent(e *tcell.EventKey) KeyEvent { ke := KeyEvent{ code: e.Key(), mod: metaToAlt(e.Modifiers()), } if e.Key() == tcell.KeyRune { ke.r = e.Rune() } return ke } func (k KeyEvent) Name() string { if k.any { return "" } s := "" m := []string{} if k.mod&tcell.ModShift != 0 { m = append(m, "Shift") } if k.mod&tcell.ModAlt != 0 { m = append(m, "Alt") } if k.mod&tcell.ModMeta != 0 { m = append(m, "Meta") } if k.mod&tcell.ModCtrl != 0 { m = append(m, "Ctrl") } ok := false if s, ok = tcell.KeyNames[k.code]; !ok { if k.code == tcell.KeyRune { s = string(k.r) } else { s = fmt.Sprintf("Key[%d]", k.code) } } if len(m) != 0 { if k.mod&tcell.ModCtrl != 0 && strings.HasPrefix(s, "Ctrl-") { s = s[5:] if len(s) == 1 { s = strings.ToLower(s) } } return fmt.Sprintf("%s-%s", strings.Join(m, "-"), s) } return s } // A KeySequence defines a list of consecutive // events. All events in the sequence must be KeyEvents // or MouseEvents. type KeySequenceEvent struct { keys []Event } func (k KeySequenceEvent) Name() string { buf := bytes.Buffer{} for _, e := range k.keys { buf.WriteByte('<') buf.WriteString(e.Name()) buf.WriteByte('>') } return buf.String() } type MouseState int const ( MousePress = iota MouseDrag MouseRelease ) // MouseEvent is a mouse event with a mouse button and // any possible key modifiers type MouseEvent struct { btn tcell.ButtonMask mod tcell.ModMask state MouseState } func (m MouseEvent) Name() string { mod := "" if m.mod&tcell.ModShift != 0 { mod = "Shift-" } if m.mod&tcell.ModAlt != 0 { mod = "Alt-" } if m.mod&tcell.ModMeta != 0 { mod = "Meta-" } if m.mod&tcell.ModCtrl != 0 { mod = "Ctrl-" } state := "" switch m.state { case MouseDrag: state = "Drag" case MouseRelease: state = "Release" } for k, v := range mouseEvents { if v == m.btn { return fmt.Sprintf("%s%s%s", mod, k, state) } } return "" } // ConstructEvent takes a tcell event and returns a micro // event. Note that tcell events can't express certain // micro events such as key sequences. This function is // mostly used for debugging/raw panes or constructing // intermediate micro events while parsing a sequence. func ConstructEvent(event tcell.Event) (Event, error) { switch e := event.(type) { case *tcell.EventKey: return keyEvent(e), nil case *tcell.EventRaw: return RawEvent{ esc: e.EscSeq(), }, nil case *tcell.EventMouse: return MouseEvent{ btn: e.Buttons(), mod: metaToAlt(e.Modifiers()), }, nil } return nil, errors.New("No micro event equivalent") } // A Handler will take a tcell event and execute it // appropriately type Handler interface { HandleEvent(tcell.Event) HandleCommand(string) } micro-2.0.14/internal/action/globals.go0000664000175000017510000000156214663411671017320 0ustar nileshnileshpackage action import "github.com/zyedidia/micro/v2/internal/buffer" // InfoBar is the global info bar. var InfoBar *InfoPane // LogBufPane is a global log buffer. var LogBufPane *BufPane // InitGlobals initializes the log buffer and the info bar func InitGlobals() { InfoBar = NewInfoBar() buffer.LogBuf = buffer.NewBufferFromString("", "Log", buffer.BTLog) } // GetInfoBar returns the infobar pane func GetInfoBar() *InfoPane { return InfoBar } // WriteLog writes a string to the log buffer func WriteLog(s string) { buffer.WriteLog(s) if LogBufPane != nil { LogBufPane.CursorEnd() } } // OpenLogBuf opens the log buffer from the current bufpane // If the current bufpane is a log buffer nothing happens, // otherwise the log buffer is opened in a horizontal split func (h *BufPane) OpenLogBuf() { LogBufPane = h.HSplitBuf(buffer.LogBuf) LogBufPane.CursorEnd() } micro-2.0.14/internal/action/infocomplete.go0000664000175000017510000002123014663411671020353 0ustar nileshnileshpackage action import ( "bytes" "sort" "strings" "github.com/zyedidia/micro/v2/internal/buffer" "github.com/zyedidia/micro/v2/internal/config" "github.com/zyedidia/micro/v2/internal/util" "github.com/zyedidia/micro/v2/pkg/highlight" ) // This file is meant (for now) for autocompletion in command mode, not // while coding. This helps micro autocomplete commands and then filenames // for example with `vsplit filename`. // CommandComplete autocompletes commands func CommandComplete(b *buffer.Buffer) ([]string, []string) { c := b.GetActiveCursor() input, argstart := b.GetArg() var suggestions []string for cmd := range commands { if strings.HasPrefix(cmd, input) { suggestions = append(suggestions, cmd) } } sort.Strings(suggestions) completions := make([]string, len(suggestions)) for i := range suggestions { completions[i] = util.SliceEndStr(suggestions[i], c.X-argstart) } return completions, suggestions } // HelpComplete autocompletes help topics func HelpComplete(b *buffer.Buffer) ([]string, []string) { c := b.GetActiveCursor() input, argstart := b.GetArg() var suggestions []string for _, file := range config.ListRuntimeFiles(config.RTHelp) { topic := file.Name() if strings.HasPrefix(topic, input) { suggestions = append(suggestions, topic) } } sort.Strings(suggestions) completions := make([]string, len(suggestions)) for i := range suggestions { completions[i] = util.SliceEndStr(suggestions[i], c.X-argstart) } return completions, suggestions } // colorschemeComplete tab-completes names of colorschemes. // This is just a heper value for OptionValueComplete func colorschemeComplete(input string) (string, []string) { var suggestions []string files := config.ListRuntimeFiles(config.RTColorscheme) for _, f := range files { if strings.HasPrefix(f.Name(), input) { suggestions = append(suggestions, f.Name()) } } var chosen string if len(suggestions) == 1 { chosen = suggestions[0] } return chosen, suggestions } // filetypeComplete autocompletes filetype func filetypeComplete(input string) (string, []string) { var suggestions []string // We cannot match filetypes just by names of syntax files, // since those names may be different from the actual filetype values // specified inside syntax files (e.g. "c++" filetype in cpp.yaml). // So we need to parse filetype values out of those files. for _, f := range config.ListRealRuntimeFiles(config.RTSyntax) { data, err := f.Data() if err != nil { continue } header, err := highlight.MakeHeaderYaml(data) if err != nil { continue } // Prevent duplicated defaults if header.FileType == "off" || header.FileType == "unknown" { continue } if strings.HasPrefix(header.FileType, input) { suggestions = append(suggestions, header.FileType) } } headerLoop: for _, f := range config.ListRuntimeFiles(config.RTSyntaxHeader) { data, err := f.Data() if err != nil { continue } header, err := highlight.MakeHeader(data) if err != nil { continue } for _, v := range suggestions { if v == header.FileType { continue headerLoop } } if strings.HasPrefix(header.FileType, input) { suggestions = append(suggestions, header.FileType) } } if strings.HasPrefix("off", input) { suggestions = append(suggestions, "off") } var chosen string if len(suggestions) == 1 { chosen = suggestions[0] } return chosen, suggestions } func contains(s []string, e string) bool { for _, a := range s { if a == e { return true } } return false } // OptionComplete autocompletes options func OptionComplete(b *buffer.Buffer) ([]string, []string) { c := b.GetActiveCursor() input, argstart := b.GetArg() var suggestions []string for option := range config.GlobalSettings { if strings.HasPrefix(option, input) { suggestions = append(suggestions, option) } } // for option := range localSettings { // if strings.HasPrefix(option, input) && !contains(suggestions, option) { // suggestions = append(suggestions, option) // } // } sort.Strings(suggestions) completions := make([]string, len(suggestions)) for i := range suggestions { completions[i] = util.SliceEndStr(suggestions[i], c.X-argstart) } return completions, suggestions } // OptionValueComplete completes values for various options func OptionValueComplete(b *buffer.Buffer) ([]string, []string) { c := b.GetActiveCursor() l := b.LineBytes(c.Y) l = util.SliceStart(l, c.X) input, argstart := b.GetArg() completeValue := false args := bytes.Split(l, []byte{' '}) if len(args) >= 2 { // localSettings := config.DefaultLocalSettings() for option := range config.GlobalSettings { if option == string(args[len(args)-2]) { completeValue = true break } } // for option := range localSettings { // if option == string(args[len(args)-2]) { // completeValue = true // break // } // } } if !completeValue { return OptionComplete(b) } inputOpt := string(args[len(args)-2]) inputOpt = strings.TrimSpace(inputOpt) var suggestions []string // localSettings := config.DefaultLocalSettings() var optionVal interface{} for k, option := range config.GlobalSettings { if k == inputOpt { optionVal = option } } // for k, option := range localSettings { // if k == inputOpt { // optionVal = option // } // } switch optionVal.(type) { case bool: if strings.HasPrefix("on", input) { suggestions = append(suggestions, "on") } else if strings.HasPrefix("true", input) { suggestions = append(suggestions, "true") } if strings.HasPrefix("off", input) { suggestions = append(suggestions, "off") } else if strings.HasPrefix("false", input) { suggestions = append(suggestions, "false") } case string: switch inputOpt { case "colorscheme": _, suggestions = colorschemeComplete(input) case "filetype": _, suggestions = filetypeComplete(input) case "sucmd": if strings.HasPrefix("sudo", input) { suggestions = append(suggestions, "sudo") } if strings.HasPrefix("doas", input) { suggestions = append(suggestions, "doas") } default: if choices, ok := config.OptionChoices[inputOpt]; ok { for _, choice := range choices { if strings.HasPrefix(choice, input) { suggestions = append(suggestions, choice) } } } } } sort.Strings(suggestions) completions := make([]string, len(suggestions)) for i := range suggestions { completions[i] = util.SliceEndStr(suggestions[i], c.X-argstart) } return completions, suggestions } // PluginCmdComplete autocompletes the plugin command func PluginCmdComplete(b *buffer.Buffer) ([]string, []string) { c := b.GetActiveCursor() input, argstart := b.GetArg() var suggestions []string for _, cmd := range PluginCmds { if strings.HasPrefix(cmd, input) { suggestions = append(suggestions, cmd) } } sort.Strings(suggestions) completions := make([]string, len(suggestions)) for i := range suggestions { completions[i] = util.SliceEndStr(suggestions[i], c.X-argstart) } return completions, suggestions } // PluginComplete completes values for the plugin command func PluginComplete(b *buffer.Buffer) ([]string, []string) { c := b.GetActiveCursor() l := b.LineBytes(c.Y) l = util.SliceStart(l, c.X) input, argstart := b.GetArg() completeValue := false args := bytes.Split(l, []byte{' '}) if len(args) >= 2 { for _, cmd := range PluginCmds { if cmd == string(args[len(args)-2]) { completeValue = true break } } } if !completeValue { return PluginCmdComplete(b) } var suggestions []string for _, pl := range config.Plugins { if strings.HasPrefix(pl.Name, input) { suggestions = append(suggestions, pl.Name) } } sort.Strings(suggestions) completions := make([]string, len(suggestions)) for i := range suggestions { completions[i] = util.SliceEndStr(suggestions[i], c.X-argstart) } return completions, suggestions } // PluginNameComplete completes with the names of loaded plugins // func PluginNameComplete(b *buffer.Buffer) ([]string, []string) { // c := b.GetActiveCursor() // input, argstart := buffer.GetArg(b) // // var suggestions []string // for _, pp := range config.GetAllPluginPackages(nil) { // if strings.HasPrefix(pp.Name, input) { // suggestions = append(suggestions, pp.Name) // } // } // // sort.Strings(suggestions) // completions := make([]string, len(suggestions)) // for i := range suggestions { // completions[i] = util.SliceEndStr(suggestions[i], c.X-argstart) // } // return completions, suggestions // } // // MakeCompletion registers a function from a plugin for autocomplete commands // func MakeCompletion(function string) Completion { // pluginCompletions = append(pluginCompletions, LuaFunctionComplete(function)) // return Completion(-len(pluginCompletions)) // } micro-2.0.14/internal/action/infopane.go0000664000175000017510000001354514663411671017500 0ustar nileshnileshpackage action import ( "bytes" "github.com/zyedidia/micro/v2/internal/buffer" "github.com/zyedidia/micro/v2/internal/config" "github.com/zyedidia/micro/v2/internal/display" "github.com/zyedidia/micro/v2/internal/info" "github.com/zyedidia/micro/v2/internal/util" "github.com/zyedidia/tcell/v2" ) type InfoKeyAction func(*InfoPane) var InfoBindings *KeyTree var InfoBufBindings *KeyTree func init() { InfoBindings = NewKeyTree() InfoBufBindings = NewKeyTree() } func InfoMapEvent(k Event, action string) { config.Bindings["command"][k.Name()] = action switch e := k.(type) { case KeyEvent, KeySequenceEvent, RawEvent: infoMapKey(e, action) case MouseEvent: infoMapMouse(e, action) } } func infoMapKey(k Event, action string) { if f, ok := InfoKeyActions[action]; ok { InfoBindings.RegisterKeyBinding(k, InfoKeyActionGeneral(f)) } else if f, ok := BufKeyActions[action]; ok { InfoBufBindings.RegisterKeyBinding(k, BufKeyActionGeneral(f)) } } func infoMapMouse(k MouseEvent, action string) { // TODO: map mouse if f, ok := BufMouseActions[action]; ok { InfoBufBindings.RegisterMouseBinding(k, BufMouseActionGeneral(f)) } else { infoMapKey(k, action) } } func InfoKeyActionGeneral(a InfoKeyAction) PaneKeyAction { return func(p Pane) bool { a(p.(*InfoPane)) return true } } type InfoPane struct { *BufPane *info.InfoBuf } func NewInfoPane(ib *info.InfoBuf, w display.BWindow, tab *Tab) *InfoPane { ip := new(InfoPane) ip.InfoBuf = ib ip.BufPane = NewBufPane(ib.Buffer, w, tab) ip.BufPane.bindings = InfoBufBindings return ip } func NewInfoBar() *InfoPane { ib := info.NewBuffer() w := display.NewInfoWindow(ib) return NewInfoPane(ib, w, nil) } func (h *InfoPane) Close() { h.InfoBuf.Close() h.BufPane.Close() } func (h *InfoPane) HandleEvent(event tcell.Event) { switch e := event.(type) { case *tcell.EventResize: // TODO case *tcell.EventKey: ke := keyEvent(e) done := h.DoKeyEvent(ke) hasYN := h.HasYN if e.Key() == tcell.KeyRune && hasYN { y := e.Rune() == 'y' || e.Rune() == 'Y' n := e.Rune() == 'n' || e.Rune() == 'N' if y || n { h.YNResp = y h.DonePrompt(false) InfoBindings.ResetEvents() InfoBufBindings.ResetEvents() } } if e.Key() == tcell.KeyRune && !done && !hasYN { h.DoRuneInsert(e.Rune()) done = true } if done && h.HasPrompt && !hasYN { resp := string(h.LineBytes(0)) hist := h.History[h.PromptType] if resp != hist[h.HistoryNum] { h.HistoryNum = len(hist) - 1 hist[h.HistoryNum] = resp h.HistorySearch = false } if h.EventCallback != nil { h.EventCallback(resp) } } default: h.BufPane.HandleEvent(event) } } // DoKeyEvent executes a key event for the command bar, doing any overridden actions. // Returns true if the action was executed OR if there are more keys remaining // to process before executing an action (if this is a key sequence event). // Returns false if no action found. func (h *InfoPane) DoKeyEvent(e KeyEvent) bool { action, more := InfoBindings.NextEvent(e, nil) if action != nil && !more { action(h) InfoBindings.ResetEvents() return true } else if action == nil && !more { InfoBindings.ResetEvents() // return false //TODO:? } if !more { // If no infopane action found, try to find a bufpane action. // // TODO: this is buggy. For example, if the command bar has the following // two bindings: // // "": "HistoryUp", // "": "Paste", // // the 2nd binding (with a bufpane action) doesn't work, since // has been already consumed by the 1st binding (with an infopane action). // // We should either iterate both InfoBindings and InfoBufBindings keytrees // together, or just use the same keytree for both infopane and bufpane // bindings. action, more = InfoBufBindings.NextEvent(e, nil) if action != nil && !more { action(h.BufPane) InfoBufBindings.ResetEvents() return true } else if action == nil && !more { InfoBufBindings.ResetEvents() } } return more } // HistoryUp cycles history up func (h *InfoPane) HistoryUp() { h.UpHistory(h.History[h.PromptType]) } // HistoryDown cycles history down func (h *InfoPane) HistoryDown() { h.DownHistory(h.History[h.PromptType]) } // HistorySearchUp fetches the previous history item beginning with the text // in the infobuffer before cursor func (h *InfoPane) HistorySearchUp() { h.SearchUpHistory(h.History[h.PromptType]) } // HistorySearchDown fetches the next history item beginning with the text // in the infobuffer before cursor func (h *InfoPane) HistorySearchDown() { h.SearchDownHistory(h.History[h.PromptType]) } // Autocomplete begins autocompletion func (h *InfoPane) CommandComplete() { b := h.Buf if b.HasSuggestions { b.CycleAutocomplete(true) return } c := b.GetActiveCursor() l := b.LineBytes(0) l = util.SliceStart(l, c.X) args := bytes.Split(l, []byte{' '}) cmd := string(args[0]) if h.PromptType == "Command" { if len(args) == 1 { b.Autocomplete(CommandComplete) } else if action, ok := commands[cmd]; ok { if action.completer != nil { b.Autocomplete(action.completer) } } } else { // by default use filename autocompletion b.Autocomplete(buffer.FileComplete) } } // ExecuteCommand completes the prompt func (h *InfoPane) ExecuteCommand() { if !h.HasYN { h.DonePrompt(false) } } // AbortCommand cancels the prompt func (h *InfoPane) AbortCommand() { h.DonePrompt(true) } // InfoKeyActions contains the list of all possible key actions the infopane could execute var InfoKeyActions = map[string]InfoKeyAction{ "HistoryUp": (*InfoPane).HistoryUp, "HistoryDown": (*InfoPane).HistoryDown, "HistorySearchUp": (*InfoPane).HistorySearchUp, "HistorySearchDown": (*InfoPane).HistorySearchDown, "CommandComplete": (*InfoPane).CommandComplete, "ExecuteCommand": (*InfoPane).ExecuteCommand, "AbortCommand": (*InfoPane).AbortCommand, } micro-2.0.14/internal/action/keytree.go0000664000175000017510000001520514663411671017344 0ustar nileshnileshpackage action import ( "bytes" "github.com/zyedidia/tcell/v2" ) type PaneKeyAction func(Pane) bool type PaneMouseAction func(Pane, *tcell.EventMouse) bool type PaneKeyAnyAction func(Pane, []KeyEvent) bool // A KeyTreeNode stores a single node in the KeyTree (trie). The // children are stored as a map, and any node may store a list of // actions (the list will be nil if no actions correspond to a certain // node) type KeyTreeNode struct { children map[Event]*KeyTreeNode // Only one of these actions may be active in the current // mode, and only one will be returned. If multiple actions // are active, it is undefined which one will be the one // returned. actions []TreeAction } func NewKeyTreeNode() *KeyTreeNode { n := new(KeyTreeNode) n.children = make(map[Event]*KeyTreeNode) n.actions = []TreeAction{} return n } // A TreeAction stores an action, and a set of mode constraints for // the action to be active. type TreeAction struct { // only one of these can be non-nil action PaneKeyAction any PaneKeyAnyAction mouse PaneMouseAction modes []ModeConstraint } // A KeyTree is a data structure for storing keybindings. It maps // key events to actions, and maintains a set of currently enabled // modes, which affects the action that is returned for a key event. // The tree acts like a Trie for Events to handle sequence events. type KeyTree struct { root *KeyTreeNode modes map[string]bool cursor KeyTreeCursor } // A KeyTreeCursor keeps track of the current location within the // tree, and stores any information from previous events that may // be needed to execute the action (values of wildcard events or // mouse events) type KeyTreeCursor struct { node *KeyTreeNode recordedEvents []Event wildcards []KeyEvent mouseInfo *tcell.EventMouse } // MakeClosure uses the information stored in a key tree cursor to construct // a PaneKeyAction from a TreeAction (which may have a PaneKeyAction, PaneMouseAction, // or AnyAction) func (k *KeyTreeCursor) MakeClosure(a TreeAction) PaneKeyAction { if a.action != nil { return a.action } else if a.any != nil { return func(p Pane) bool { return a.any(p, k.wildcards) } } else if a.mouse != nil { return func(p Pane) bool { return a.mouse(p, k.mouseInfo) } } return nil } // NewKeyTree allocates and returns an empty key tree func NewKeyTree() *KeyTree { root := NewKeyTreeNode() tree := new(KeyTree) tree.root = root tree.modes = make(map[string]bool) tree.cursor = KeyTreeCursor{ node: root, wildcards: []KeyEvent{}, mouseInfo: nil, } return tree } // A ModeConstraint specifies that an action can only be executed // while a certain mode is enabled or disabled. type ModeConstraint struct { mode string disabled bool } // RegisterKeyBinding registers a PaneKeyAction with an Event. func (k *KeyTree) RegisterKeyBinding(e Event, a PaneKeyAction) { k.registerBinding(e, TreeAction{ action: a, any: nil, mouse: nil, modes: nil, }) } // RegisterKeyAnyBinding registers a PaneKeyAnyAction with an Event. // The event should contain an "any" event. func (k *KeyTree) RegisterKeyAnyBinding(e Event, a PaneKeyAnyAction) { k.registerBinding(e, TreeAction{ action: nil, any: a, mouse: nil, modes: nil, }) } // RegisterMouseBinding registers a PaneMouseAction with an Event. // The event should contain a mouse event. func (k *KeyTree) RegisterMouseBinding(e Event, a PaneMouseAction) { k.registerBinding(e, TreeAction{ action: nil, any: nil, mouse: a, modes: nil, }) } func (k *KeyTree) registerBinding(e Event, a TreeAction) { switch ev := e.(type) { case KeyEvent, MouseEvent, RawEvent: newNode, ok := k.root.children[e] if !ok { newNode = NewKeyTreeNode() k.root.children[e] = newNode } // newNode.actions = append(newNode.actions, a) newNode.actions = []TreeAction{a} case KeySequenceEvent: n := k.root for _, key := range ev.keys { newNode, ok := n.children[key] if !ok { newNode = NewKeyTreeNode() n.children[key] = newNode } n = newNode } // n.actions = append(n.actions, a) n.actions = []TreeAction{a} } } // NextEvent returns the action for the current sequence where e is the next // event. Even if the action was registered as a PaneKeyAnyAction or PaneMouseAction, // it will be returned as a PaneKeyAction closure where the appropriate arguments // have been provided. // If no action is associated with the given Event, or mode constraints are not // met for that action, nil is returned. // A boolean is returned to indicate if there is a conflict with this action. A // conflict occurs when there is an active action for this event but there are // bindings associated with further sequences starting with this event. The // calling function can decide what to do about the conflict (e.g. use a // timeout). func (k *KeyTree) NextEvent(e Event, mouse *tcell.EventMouse) (PaneKeyAction, bool) { n := k.cursor.node c, ok := n.children[e] if !ok { return nil, false } more := len(c.children) > 0 k.cursor.node = c k.cursor.recordedEvents = append(k.cursor.recordedEvents, e) switch ev := e.(type) { case KeyEvent: if ev.any { k.cursor.wildcards = append(k.cursor.wildcards, ev) } case MouseEvent: k.cursor.mouseInfo = mouse } if len(c.actions) > 0 { // check if actions are active for _, a := range c.actions { active := true for _, mc := range a.modes { // if any mode constraint is not met, the action is not active hasMode := k.modes[mc.mode] if hasMode != mc.disabled { active = false } } if active { // the first active action to be found is returned return k.cursor.MakeClosure(a), more } } } return nil, more } // ResetEvents sets the current sequence back to the initial value. func (k *KeyTree) ResetEvents() { k.cursor.node = k.root k.cursor.wildcards = []KeyEvent{} k.cursor.recordedEvents = []Event{} k.cursor.mouseInfo = nil } // RecordedEventsStr returns the list of recorded events as a string func (k *KeyTree) RecordedEventsStr() string { buf := &bytes.Buffer{} for _, e := range k.cursor.recordedEvents { buf.WriteString(e.Name()) } return buf.String() } // DeleteBinding removes any currently active actions associated with the // given event. func (k *KeyTree) DeleteBinding(e Event) { } // DeleteAllBindings removes all actions associated with the given event, // regardless of whether they are active or not. func (k *KeyTree) DeleteAllBindings(e Event) { } // SetMode enables or disabled a given mode func (k *KeyTree) SetMode(mode string, en bool) { k.modes[mode] = en } // HasMode returns if the given mode is currently active func (k *KeyTree) HasMode(mode string) bool { return k.modes[mode] } micro-2.0.14/internal/action/pane.go0000664000175000017510000000041514663411671016614 0ustar nileshnileshpackage action import ( "github.com/zyedidia/micro/v2/internal/display" ) // A Pane is a general interface for a window in the editor. type Pane interface { Handler display.Window ID() uint64 SetID(i uint64) Name() string Close() SetTab(t *Tab) Tab() *Tab } micro-2.0.14/internal/action/rawpane.go0000664000175000017510000000167714663411671017341 0ustar nileshnileshpackage action import ( "fmt" "reflect" "github.com/zyedidia/micro/v2/internal/buffer" "github.com/zyedidia/micro/v2/internal/display" "github.com/zyedidia/tcell/v2" ) type RawPane struct { *BufPane } func NewRawPaneFromWin(b *buffer.Buffer, win display.BWindow, tab *Tab) *RawPane { rh := new(RawPane) rh.BufPane = NewBufPane(b, win, tab) return rh } func NewRawPane(tab *Tab) *RawPane { b := buffer.NewBufferFromString("", "", buffer.BTRaw) w := display.NewBufWindow(0, 0, 0, 0, b) return NewRawPaneFromWin(b, w, tab) } func (h *RawPane) HandleEvent(event tcell.Event) { switch e := event.(type) { case *tcell.EventKey: if e.Key() == tcell.KeyCtrlQ { h.Quit() } } h.Buf.Insert(h.Cursor.Loc, reflect.TypeOf(event).String()[7:]) e, err := ConstructEvent(event) if err == nil { h.Buf.Insert(h.Cursor.Loc, fmt.Sprintf(": %s", e.Name())) } h.Buf.Insert(h.Cursor.Loc, fmt.Sprintf(": %q\n", event.EscSeq())) h.Relocate() } micro-2.0.14/internal/action/tab.go0000664000175000017510000002137314663411671016445 0ustar nileshnileshpackage action import ( luar "layeh.com/gopher-luar" "github.com/zyedidia/micro/v2/internal/buffer" "github.com/zyedidia/micro/v2/internal/config" "github.com/zyedidia/micro/v2/internal/display" ulua "github.com/zyedidia/micro/v2/internal/lua" "github.com/zyedidia/micro/v2/internal/screen" "github.com/zyedidia/micro/v2/internal/views" "github.com/zyedidia/tcell/v2" ) // The TabList is a list of tabs and a window to display the tab bar // at the top of the screen type TabList struct { *display.TabWindow List []*Tab } // NewTabList creates a TabList from a list of buffers by creating a Tab // for each buffer func NewTabList(bufs []*buffer.Buffer) *TabList { w, h := screen.Screen.Size() iOffset := config.GetInfoBarOffset() tl := new(TabList) tl.List = make([]*Tab, len(bufs)) if len(bufs) > 1 { for i, b := range bufs { tl.List[i] = NewTabFromBuffer(0, 1, w, h-1-iOffset, b) } } else { tl.List[0] = NewTabFromBuffer(0, 0, w, h-iOffset, bufs[0]) } tl.TabWindow = display.NewTabWindow(w, 0) tl.Names = make([]string, len(bufs)) return tl } // UpdateNames makes sure that the list of names the tab window has access to is // correct func (t *TabList) UpdateNames() { t.Names = t.Names[:0] for _, p := range t.List { t.Names = append(t.Names, p.Panes[p.active].Name()) } } // AddTab adds a new tab to this TabList func (t *TabList) AddTab(p *Tab) { t.List = append(t.List, p) t.Resize() t.UpdateNames() } // RemoveTab removes a tab with the given id from the TabList func (t *TabList) RemoveTab(id uint64) { for i, p := range t.List { if len(p.Panes) == 0 { continue } if p.Panes[0].ID() == id { copy(t.List[i:], t.List[i+1:]) t.List[len(t.List)-1] = nil t.List = t.List[:len(t.List)-1] if t.Active() >= len(t.List) { t.SetActive(len(t.List) - 1) } t.Resize() t.UpdateNames() return } } } // Resize resizes all elements within the tab list // One thing to note is that when there is only 1 tab // the tab bar should not be drawn so resizing must take // that into account func (t *TabList) Resize() { w, h := screen.Screen.Size() iOffset := config.GetInfoBarOffset() InfoBar.Resize(w, h-1) if len(t.List) > 1 { for _, p := range t.List { p.Y = 1 p.Node.Resize(w, h-1-iOffset) p.Resize() } } else if len(t.List) == 1 { t.List[0].Y = 0 t.List[0].Node.Resize(w, h-iOffset) t.List[0].Resize() } t.TabWindow.Resize(w, h) } // HandleEvent checks for a resize event or a mouse event on the tab bar // otherwise it will forward the event to the currently active tab func (t *TabList) HandleEvent(event tcell.Event) { switch e := event.(type) { case *tcell.EventResize: t.Resize() case *tcell.EventMouse: mx, my := e.Position() switch e.Buttons() { case tcell.Button1: if my == t.Y && len(t.List) > 1 { if mx == 0 { t.Scroll(-4) } else if mx == t.Width-1 { t.Scroll(4) } else { ind := t.LocFromVisual(buffer.Loc{mx, my}) if ind != -1 { t.SetActive(ind) } } return } case tcell.ButtonNone: if t.List[t.Active()].release { // Mouse release received, while already released t.ResetMouse() return } case tcell.WheelUp: if my == t.Y && len(t.List) > 1 { t.Scroll(4) return } case tcell.WheelDown: if my == t.Y && len(t.List) > 1 { t.Scroll(-4) return } } } t.List[t.Active()].HandleEvent(event) } // Display updates the names and then displays the tab bar func (t *TabList) Display() { t.UpdateNames() if len(t.List) > 1 { t.TabWindow.Display() } } func (t *TabList) SetActive(a int) { t.TabWindow.SetActive(a) for i, p := range t.List { if i == a { if !p.isActive { p.isActive = true err := config.RunPluginFn("onSetActive", luar.New(ulua.L, p.CurPane())) if err != nil { screen.TermMessage(err) } } } else { p.isActive = false } } } // ResetMouse resets the mouse release state after the screen was stopped // or the pane changed. // This prevents situations in which mouse releases are received at the wrong place // and the mouse state is still pressed. func (t *TabList) ResetMouse() { for _, tab := range t.List { if !tab.release && tab.resizing != nil { tab.resizing = nil } tab.release = true for _, p := range tab.Panes { if bp, ok := p.(*BufPane); ok { bp.resetMouse() } } } } // CloseTerms notifies term panes that a terminal job has finished. func (t *TabList) CloseTerms() { for _, tab := range t.List { for _, p := range tab.Panes { if tp, ok := p.(*TermPane); ok { tp.HandleTermClose() } } } } // Tabs is the global tab list var Tabs *TabList func InitTabs(bufs []*buffer.Buffer) { multiopen := config.GetGlobalOption("multiopen").(string) if multiopen == "tab" { Tabs = NewTabList(bufs) } else { Tabs = NewTabList(bufs[:1]) for _, b := range bufs[1:] { if multiopen == "vsplit" { MainTab().CurPane().VSplitBuf(b) } else { // default hsplit MainTab().CurPane().HSplitBuf(b) } } } screen.RestartCallback = Tabs.ResetMouse } func MainTab() *Tab { return Tabs.List[Tabs.Active()] } // A Tab represents a single tab // It consists of a list of edit panes (the open buffers), // a split tree (stored as just the root node), and a uiwindow // to display the UI elements like the borders between splits type Tab struct { *views.Node *display.UIWindow isActive bool Panes []Pane active int resizing *views.Node // node currently being resized // captures whether the mouse is released release bool } // NewTabFromBuffer creates a new tab from the given buffer func NewTabFromBuffer(x, y, width, height int, b *buffer.Buffer) *Tab { t := new(Tab) t.Node = views.NewRoot(x, y, width, height) t.UIWindow = display.NewUIWindow(t.Node) t.release = true e := NewBufPaneFromBuf(b, t) e.SetID(t.ID()) t.Panes = append(t.Panes, e) return t } func NewTabFromPane(x, y, width, height int, pane Pane) *Tab { t := new(Tab) t.Node = views.NewRoot(x, y, width, height) t.UIWindow = display.NewUIWindow(t.Node) t.release = true pane.SetTab(t) pane.SetID(t.ID()) t.Panes = append(t.Panes, pane) return t } // HandleEvent takes a tcell event and usually dispatches it to the current // active pane. However if the event is a resize or a mouse event where the user // is interacting with the UI (resizing splits) then the event is consumed here // If the event is a mouse press event in a pane, that pane will become active // and get the event func (t *Tab) HandleEvent(event tcell.Event) { switch e := event.(type) { case *tcell.EventMouse: mx, my := e.Position() btn := e.Buttons() switch { case btn & ^(tcell.WheelUp|tcell.WheelDown|tcell.WheelLeft|tcell.WheelRight) != tcell.ButtonNone: // button press or drag wasReleased := t.release t.release = false if btn == tcell.Button1 { if t.resizing != nil { var size int if t.resizing.Kind == views.STVert { size = mx - t.resizing.X } else { size = my - t.resizing.Y + 1 } t.resizing.ResizeSplit(size) t.Resize() return } if wasReleased { t.resizing = t.GetMouseSplitNode(buffer.Loc{mx, my}) if t.resizing != nil { return } } } if wasReleased { for i, p := range t.Panes { v := p.GetView() inpane := mx >= v.X && mx < v.X+v.Width && my >= v.Y && my < v.Y+v.Height if inpane { t.SetActive(i) break } } } case btn == tcell.ButtonNone: // button release t.release = true if t.resizing != nil { t.resizing = nil return } default: // wheel move for _, p := range t.Panes { v := p.GetView() inpane := mx >= v.X && mx < v.X+v.Width && my >= v.Y && my < v.Y+v.Height if inpane { p.HandleEvent(event) return } } } } t.Panes[t.active].HandleEvent(event) } // SetActive changes the currently active pane to the specified index func (t *Tab) SetActive(i int) { t.active = i for j, p := range t.Panes { if j == i { p.SetActive(true) } else { p.SetActive(false) } } } // GetPane returns the pane with the given split index func (t *Tab) GetPane(splitid uint64) int { for i, p := range t.Panes { if p.ID() == splitid { return i } } return 0 } // Remove pane removes the pane with the given index func (t *Tab) RemovePane(i int) { copy(t.Panes[i:], t.Panes[i+1:]) t.Panes[len(t.Panes)-1] = nil t.Panes = t.Panes[:len(t.Panes)-1] } // Resize resizes all panes according to their corresponding split nodes func (t *Tab) Resize() { for _, p := range t.Panes { n := t.GetNode(p.ID()) pv := p.GetView() offset := 0 if n.X != 0 { offset = 1 } pv.X, pv.Y = n.X+offset, n.Y p.SetView(pv) p.Resize(n.W-offset, n.H) } } // CurPane returns the currently active pane func (t *Tab) CurPane() *BufPane { p, ok := t.Panes[t.active].(*BufPane) if !ok { return nil } return p } micro-2.0.14/internal/action/terminal_supported.go0000664000175000017510000000242514663411671021614 0ustar nileshnilesh// +build linux darwin dragonfly openbsd_amd64 freebsd package action import ( shellquote "github.com/kballard/go-shellquote" "github.com/zyedidia/micro/v2/internal/shell" ) // TermEmuSupported is a constant that marks if the terminal emulator is supported const TermEmuSupported = true // RunTermEmulator starts a terminal emulator from a bufpane with the given input (command) // if wait is true it will wait for the user to exit by pressing enter once the executable has terminated // if getOutput is true it will redirect the stdout of the process to a pipe which will be passed to the // callback which is a function that takes a string and a list of optional user arguments func RunTermEmulator(h *BufPane, input string, wait bool, getOutput bool, callback func(out string, userargs []interface{}), userargs []interface{}) error { args, err := shellquote.Split(input) if err != nil { return err } if len(args) == 0 { return nil } t := new(shell.Terminal) err = t.Start(args, getOutput, wait, callback, userargs) if err != nil { return err } h.AddTab() id := MainTab().Panes[0].ID() v := h.GetView() tp, err := NewTermPane(v.X, v.Y, v.Width, v.Height, t, id, MainTab()) if err != nil { return err } MainTab().Panes[0] = tp MainTab().SetActive(0) return nil } micro-2.0.14/internal/action/terminal_unsupported.go0000664000175000017510000000074514663411671022162 0ustar nileshnilesh// +build !linux,!darwin,!freebsd,!dragonfly,!openbsd_amd64 package action import "errors" // TermEmuSupported is a constant that marks if the terminal emulator is supported const TermEmuSupported = false // RunTermEmulator returns an error for unsupported systems (non-unix systems func RunTermEmulator(input string, wait bool, getOutput bool, callback func(out string, userargs []interface{}), userargs []interface{}) error { return errors.New("Unsupported operating system") } micro-2.0.14/internal/action/termpane.go0000664000175000017510000001217114663411671017506 0ustar nileshnileshpackage action import ( "errors" "runtime" "github.com/zyedidia/micro/v2/internal/clipboard" "github.com/zyedidia/micro/v2/internal/config" "github.com/zyedidia/micro/v2/internal/display" "github.com/zyedidia/micro/v2/internal/screen" "github.com/zyedidia/micro/v2/internal/shell" "github.com/zyedidia/tcell/v2" "github.com/zyedidia/terminal" ) type TermKeyAction func(*TermPane) var TermBindings *KeyTree func init() { TermBindings = NewKeyTree() } func TermKeyActionGeneral(a TermKeyAction) PaneKeyAction { return func(p Pane) bool { a(p.(*TermPane)) return true } } func TermMapEvent(k Event, action string) { config.Bindings["terminal"][k.Name()] = action switch e := k.(type) { case KeyEvent, KeySequenceEvent, RawEvent: termMapKey(e, action) case MouseEvent: termMapMouse(e, action) } } func termMapKey(k Event, action string) { if f, ok := TermKeyActions[action]; ok { TermBindings.RegisterKeyBinding(k, TermKeyActionGeneral(f)) } } func termMapMouse(k MouseEvent, action string) { // TODO: map mouse termMapKey(k, action) } type TermPane struct { *shell.Terminal display.Window mouseReleased bool id uint64 tab *Tab } func NewTermPane(x, y, w, h int, t *shell.Terminal, id uint64, tab *Tab) (*TermPane, error) { if !TermEmuSupported { return nil, errors.New("Terminal emulator is not supported on this system") } th := new(TermPane) th.Terminal = t th.id = id th.mouseReleased = true th.Window = display.NewTermWindow(x, y, w, h, t) th.tab = tab return th, nil } func (t *TermPane) ID() uint64 { return t.id } func (t *TermPane) SetID(i uint64) { t.id = i } func (t *TermPane) Name() string { return t.Terminal.Name() } func (t *TermPane) SetTab(tab *Tab) { t.tab = tab } func (t *TermPane) Tab() *Tab { return t.tab } func (t *TermPane) Close() {} // Quit closes this termpane func (t *TermPane) Quit() { t.Close() if len(MainTab().Panes) > 1 { t.Unsplit() } else if len(Tabs.List) > 1 { Tabs.RemoveTab(t.id) } else { screen.Screen.Fini() InfoBar.Close() runtime.Goexit() } } // Unsplit removes this split func (t *TermPane) Unsplit() { n := MainTab().GetNode(t.id) n.Unsplit() MainTab().RemovePane(MainTab().GetPane(t.id)) MainTab().Resize() MainTab().SetActive(len(MainTab().Panes) - 1) } // HandleEvent handles a tcell event by forwarding it to the terminal emulator // If the event is a mouse event and the program running in the emulator // does not have mouse support, the emulator will support selections and // copy-paste func (t *TermPane) HandleEvent(event tcell.Event) { if e, ok := event.(*tcell.EventKey); ok { ke := keyEvent(e) action, more := TermBindings.NextEvent(ke, nil) if !more { if action != nil { action(t) TermBindings.ResetEvents() return } TermBindings.ResetEvents() } if more { return } if t.Status == shell.TTDone { switch e.Key() { case tcell.KeyEscape, tcell.KeyCtrlQ, tcell.KeyEnter: t.Close() t.Quit() default: } } if e.Key() == tcell.KeyCtrlC && t.HasSelection() { clipboard.Write(t.GetSelection(t.GetView().Width), clipboard.ClipboardReg) InfoBar.Message("Copied selection to clipboard") } else if t.Status != shell.TTDone { t.WriteString(event.EscSeq()) } } else if _, ok := event.(*tcell.EventPaste); ok { if t.Status != shell.TTDone { t.WriteString(event.EscSeq()) } } else if e, ok := event.(*tcell.EventMouse); !ok || t.State.Mode(terminal.ModeMouseMask) { // t.WriteString(event.EscSeq()) } else { x, y := e.Position() v := t.GetView() x -= v.X y -= v.Y if e.Buttons() == tcell.Button1 { if !t.mouseReleased { // drag t.Selection[1].X = x t.Selection[1].Y = y } else { t.Selection[0].X = x t.Selection[0].Y = y t.Selection[1].X = x t.Selection[1].Y = y } t.mouseReleased = false } else if e.Buttons() == tcell.ButtonNone { if !t.mouseReleased { t.Selection[1].X = x t.Selection[1].Y = y } t.mouseReleased = true } } } // HandleTermClose is called when a terminal has finished its job // and should be closed. If that terminal is this termpane's terminal, // HandleTermClose will close the terminal and the termpane itself. func (t *TermPane) HandleTermClose() { if t.Status == shell.TTClose { t.Quit() } } // Exit closes the termpane func (t *TermPane) Exit() { t.Terminal.Close() t.Quit() } // CommandMode opens the termpane's command mode func (t *TermPane) CommandMode() { InfoBar.Prompt("> ", "", "TerminalCommand", nil, func(resp string, canceled bool) { if !canceled { t.HandleCommand(resp) } }) } // NextSplit moves to the next split func (t *TermPane) NextSplit() { a := t.tab.active if a < len(t.tab.Panes)-1 { a++ } else { a = 0 } t.tab.SetActive(a) } // HandleCommand handles a command for the term pane func (t *TermPane) HandleCommand(input string) { InfoBar.Error("Commands are unsupported in term for now") } // TermKeyActions contains the list of all possible key actions the termpane could execute var TermKeyActions = map[string]TermKeyAction{ "Exit": (*TermPane).Exit, "CommandMode": (*TermPane).CommandMode, "NextSplit": (*TermPane).NextSplit, } micro-2.0.14/internal/buffer/0000775000175000017510000000000014663411671015336 5ustar nileshnileshmicro-2.0.14/internal/buffer/autocomplete.go0000664000175000017510000001155714663411671020377 0ustar nileshnileshpackage buffer import ( "bytes" "io/ioutil" "os" "sort" "strings" "github.com/zyedidia/micro/v2/internal/util" ) // A Completer is a function that takes a buffer and returns info // describing what autocompletions should be inserted at the current // cursor location // It returns a list of string suggestions which will be inserted at // the current cursor location if selected as well as a list of // suggestion names which can be displayed in an autocomplete box or // other UI element type Completer func(*Buffer) ([]string, []string) func (b *Buffer) GetSuggestions() { } // Autocomplete starts the autocomplete process func (b *Buffer) Autocomplete(c Completer) bool { b.Completions, b.Suggestions = c(b) if len(b.Completions) != len(b.Suggestions) || len(b.Completions) == 0 { return false } b.CurSuggestion = -1 b.CycleAutocomplete(true) return true } // CycleAutocomplete moves to the next suggestion func (b *Buffer) CycleAutocomplete(forward bool) { prevSuggestion := b.CurSuggestion if forward { b.CurSuggestion++ } else { b.CurSuggestion-- } if b.CurSuggestion >= len(b.Suggestions) { b.CurSuggestion = 0 } else if b.CurSuggestion < 0 { b.CurSuggestion = len(b.Suggestions) - 1 } c := b.GetActiveCursor() start := c.Loc end := c.Loc if prevSuggestion < len(b.Suggestions) && prevSuggestion >= 0 { start = end.Move(-util.CharacterCountInString(b.Completions[prevSuggestion]), b) } b.Replace(start, end, b.Completions[b.CurSuggestion]) if len(b.Suggestions) > 1 { b.HasSuggestions = true } } // GetWord gets the most recent word separated by any separator // (whitespace, punctuation, any non alphanumeric character) func (b *Buffer) GetWord() ([]byte, int) { c := b.GetActiveCursor() l := b.LineBytes(c.Y) l = util.SliceStart(l, c.X) if c.X == 0 || util.IsWhitespace(b.RuneAt(c.Loc.Move(-1, b))) { return []byte{}, -1 } if util.IsNonWordChar(b.RuneAt(c.Loc.Move(-1, b))) { return []byte{}, c.X } args := bytes.FieldsFunc(l, util.IsNonWordChar) input := args[len(args)-1] return input, c.X - util.CharacterCount(input) } // GetArg gets the most recent word (separated by ' ' only) func (b *Buffer) GetArg() (string, int) { c := b.GetActiveCursor() l := b.LineBytes(c.Y) l = util.SliceStart(l, c.X) args := bytes.Split(l, []byte{' '}) input := string(args[len(args)-1]) argstart := 0 for i, a := range args { if i == len(args)-1 { break } argstart += util.CharacterCount(a) + 1 } return input, argstart } // FileComplete autocompletes filenames func FileComplete(b *Buffer) ([]string, []string) { c := b.GetActiveCursor() input, argstart := b.GetArg() sep := string(os.PathSeparator) dirs := strings.Split(input, sep) var files []os.FileInfo var err error if len(dirs) > 1 { directories := strings.Join(dirs[:len(dirs)-1], sep) + sep directories, _ = util.ReplaceHome(directories) files, err = ioutil.ReadDir(directories) } else { files, err = ioutil.ReadDir(".") } if err != nil { return nil, nil } var suggestions []string for _, f := range files { name := f.Name() if f.IsDir() { name += sep } if strings.HasPrefix(name, dirs[len(dirs)-1]) { suggestions = append(suggestions, name) } } sort.Strings(suggestions) completions := make([]string, len(suggestions)) for i := range suggestions { var complete string if len(dirs) > 1 { complete = strings.Join(dirs[:len(dirs)-1], sep) + sep + suggestions[i] } else { complete = suggestions[i] } completions[i] = util.SliceEndStr(complete, c.X-argstart) } return completions, suggestions } // BufferComplete autocompletes based on previous words in the buffer func BufferComplete(b *Buffer) ([]string, []string) { c := b.GetActiveCursor() input, argstart := b.GetWord() if argstart == -1 { return []string{}, []string{} } inputLen := util.CharacterCount(input) suggestionsSet := make(map[string]struct{}) var suggestions []string for i := c.Y; i >= 0; i-- { l := b.LineBytes(i) words := bytes.FieldsFunc(l, util.IsNonWordChar) for _, w := range words { if bytes.HasPrefix(w, input) && util.CharacterCount(w) > inputLen { strw := string(w) if _, ok := suggestionsSet[strw]; !ok { suggestionsSet[strw] = struct{}{} suggestions = append(suggestions, strw) } } } } for i := c.Y + 1; i < b.LinesNum(); i++ { l := b.LineBytes(i) words := bytes.FieldsFunc(l, util.IsNonWordChar) for _, w := range words { if bytes.HasPrefix(w, input) && util.CharacterCount(w) > inputLen { strw := string(w) if _, ok := suggestionsSet[strw]; !ok { suggestionsSet[strw] = struct{}{} suggestions = append(suggestions, strw) } } } } if len(suggestions) > 1 { suggestions = append(suggestions, string(input)) } completions := make([]string, len(suggestions)) for i := range suggestions { completions[i] = util.SliceEndStr(suggestions[i], c.X-argstart) } return completions, suggestions } micro-2.0.14/internal/buffer/backup.go0000664000175000017510000000713014663411671017133 0ustar nileshnileshpackage buffer import ( "fmt" "io" "os" "path/filepath" "sync/atomic" "time" "github.com/zyedidia/micro/v2/internal/config" "github.com/zyedidia/micro/v2/internal/screen" "github.com/zyedidia/micro/v2/internal/util" "golang.org/x/text/encoding" ) const backupMsg = `A backup was detected for this file. This likely means that micro crashed while editing this file, or another instance of micro is currently editing this file. The backup was created on %s, and the file is %s * 'recover' will apply the backup as unsaved changes to the current buffer. When the buffer is closed, the backup will be removed. * 'ignore' will ignore the backup, discarding its changes. The backup file will be removed. * 'abort' will abort the open operation, and instead open an empty buffer. Options: [r]ecover, [i]gnore, [a]bort: ` var backupRequestChan chan *Buffer func backupThread() { for { time.Sleep(time.Second * 8) for len(backupRequestChan) > 0 { b := <-backupRequestChan bfini := atomic.LoadInt32(&(b.fini)) != 0 if !bfini { b.Backup() } } } } func init() { backupRequestChan = make(chan *Buffer, 10) go backupThread() } func (b *Buffer) RequestBackup() { if !b.requestedBackup { select { case backupRequestChan <- b: default: // channel is full } b.requestedBackup = true } } // Backup saves the current buffer to ConfigDir/backups func (b *Buffer) Backup() error { if !b.Settings["backup"].(bool) || b.Path == "" || b.Type != BTDefault { return nil } backupdir, err := util.ReplaceHome(b.Settings["backupdir"].(string)) if backupdir == "" || err != nil { backupdir = filepath.Join(config.ConfigDir, "backups") } if _, err := os.Stat(backupdir); os.IsNotExist(err) { os.Mkdir(backupdir, os.ModePerm) } name := filepath.Join(backupdir, util.EscapePath(b.AbsPath)) err = overwriteFile(name, encoding.Nop, func(file io.Writer) (e error) { if len(b.lines) == 0 { return } // end of line eol := []byte{'\n'} // write lines if _, e = file.Write(b.lines[0].data); e != nil { return } for _, l := range b.lines[1:] { if _, e = file.Write(eol); e != nil { return } if _, e = file.Write(l.data); e != nil { return } } return }, false) b.requestedBackup = false return err } // RemoveBackup removes any backup file associated with this buffer func (b *Buffer) RemoveBackup() { if !b.Settings["backup"].(bool) || b.Settings["permbackup"].(bool) || b.Path == "" || b.Type != BTDefault { return } f := filepath.Join(config.ConfigDir, "backups", util.EscapePath(b.AbsPath)) os.Remove(f) } // ApplyBackup applies the corresponding backup file to this buffer (if one exists) // Returns true if a backup was applied func (b *Buffer) ApplyBackup(fsize int64) (bool, bool) { if b.Settings["backup"].(bool) && !b.Settings["permbackup"].(bool) && len(b.Path) > 0 && b.Type == BTDefault { backupfile := filepath.Join(config.ConfigDir, "backups", util.EscapePath(b.AbsPath)) if info, err := os.Stat(backupfile); err == nil { backup, err := os.Open(backupfile) if err == nil { defer backup.Close() t := info.ModTime() msg := fmt.Sprintf(backupMsg, t.Format("Mon Jan _2 at 15:04, 2006"), util.EscapePath(b.AbsPath)) choice := screen.TermPrompt(msg, []string{"r", "i", "a", "recover", "ignore", "abort"}, true) if choice%3 == 0 { // recover b.LineArray = NewLineArray(uint64(fsize), FFAuto, backup) b.isModified = true return true, true } else if choice%3 == 1 { // delete os.Remove(backupfile) } else if choice%3 == 2 { return false, false } } } } return false, true } micro-2.0.14/internal/buffer/buffer.go0000664000175000017510000010644414663411671017147 0ustar nileshnileshpackage buffer import ( "bufio" "bytes" "crypto/md5" "errors" "fmt" "io" "io/ioutil" "os" "path" "path/filepath" "strconv" "strings" "sync" "sync/atomic" "time" luar "layeh.com/gopher-luar" dmp "github.com/sergi/go-diff/diffmatchpatch" "github.com/zyedidia/micro/v2/internal/config" ulua "github.com/zyedidia/micro/v2/internal/lua" "github.com/zyedidia/micro/v2/internal/screen" "github.com/zyedidia/micro/v2/internal/util" "github.com/zyedidia/micro/v2/pkg/highlight" "golang.org/x/text/encoding/htmlindex" "golang.org/x/text/encoding/unicode" "golang.org/x/text/transform" ) const backupTime = 8000 var ( // OpenBuffers is a list of the currently open buffers OpenBuffers []*Buffer // LogBuf is a reference to the log buffer which can be opened with the // `> log` command LogBuf *Buffer ) // The BufType defines what kind of buffer this is type BufType struct { Kind int Readonly bool // The buffer cannot be edited Scratch bool // The buffer cannot be saved Syntax bool // Syntax highlighting is enabled } var ( // BTDefault is a default buffer BTDefault = BufType{0, false, false, true} // BTHelp is a help buffer BTHelp = BufType{1, true, true, true} // BTLog is a log buffer BTLog = BufType{2, true, true, false} // BTScratch is a buffer that cannot be saved (for scratch work) BTScratch = BufType{3, false, true, false} // BTRaw is a buffer that shows raw terminal events BTRaw = BufType{4, false, true, false} // BTInfo is a buffer for inputting information BTInfo = BufType{5, false, true, false} // BTStdout is a buffer that only writes to stdout // when closed BTStdout = BufType{6, false, true, true} ) // SharedBuffer is a struct containing info that is shared among buffers // that have the same file open type SharedBuffer struct { *LineArray // Stores the last modification time of the file the buffer is pointing to ModTime time.Time // Type of the buffer (e.g. help, raw, scratch etc..) Type BufType // Path to the file on disk Path string // Absolute path to the file on disk AbsPath string // Name of the buffer on the status line name string toStdout bool // Settings customized by the user Settings map[string]interface{} // LocalSettings customized by the user for this buffer only LocalSettings map[string]bool Suggestions []string Completions []string CurSuggestion int Messages []*Message updateDiffTimer *time.Timer diffBase []byte diffBaseLineCount int diffLock sync.RWMutex diff map[int]DiffStatus requestedBackup bool // ReloadDisabled allows the user to disable reloads if they // are viewing a file that is constantly changing ReloadDisabled bool isModified bool // Whether or not suggestions can be autocompleted must be shared because // it changes based on how the buffer has changed HasSuggestions bool // The Highlighter struct actually performs the highlighting Highlighter *highlight.Highlighter // SyntaxDef represents the syntax highlighting definition being used // This stores the highlighting rules and filetype detection info SyntaxDef *highlight.Def ModifiedThisFrame bool // Hash of the original buffer -- empty if fastdirty is on origHash [md5.Size]byte } func (b *SharedBuffer) insert(pos Loc, value []byte) { b.isModified = true b.HasSuggestions = false b.LineArray.insert(pos, value) inslines := bytes.Count(value, []byte{'\n'}) b.MarkModified(pos.Y, pos.Y+inslines) } func (b *SharedBuffer) remove(start, end Loc) []byte { b.isModified = true b.HasSuggestions = false defer b.MarkModified(start.Y, end.Y) return b.LineArray.remove(start, end) } // MarkModified marks the buffer as modified for this frame // and performs rehighlighting if syntax highlighting is enabled func (b *SharedBuffer) MarkModified(start, end int) { b.ModifiedThisFrame = true start = util.Clamp(start, 0, len(b.lines)-1) end = util.Clamp(end, 0, len(b.lines)-1) if b.Settings["syntax"].(bool) && b.SyntaxDef != nil { l := -1 for i := start; i <= end; i++ { l = util.Max(b.Highlighter.ReHighlightStates(b, i), l) } b.Highlighter.HighlightMatches(b, start, l) } for i := start; i <= end; i++ { b.LineArray.invalidateSearchMatches(i) } } // DisableReload disables future reloads of this sharedbuffer func (b *SharedBuffer) DisableReload() { b.ReloadDisabled = true } const ( DSUnchanged = 0 DSAdded = 1 DSModified = 2 DSDeletedAbove = 3 ) type DiffStatus byte // Buffer stores the main information about a currently open file including // the actual text (in a LineArray), the undo/redo stack (in an EventHandler) // all the cursors, the syntax highlighting info, the settings for the buffer // and some misc info about modification time and path location. // The syntax highlighting info must be stored with the buffer because the syntax // highlighter attaches information to each line of the buffer for optimization // purposes so it doesn't have to rehighlight everything on every update. // Likewise for the search highlighting. type Buffer struct { *EventHandler *SharedBuffer fini int32 cursors []*Cursor curCursor int StartCursor Loc // OptionCallback is called after a buffer option value is changed. // The display module registers its OptionCallback to ensure the buffer window // is properly updated when needed. This is a workaround for the fact that // the buffer module cannot directly call the display's API (it would mean // a circular dependency between packages). OptionCallback func(option string, nativeValue interface{}) // The display module registers its own GetVisualX function for getting // the correct visual x location of a cursor when softwrap is used. // This is hacky. Maybe it would be better to move all the visual x logic // from buffer to display, but it would require rewriting a lot of code. GetVisualX func(loc Loc) int // Last search stores the last successful search LastSearch string LastSearchRegex bool // HighlightSearch enables highlighting all instances of the last successful search HighlightSearch bool } // NewBufferFromFileAtLoc opens a new buffer with a given cursor location // If cursorLoc is {-1, -1} the location does not overwrite what the cursor location // would otherwise be (start of file, or saved cursor position if `savecursor` is // enabled) func NewBufferFromFileAtLoc(path string, btype BufType, cursorLoc Loc) (*Buffer, error) { var err error filename := path if config.GetGlobalOption("parsecursor").(bool) && cursorLoc.X == -1 && cursorLoc.Y == -1 { var cursorPos []string filename, cursorPos = util.GetPathAndCursorPosition(filename) cursorLoc, err = ParseCursorLocation(cursorPos) if err != nil { cursorLoc = Loc{-1, -1} } } filename, err = util.ReplaceHome(filename) if err != nil { return nil, err } f, err := os.OpenFile(filename, os.O_WRONLY, 0) readonly := os.IsPermission(err) f.Close() fileInfo, serr := os.Stat(filename) if serr != nil && !os.IsNotExist(serr) { return nil, serr } if serr == nil && fileInfo.IsDir() { return nil, errors.New("Error: " + filename + " is a directory and cannot be opened") } file, err := os.Open(filename) if err == nil { defer file.Close() } var buf *Buffer if os.IsNotExist(err) { // File does not exist -- create an empty buffer with that name buf = NewBufferFromString("", filename, btype) } else if err != nil { return nil, err } else { buf = NewBuffer(file, util.FSize(file), filename, cursorLoc, btype) if buf == nil { return nil, errors.New("could not open file") } } if readonly && prompt != nil { prompt.Message(fmt.Sprintf("Warning: file is readonly - %s will be attempted when saving", config.GlobalSettings["sucmd"].(string))) // buf.SetOptionNative("readonly", true) } return buf, nil } // NewBufferFromFile opens a new buffer using the given path // It will also automatically handle `~`, and line/column with filename:l:c // It will return an empty buffer if the path does not exist // and an error if the file is a directory func NewBufferFromFile(path string, btype BufType) (*Buffer, error) { return NewBufferFromFileAtLoc(path, btype, Loc{-1, -1}) } // NewBufferFromStringAtLoc creates a new buffer containing the given string with a cursor loc func NewBufferFromStringAtLoc(text, path string, btype BufType, cursorLoc Loc) *Buffer { return NewBuffer(strings.NewReader(text), int64(len(text)), path, cursorLoc, btype) } // NewBufferFromString creates a new buffer containing the given string func NewBufferFromString(text, path string, btype BufType) *Buffer { return NewBuffer(strings.NewReader(text), int64(len(text)), path, Loc{-1, -1}, btype) } // NewBuffer creates a new buffer from a given reader with a given path // Ensure that ReadSettings and InitGlobalSettings have been called before creating // a new buffer // Places the cursor at startcursor. If startcursor is -1, -1 places the // cursor at an autodetected location (based on savecursor or :LINE:COL) func NewBuffer(r io.Reader, size int64, path string, startcursor Loc, btype BufType) *Buffer { absPath, err := filepath.Abs(path) if err != nil { absPath = path } b := new(Buffer) found := false if len(path) > 0 { for _, buf := range OpenBuffers { if buf.AbsPath == absPath && buf.Type != BTInfo { found = true b.SharedBuffer = buf.SharedBuffer b.EventHandler = buf.EventHandler } } } hasBackup := false if !found { b.SharedBuffer = new(SharedBuffer) b.Type = btype b.AbsPath = absPath b.Path = path // this is a little messy since we need to know some settings to read // the file properly, but some settings depend on the filetype, which // we don't know until reading the file. We first read the settings // into a local variable and then use that to determine the encoding, // readonly, and fileformat necessary for reading the file and // assigning the filetype. settings := config.DefaultCommonSettings() b.Settings = config.DefaultCommonSettings() b.LocalSettings = make(map[string]bool) for k, v := range config.GlobalSettings { if _, ok := config.DefaultGlobalOnlySettings[k]; !ok { // make sure setting is not global-only settings[k] = v b.Settings[k] = v } } config.InitLocalSettings(settings, absPath) b.Settings["readonly"] = settings["readonly"] b.Settings["filetype"] = settings["filetype"] b.Settings["syntax"] = settings["syntax"] enc, err := htmlindex.Get(settings["encoding"].(string)) if err != nil { enc = unicode.UTF8 b.Settings["encoding"] = "utf-8" } var ok bool hasBackup, ok = b.ApplyBackup(size) if !ok { return NewBufferFromString("", "", btype) } if !hasBackup { reader := bufio.NewReader(transform.NewReader(r, enc.NewDecoder())) var ff FileFormat = FFAuto if size == 0 { // for empty files, use the fileformat setting instead of // autodetection switch settings["fileformat"] { case "unix": ff = FFUnix case "dos": ff = FFDos } } else { // in case of autodetection treat as locally set b.LocalSettings["fileformat"] = true } b.LineArray = NewLineArray(uint64(size), ff, reader) } b.EventHandler = NewEventHandler(b.SharedBuffer, b.cursors) // The last time this file was modified b.UpdateModTime() } if b.Settings["readonly"].(bool) && b.Type == BTDefault { b.Type.Readonly = true } switch b.Endings { case FFUnix: b.Settings["fileformat"] = "unix" case FFDos: b.Settings["fileformat"] = "dos" } b.UpdateRules() // init local settings again now that we know the filetype config.InitLocalSettings(b.Settings, b.Path) if _, err := os.Stat(filepath.Join(config.ConfigDir, "buffers")); os.IsNotExist(err) { os.Mkdir(filepath.Join(config.ConfigDir, "buffers"), os.ModePerm) } if startcursor.X != -1 && startcursor.Y != -1 { b.StartCursor = startcursor } else if b.Settings["savecursor"].(bool) || b.Settings["saveundo"].(bool) { err := b.Unserialize() if err != nil { screen.TermMessage(err) } } b.AddCursor(NewCursor(b, b.StartCursor)) b.GetActiveCursor().Relocate() if !b.Settings["fastdirty"].(bool) && !found { if size > LargeFileThreshold { // If the file is larger than LargeFileThreshold fastdirty needs to be on b.Settings["fastdirty"] = true } else if !hasBackup { // since applying a backup does not save the applied backup to disk, we should // not calculate the original hash based on the backup data calcHash(b, &b.origHash) } } err = config.RunPluginFn("onBufferOpen", luar.New(ulua.L, b)) if err != nil { screen.TermMessage(err) } OpenBuffers = append(OpenBuffers, b) return b } // CloseOpenBuffers removes all open buffers func CloseOpenBuffers() { for i, buf := range OpenBuffers { buf.Fini() OpenBuffers[i] = nil } OpenBuffers = OpenBuffers[:0] } // Close removes this buffer from the list of open buffers func (b *Buffer) Close() { for i, buf := range OpenBuffers { if b == buf { b.Fini() copy(OpenBuffers[i:], OpenBuffers[i+1:]) OpenBuffers[len(OpenBuffers)-1] = nil OpenBuffers = OpenBuffers[:len(OpenBuffers)-1] return } } } // Fini should be called when a buffer is closed and performs // some cleanup func (b *Buffer) Fini() { if !b.Modified() { b.Serialize() } b.RemoveBackup() if b.Type == BTStdout { fmt.Fprint(util.Stdout, string(b.Bytes())) } atomic.StoreInt32(&(b.fini), int32(1)) } // GetName returns the name that should be displayed in the statusline // for this buffer func (b *Buffer) GetName() string { name := b.name if name == "" { if b.Path == "" { return "No name" } name = b.Path } if b.Settings["basename"].(bool) { return path.Base(name) } return name } // SetName changes the name for this buffer func (b *Buffer) SetName(s string) { b.name = s } // Insert inserts the given string of text at the start location func (b *Buffer) Insert(start Loc, text string) { if !b.Type.Readonly { b.EventHandler.cursors = b.cursors b.EventHandler.active = b.curCursor b.EventHandler.Insert(start, text) b.RequestBackup() } } // Remove removes the characters between the start and end locations func (b *Buffer) Remove(start, end Loc) { if !b.Type.Readonly { b.EventHandler.cursors = b.cursors b.EventHandler.active = b.curCursor b.EventHandler.Remove(start, end) b.RequestBackup() } } // FileType returns the buffer's filetype func (b *Buffer) FileType() string { return b.Settings["filetype"].(string) } // ExternallyModified returns whether the file being edited has // been modified by some external process func (b *Buffer) ExternallyModified() bool { modTime, err := util.GetModTime(b.Path) if err == nil { return modTime != b.ModTime } return false } // UpdateModTime updates the modtime of this file func (b *Buffer) UpdateModTime() (err error) { b.ModTime, err = util.GetModTime(b.Path) return } // ReOpen reloads the current buffer from disk func (b *Buffer) ReOpen() error { file, err := os.Open(b.Path) if err != nil { return err } enc, err := htmlindex.Get(b.Settings["encoding"].(string)) if err != nil { return err } reader := bufio.NewReader(transform.NewReader(file, enc.NewDecoder())) data, err := ioutil.ReadAll(reader) txt := string(data) if err != nil { return err } b.EventHandler.ApplyDiff(txt) err = b.UpdateModTime() if !b.Settings["fastdirty"].(bool) { if len(data) > LargeFileThreshold { b.Settings["fastdirty"] = true } else { calcHash(b, &b.origHash) } } b.isModified = false b.RelocateCursors() return err } // RelocateCursors relocates all cursors (makes sure they are in the buffer) func (b *Buffer) RelocateCursors() { for _, c := range b.cursors { c.Relocate() } } // DeselectCursors removes selection from all cursors func (b *Buffer) DeselectCursors() { for _, c := range b.cursors { c.Deselect(true) } } // RuneAt returns the rune at a given location in the buffer func (b *Buffer) RuneAt(loc Loc) rune { line := b.LineBytes(loc.Y) if len(line) > 0 { i := 0 for len(line) > 0 { r, _, size := util.DecodeCharacter(line) line = line[size:] if i == loc.X { return r } i++ } } return '\n' } // WordAt returns the word around a given location in the buffer func (b *Buffer) WordAt(loc Loc) []byte { if len(b.LineBytes(loc.Y)) == 0 || !util.IsWordChar(b.RuneAt(loc)) { return []byte{} } start := loc end := loc.Move(1, b) for start.X > 0 && util.IsWordChar(b.RuneAt(start.Move(-1, b))) { start.X-- } lineLen := util.CharacterCount(b.LineBytes(loc.Y)) for end.X < lineLen && util.IsWordChar(b.RuneAt(end)) { end.X++ } return b.Substr(start, end) } // Modified returns if this buffer has been modified since // being opened func (b *Buffer) Modified() bool { if b.Type.Scratch { return false } if b.Settings["fastdirty"].(bool) { return b.isModified } var buff [md5.Size]byte calcHash(b, &buff) return buff != b.origHash } // Size returns the number of bytes in the current buffer func (b *Buffer) Size() int { nb := 0 for i := 0; i < b.LinesNum(); i++ { nb += len(b.LineBytes(i)) if i != b.LinesNum()-1 { if b.Endings == FFDos { nb++ // carriage return } nb++ // newline } } return nb } // calcHash calculates md5 hash of all lines in the buffer func calcHash(b *Buffer, out *[md5.Size]byte) { h := md5.New() if len(b.lines) > 0 { h.Write(b.lines[0].data) for _, l := range b.lines[1:] { if b.Endings == FFDos { h.Write([]byte{'\r', '\n'}) } else { h.Write([]byte{'\n'}) } h.Write(l.data) } } h.Sum((*out)[:0]) } func parseDefFromFile(f config.RuntimeFile, header *highlight.Header) *highlight.Def { data, err := f.Data() if err != nil { screen.TermMessage("Error loading syntax file " + f.Name() + ": " + err.Error()) return nil } if header == nil { header, err = highlight.MakeHeaderYaml(data) if err != nil { screen.TermMessage("Error parsing header for syntax file " + f.Name() + ": " + err.Error()) return nil } } file, err := highlight.ParseFile(data) if err != nil { screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error()) return nil } syndef, err := highlight.ParseDef(file, header) if err != nil { screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error()) return nil } return syndef } // findRealRuntimeSyntaxDef finds a specific syntax definition // in the user's custom syntax files func findRealRuntimeSyntaxDef(name string, header *highlight.Header) *highlight.Def { for _, f := range config.ListRealRuntimeFiles(config.RTSyntax) { if f.Name() == name { syndef := parseDefFromFile(f, header) if syndef != nil { return syndef } } } return nil } // findRuntimeSyntaxDef finds a specific syntax definition // in the built-in syntax files func findRuntimeSyntaxDef(name string, header *highlight.Header) *highlight.Def { for _, f := range config.ListRuntimeFiles(config.RTSyntax) { if f.Name() == name { syndef := parseDefFromFile(f, header) if syndef != nil { return syndef } } } return nil } func resolveIncludes(syndef *highlight.Def) { includes := highlight.GetIncludes(syndef) if len(includes) == 0 { return } var files []*highlight.File for _, f := range config.ListRuntimeFiles(config.RTSyntax) { data, err := f.Data() if err != nil { screen.TermMessage("Error loading syntax file " + f.Name() + ": " + err.Error()) continue } header, err := highlight.MakeHeaderYaml(data) if err != nil { screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error()) continue } for _, i := range includes { if header.FileType == i { file, err := highlight.ParseFile(data) if err != nil { screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error()) continue } files = append(files, file) break } } if len(files) >= len(includes) { break } } highlight.ResolveIncludes(syndef, files) } // UpdateRules updates the syntax rules and filetype for this buffer // This is called when the colorscheme changes func (b *Buffer) UpdateRules() { if !b.Type.Syntax { return } ft := b.Settings["filetype"].(string) if ft == "off" { b.ClearMatches() b.SyntaxDef = nil return } b.SyntaxDef = nil // syntaxFileInfo is an internal helper structure // to store properties of one single syntax file type syntaxFileInfo struct { header *highlight.Header fileName string syntaxDef *highlight.Def } fnameMatches := []syntaxFileInfo{} headerMatches := []syntaxFileInfo{} syntaxFile := "" foundDef := false var header *highlight.Header // search for the syntax file in the user's custom syntax files for _, f := range config.ListRealRuntimeFiles(config.RTSyntax) { if f.Name() == "default" { continue } data, err := f.Data() if err != nil { screen.TermMessage("Error loading syntax file " + f.Name() + ": " + err.Error()) continue } header, err = highlight.MakeHeaderYaml(data) if err != nil { screen.TermMessage("Error parsing header for syntax file " + f.Name() + ": " + err.Error()) continue } matchedFileType := false matchedFileName := false matchedFileHeader := false if ft == "unknown" || ft == "" { if header.MatchFileName(b.Path) { matchedFileName = true } if len(fnameMatches) == 0 && header.MatchFileHeader(b.lines[0].data) { matchedFileHeader = true } } else if header.FileType == ft { matchedFileType = true } if matchedFileType || matchedFileName || matchedFileHeader { file, err := highlight.ParseFile(data) if err != nil { screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error()) continue } syndef, err := highlight.ParseDef(file, header) if err != nil { screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error()) continue } if matchedFileType { b.SyntaxDef = syndef syntaxFile = f.Name() foundDef = true break } if matchedFileName { fnameMatches = append(fnameMatches, syntaxFileInfo{header, f.Name(), syndef}) } else if matchedFileHeader { headerMatches = append(headerMatches, syntaxFileInfo{header, f.Name(), syndef}) } } } if !foundDef { // search for the syntax file in the built-in syntax files for _, f := range config.ListRuntimeFiles(config.RTSyntaxHeader) { data, err := f.Data() if err != nil { screen.TermMessage("Error loading syntax header file " + f.Name() + ": " + err.Error()) continue } header, err = highlight.MakeHeader(data) if err != nil { screen.TermMessage("Error reading syntax header file", f.Name(), err) continue } if ft == "unknown" || ft == "" { if header.MatchFileName(b.Path) { fnameMatches = append(fnameMatches, syntaxFileInfo{header, f.Name(), nil}) } if len(fnameMatches) == 0 && header.MatchFileHeader(b.lines[0].data) { headerMatches = append(headerMatches, syntaxFileInfo{header, f.Name(), nil}) } } else if header.FileType == ft { syntaxFile = f.Name() break } } } if syntaxFile == "" { matches := fnameMatches if len(matches) == 0 { matches = headerMatches } length := len(matches) if length > 0 { signatureMatch := false if length > 1 { // multiple matching syntax files found, try to resolve the ambiguity // using signatures detectlimit := util.IntOpt(b.Settings["detectlimit"]) lineCount := len(b.lines) limit := lineCount if detectlimit > 0 && lineCount > detectlimit { limit = detectlimit } matchLoop: for _, m := range matches { if m.header.HasFileSignature() { for i := 0; i < limit; i++ { if m.header.MatchFileSignature(b.lines[i].data) { syntaxFile = m.fileName if m.syntaxDef != nil { b.SyntaxDef = m.syntaxDef foundDef = true } header = m.header signatureMatch = true break matchLoop } } } } } if length == 1 || !signatureMatch { syntaxFile = matches[0].fileName if matches[0].syntaxDef != nil { b.SyntaxDef = matches[0].syntaxDef foundDef = true } header = matches[0].header } } } if syntaxFile != "" && !foundDef { // we found a syntax file using a syntax header file b.SyntaxDef = findRuntimeSyntaxDef(syntaxFile, header) } if b.SyntaxDef != nil { b.Settings["filetype"] = b.SyntaxDef.FileType } else { // search for the default file in the user's custom syntax files b.SyntaxDef = findRealRuntimeSyntaxDef("default", nil) if b.SyntaxDef == nil { // search for the default file in the built-in syntax files b.SyntaxDef = findRuntimeSyntaxDef("default", nil) } } if b.SyntaxDef != nil { resolveIncludes(b.SyntaxDef) } if b.SyntaxDef != nil { b.Highlighter = highlight.NewHighlighter(b.SyntaxDef) if b.Settings["syntax"].(bool) { go func() { b.Highlighter.HighlightStates(b) b.Highlighter.HighlightMatches(b, 0, b.End().Y) screen.Redraw() }() } } } // ClearMatches clears all of the syntax highlighting for the buffer func (b *Buffer) ClearMatches() { for i := range b.lines { b.SetMatch(i, nil) b.SetState(i, nil) } } // IndentString returns this buffer's indent method (a tabstop or n spaces // depending on the settings) func (b *Buffer) IndentString(tabsize int) string { if b.Settings["tabstospaces"].(bool) { return util.Spaces(tabsize) } return "\t" } // SetCursors resets this buffer's cursors to a new list func (b *Buffer) SetCursors(c []*Cursor) { b.cursors = c b.EventHandler.cursors = b.cursors b.EventHandler.active = b.curCursor } // AddCursor adds a new cursor to the list func (b *Buffer) AddCursor(c *Cursor) { b.cursors = append(b.cursors, c) b.EventHandler.cursors = b.cursors b.EventHandler.active = b.curCursor b.UpdateCursors() } // SetCurCursor sets the current cursor func (b *Buffer) SetCurCursor(n int) { b.curCursor = n } // GetActiveCursor returns the main cursor in this buffer func (b *Buffer) GetActiveCursor() *Cursor { return b.cursors[b.curCursor] } // GetCursor returns the nth cursor func (b *Buffer) GetCursor(n int) *Cursor { return b.cursors[n] } // GetCursors returns the list of cursors in this buffer func (b *Buffer) GetCursors() []*Cursor { return b.cursors } // NumCursors returns the number of cursors func (b *Buffer) NumCursors() int { return len(b.cursors) } // MergeCursors merges any cursors that are at the same position // into one cursor func (b *Buffer) MergeCursors() { var cursors []*Cursor for i := 0; i < len(b.cursors); i++ { c1 := b.cursors[i] if c1 != nil { for j := 0; j < len(b.cursors); j++ { c2 := b.cursors[j] if c2 != nil && i != j && c1.Loc == c2.Loc { b.cursors[j] = nil } } cursors = append(cursors, c1) } } b.cursors = cursors for i := range b.cursors { b.cursors[i].Num = i } if b.curCursor >= len(b.cursors) { b.curCursor = len(b.cursors) - 1 } b.EventHandler.cursors = b.cursors b.EventHandler.active = b.curCursor } // UpdateCursors updates all the cursors indices func (b *Buffer) UpdateCursors() { b.EventHandler.cursors = b.cursors b.EventHandler.active = b.curCursor for i, c := range b.cursors { c.Num = i } } func (b *Buffer) RemoveCursor(i int) { copy(b.cursors[i:], b.cursors[i+1:]) b.cursors[len(b.cursors)-1] = nil b.cursors = b.cursors[:len(b.cursors)-1] b.curCursor = util.Clamp(b.curCursor, 0, len(b.cursors)-1) b.UpdateCursors() } // ClearCursors removes all extra cursors func (b *Buffer) ClearCursors() { for i := 1; i < len(b.cursors); i++ { b.cursors[i] = nil } b.cursors = b.cursors[:1] b.UpdateCursors() b.curCursor = 0 b.GetActiveCursor().Deselect(true) } // MoveLinesUp moves the range of lines up one row func (b *Buffer) MoveLinesUp(start int, end int) { if start < 1 || start >= end || end > len(b.lines) { return } l := string(b.LineBytes(start - 1)) if end == len(b.lines) { b.insert( Loc{ util.CharacterCount(b.lines[end-1].data), end - 1, }, []byte{'\n'}, ) } b.Insert( Loc{0, end}, l+"\n", ) b.Remove( Loc{0, start - 1}, Loc{0, start}, ) } // MoveLinesDown moves the range of lines down one row func (b *Buffer) MoveLinesDown(start int, end int) { if start < 0 || start >= end || end >= len(b.lines) { return } l := string(b.LineBytes(end)) b.Insert( Loc{0, start}, l+"\n", ) end++ b.Remove( Loc{0, end}, Loc{0, end + 1}, ) } var BracePairs = [][2]rune{ {'(', ')'}, {'{', '}'}, {'[', ']'}, } func (b *Buffer) findMatchingBrace(braceType [2]rune, start Loc, char rune) (Loc, bool) { var i int if char == braceType[0] { for y := start.Y; y < b.LinesNum(); y++ { l := []rune(string(b.LineBytes(y))) xInit := 0 if y == start.Y { xInit = start.X } for x := xInit; x < len(l); x++ { r := l[x] if r == braceType[0] { i++ } else if r == braceType[1] { i-- if i == 0 { return Loc{x, y}, true } } } } } else if char == braceType[1] { for y := start.Y; y >= 0; y-- { l := []rune(string(b.lines[y].data)) xInit := len(l) - 1 if y == start.Y { xInit = start.X } for x := xInit; x >= 0; x-- { r := l[x] if r == braceType[1] { i++ } else if r == braceType[0] { i-- if i == 0 { return Loc{x, y}, true } } } } } return start, false } // If there is a brace character (for example '{' or ']') at the given start location, // FindMatchingBrace returns the location of the matching brace for it (for example '}' // or '['). The second returned value is true if there was no matching brace found // for given starting location but it was found for the location one character left // of it. The third returned value is true if the matching brace was found at all. func (b *Buffer) FindMatchingBrace(start Loc) (Loc, bool, bool) { // TODO: maybe can be more efficient with utf8 package curLine := []rune(string(b.LineBytes(start.Y))) // first try to find matching brace for the given location (it has higher priority) if start.X >= 0 && start.X < len(curLine) { startChar := curLine[start.X] for _, bp := range BracePairs { if startChar == bp[0] || startChar == bp[1] { mb, found := b.findMatchingBrace(bp, start, startChar) if found { return mb, false, true } } } } if b.Settings["matchbraceleft"].(bool) { // failed to find matching brace for the given location, so try to find matching // brace for the location one character left of it if start.X-1 >= 0 && start.X-1 < len(curLine) { leftChar := curLine[start.X-1] left := Loc{start.X - 1, start.Y} for _, bp := range BracePairs { if leftChar == bp[0] || leftChar == bp[1] { mb, found := b.findMatchingBrace(bp, left, leftChar) if found { return mb, true, true } } } } } return start, false, false } // Retab changes all tabs to spaces or vice versa func (b *Buffer) Retab() { toSpaces := b.Settings["tabstospaces"].(bool) tabsize := util.IntOpt(b.Settings["tabsize"]) dirty := false for i := 0; i < b.LinesNum(); i++ { l := b.LineBytes(i) ws := util.GetLeadingWhitespace(l) if len(ws) != 0 { if toSpaces { ws = bytes.ReplaceAll(ws, []byte{'\t'}, bytes.Repeat([]byte{' '}, tabsize)) } else { ws = bytes.ReplaceAll(ws, bytes.Repeat([]byte{' '}, tabsize), []byte{'\t'}) } } l = bytes.TrimLeft(l, " \t") b.Lock() b.lines[i].data = append(ws, l...) b.Unlock() b.MarkModified(i, i) dirty = true } b.isModified = dirty } // ParseCursorLocation turns a cursor location like 10:5 (LINE:COL) // into a loc func ParseCursorLocation(cursorPositions []string) (Loc, error) { startpos := Loc{0, 0} var err error // if no positions are available exit early if cursorPositions == nil { return startpos, errors.New("No cursor positions were provided.") } startpos.Y, err = strconv.Atoi(cursorPositions[0]) startpos.Y-- if err == nil { if len(cursorPositions) > 1 { startpos.X, err = strconv.Atoi(cursorPositions[1]) if startpos.X > 0 { startpos.X-- } } } return startpos, err } // Line returns the string representation of the given line number func (b *Buffer) Line(i int) string { return string(b.LineBytes(i)) } func (b *Buffer) Write(bytes []byte) (n int, err error) { b.EventHandler.InsertBytes(b.End(), bytes) return len(bytes), nil } func (b *Buffer) updateDiff(synchronous bool) { b.diffLock.Lock() defer b.diffLock.Unlock() b.diff = make(map[int]DiffStatus) if b.diffBase == nil { return } differ := dmp.New() if !synchronous { b.Lock() } bytes := b.Bytes() if !synchronous { b.Unlock() } baseRunes, bufferRunes, _ := differ.DiffLinesToRunes(string(b.diffBase), string(bytes)) diffs := differ.DiffMainRunes(baseRunes, bufferRunes, false) lineN := 0 for _, diff := range diffs { lineCount := len([]rune(diff.Text)) switch diff.Type { case dmp.DiffEqual: lineN += lineCount case dmp.DiffInsert: var status DiffStatus if b.diff[lineN] == DSDeletedAbove { status = DSModified } else { status = DSAdded } for i := 0; i < lineCount; i++ { b.diff[lineN] = status lineN++ } case dmp.DiffDelete: b.diff[lineN] = DSDeletedAbove } } } // UpdateDiff computes the diff between the diff base and the buffer content. // The update may be performed synchronously or asynchronously. // If an asynchronous update is already pending when UpdateDiff is called, // UpdateDiff does not schedule another update. func (b *Buffer) UpdateDiff() { if b.updateDiffTimer != nil { return } lineCount := b.LinesNum() if b.diffBaseLineCount > lineCount { lineCount = b.diffBaseLineCount } if lineCount < 1000 { b.updateDiff(true) } else if lineCount < 30000 { b.updateDiffTimer = time.AfterFunc(500*time.Millisecond, func() { b.updateDiffTimer = nil b.updateDiff(false) screen.Redraw() }) } else { // Don't compute diffs for very large files b.diffLock.Lock() b.diff = make(map[int]DiffStatus) b.diffLock.Unlock() } } // SetDiffBase sets the text that is used as the base for diffing the buffer content func (b *Buffer) SetDiffBase(diffBase []byte) { b.diffBase = diffBase if diffBase == nil { b.diffBaseLineCount = 0 } else { b.diffBaseLineCount = strings.Count(string(diffBase), "\n") } b.UpdateDiff() } // DiffStatus returns the diff status for a line in the buffer func (b *Buffer) DiffStatus(lineN int) DiffStatus { b.diffLock.RLock() defer b.diffLock.RUnlock() // Note that the zero value for DiffStatus is equal to DSUnchanged return b.diff[lineN] } // FindNextDiffLine returns the line number of the next block of diffs. // If `startLine` is already in a block of diffs, lines in that block are skipped. func (b *Buffer) FindNextDiffLine(startLine int, forward bool) (int, error) { if b.diff == nil { return 0, errors.New("no diff data") } startStatus, ok := b.diff[startLine] if !ok { startStatus = DSUnchanged } curLine := startLine for { curStatus, ok := b.diff[curLine] if !ok { curStatus = DSUnchanged } if curLine < 0 || curLine > b.LinesNum() { return 0, errors.New("no next diff hunk") } if curStatus != startStatus { if startStatus != DSUnchanged && curStatus == DSUnchanged { // Skip over the block of unchanged text startStatus = DSUnchanged } else { return curLine, nil } } if forward { curLine++ } else { curLine-- } } } // SearchMatch returns true if the given location is within a match of the last search. // It is used for search highlighting func (b *Buffer) SearchMatch(pos Loc) bool { return b.LineArray.SearchMatch(b, pos) } // WriteLog writes a string to the log buffer func WriteLog(s string) { LogBuf.EventHandler.Insert(LogBuf.End(), s) } // GetLogBuf returns the log buffer func GetLogBuf() *Buffer { return LogBuf } micro-2.0.14/internal/buffer/buffer_generated_test.go0000664000175000017510000005437714663411671022233 0ustar nileshnilesh// This file is generated from VSCode model tests by the testgen tool. // DO NOT EDIT THIS FILE BY HAND; your changes will be overwritten! package buffer import "testing" func TestAuto1(t *testing.T) { check( t, []string{ "ioe", "", "yjct", "", "", }, []operation{ { start: Loc{1, 0}, end: Loc{1, 0}, text: []string{ "b", "r", "fq", }, }, { start: Loc{3, 0}, end: Loc{0, 1}, text: []string{ "", "", }, }, }, []string{ "ib", "r", "fqoe", "", "yjct", "", "", }, ) } func TestAuto2(t *testing.T) { check( t, []string{ "f", "littnhskrq", "utxvsizqnk", "lslqz", "jxn", "gmm", }, []operation{ { start: Loc{1, 0}, end: Loc{1, 0}, text: []string{ "", "o", }, }, { start: Loc{3, 1}, end: Loc{3, 1}, text: []string{ "zaq", "avb", }, }, { start: Loc{4, 1}, end: Loc{1, 5}, text: []string{ "jlr", "zl", "j", }, }, }, []string{ "f", "o", "litzaq", "avbtjlr", "zl", "jmm", }, ) } func TestAuto3(t *testing.T) { check( t, []string{ "ofw", "qsxmziuvzw", "rp", "qsnymek", "elth", "wmgzbwudxz", "iwsdkndh", "bujlbwb", "asuouxfv", "xuccnb", }, []operation{ { start: Loc{2, 3}, end: Loc{2, 3}, text: []string{ "", }, }, }, []string{ "ofw", "qsxmziuvzw", "rp", "qsnymek", "elth", "wmgzbwudxz", "iwsdkndh", "bujlbwb", "asuouxfv", "xuccnb", }, ) } func TestAuto4(t *testing.T) { check( t, []string{ "fefymj", "qum", "vmiwxxaiqq", "dz", "lnqdgorosf", }, []operation{ { start: Loc{2, 0}, end: Loc{4, 0}, text: []string{ "hp", }, }, { start: Loc{6, 0}, end: Loc{0, 1}, text: []string{ "kcg", "", "mpx", }, }, { start: Loc{1, 1}, end: Loc{1, 1}, text: []string{ "", "aw", "", }, }, { start: Loc{1, 1}, end: Loc{1, 1}, text: []string{ "vqr", "mo", }, }, { start: Loc{1, 3}, end: Loc{2, 4}, text: []string{ "xyc", }, }, }, []string{ "fehpmjkcg", "", "mpxq", "aw", "vqr", "moum", "vmiwxxaiqq", "dxycqdgorosf", }, ) } func TestBug19872UndoIsFunky(t *testing.T) { check( t, []string{ "something", " A", "", " B", "something else", }, []operation{ { start: Loc{0, 1}, end: Loc{1, 1}, text: []string{ "", }, }, { start: Loc{0, 2}, end: Loc{1, 3}, text: []string{ "", }, }, }, []string{ "something", "A", "B", "something else", }, ) } func TestBug19872UndoIsFunky_2(t *testing.T) { check( t, []string{ "something", "A", "B", "something else", }, []operation{ { start: Loc{0, 1}, end: Loc{0, 1}, text: []string{ " ", }, }, { start: Loc{0, 2}, end: Loc{0, 2}, text: []string{ "", " ", }, }, }, []string{ "something", " A", "", " B", "something else", }, ) } func TestInsertEmptyText(t *testing.T) { check( t, []string{ "My First Line", "\t\tMy Second Line", " Third Line", "", "1", }, []operation{ { start: Loc{0, 0}, end: Loc{0, 0}, text: []string{ "", }, }, }, []string{ "My First Line", "\t\tMy Second Line", " Third Line", "", "1", }, ) } func TestLastOpIsNoOp(t *testing.T) { check( t, []string{ "My First Line", "\t\tMy Second Line", " Third Line", "", "1", }, []operation{ { start: Loc{0, 0}, end: Loc{1, 0}, text: []string{ "", }, }, { start: Loc{0, 3}, end: Loc{0, 3}, text: []string{ "", }, }, }, []string{ "y First Line", "\t\tMy Second Line", " Third Line", "", "1", }, ) } func TestInsertTextWithoutNewline1(t *testing.T) { check( t, []string{ "My First Line", "\t\tMy Second Line", " Third Line", "", "1", }, []operation{ { start: Loc{0, 0}, end: Loc{0, 0}, text: []string{ "foo ", }, }, }, []string{ "foo My First Line", "\t\tMy Second Line", " Third Line", "", "1", }, ) } func TestInsertTextWithoutNewline2(t *testing.T) { check( t, []string{ "My First Line", "\t\tMy Second Line", " Third Line", "", "1", }, []operation{ { start: Loc{2, 0}, end: Loc{2, 0}, text: []string{ " foo", }, }, }, []string{ "My foo First Line", "\t\tMy Second Line", " Third Line", "", "1", }, ) } func TestInsertOneNewline(t *testing.T) { check( t, []string{ "My First Line", "\t\tMy Second Line", " Third Line", "", "1", }, []operation{ { start: Loc{3, 0}, end: Loc{3, 0}, text: []string{ "", "", }, }, }, []string{ "My ", "First Line", "\t\tMy Second Line", " Third Line", "", "1", }, ) } func TestInsertTextWithOneNewline(t *testing.T) { check( t, []string{ "My First Line", "\t\tMy Second Line", " Third Line", "", "1", }, []operation{ { start: Loc{2, 0}, end: Loc{2, 0}, text: []string{ " new line", "No longer", }, }, }, []string{ "My new line", "No longer First Line", "\t\tMy Second Line", " Third Line", "", "1", }, ) } func TestInsertTextWithTwoNewlines(t *testing.T) { check( t, []string{ "My First Line", "\t\tMy Second Line", " Third Line", "", "1", }, []operation{ { start: Loc{2, 0}, end: Loc{2, 0}, text: []string{ " new line", "One more line in the middle", "No longer", }, }, }, []string{ "My new line", "One more line in the middle", "No longer First Line", "\t\tMy Second Line", " Third Line", "", "1", }, ) } func TestInsertTextWithManyNewlines(t *testing.T) { check( t, []string{ "My First Line", "\t\tMy Second Line", " Third Line", "", "1", }, []operation{ { start: Loc{2, 0}, end: Loc{2, 0}, text: []string{ "", "", "", "", "", }, }, }, []string{ "My", "", "", "", " First Line", "\t\tMy Second Line", " Third Line", "", "1", }, ) } func TestInsertMultipleNewlines(t *testing.T) { check( t, []string{ "My First Line", "\t\tMy Second Line", " Third Line", "", "1", }, []operation{ { start: Loc{2, 0}, end: Loc{2, 0}, text: []string{ "", "", "", "", "", }, }, { start: Loc{14, 2}, end: Loc{14, 2}, text: []string{ "a", "b", }, }, }, []string{ "My", "", "", "", " First Line", "\t\tMy Second Line", " Third Linea", "b", "", "1", }, ) } func TestDeleteEmptyText(t *testing.T) { check( t, []string{ "My First Line", "\t\tMy Second Line", " Third Line", "", "1", }, []operation{ { start: Loc{0, 0}, end: Loc{0, 0}, text: []string{ "", }, }, }, []string{ "My First Line", "\t\tMy Second Line", " Third Line", "", "1", }, ) } func TestDeleteTextFromOneLine(t *testing.T) { check( t, []string{ "My First Line", "\t\tMy Second Line", " Third Line", "", "1", }, []operation{ { start: Loc{0, 0}, end: Loc{1, 0}, text: []string{ "", }, }, }, []string{ "y First Line", "\t\tMy Second Line", " Third Line", "", "1", }, ) } func TestDeleteTextFromOneLine2(t *testing.T) { check( t, []string{ "My First Line", "\t\tMy Second Line", " Third Line", "", "1", }, []operation{ { start: Loc{0, 0}, end: Loc{2, 0}, text: []string{ "a", }, }, }, []string{ "a First Line", "\t\tMy Second Line", " Third Line", "", "1", }, ) } func TestDeleteAllTextFromALine(t *testing.T) { check( t, []string{ "My First Line", "\t\tMy Second Line", " Third Line", "", "1", }, []operation{ { start: Loc{0, 0}, end: Loc{13, 0}, text: []string{ "", }, }, }, []string{ "", "\t\tMy Second Line", " Third Line", "", "1", }, ) } func TestDeleteTextFromTwoLines(t *testing.T) { check( t, []string{ "My First Line", "\t\tMy Second Line", " Third Line", "", "1", }, []operation{ { start: Loc{3, 0}, end: Loc{5, 1}, text: []string{ "", }, }, }, []string{ "My Second Line", " Third Line", "", "1", }, ) } func TestDeleteTextFromManyLines(t *testing.T) { check( t, []string{ "My First Line", "\t\tMy Second Line", " Third Line", "", "1", }, []operation{ { start: Loc{3, 0}, end: Loc{4, 2}, text: []string{ "", }, }, }, []string{ "My Third Line", "", "1", }, ) } func TestDeleteEverything(t *testing.T) { check( t, []string{ "My First Line", "\t\tMy Second Line", " Third Line", "", "1", }, []operation{ { start: Loc{0, 0}, end: Loc{1, 4}, text: []string{ "", }, }, }, []string{ "", }, ) } func TestTwoUnrelatedEdits(t *testing.T) { check( t, []string{ "My First Line", "\t\tMy Second Line", " Third Line", "", "123", }, []operation{ { start: Loc{0, 1}, end: Loc{2, 1}, text: []string{ "\t", }, }, { start: Loc{0, 2}, end: Loc{4, 2}, text: []string{ "", }, }, }, []string{ "My First Line", "\tMy Second Line", "Third Line", "", "123", }, ) } func TestTwoEditsOnOneLine(t *testing.T) { check( t, []string{ "\t\tfirst\t ", "\t\tsecond line", "\tthird line", "fourth line", "\t\t\t\t", }, []operation{ { start: Loc{2, 4}, end: Loc{6, 4}, text: []string{ "", }, }, { start: Loc{11, 4}, end: Loc{15, 4}, text: []string{ "", }, }, }, []string{ "\t\tfirst\t ", "\t\tsecond line", "\tthird line", "fourth line", "\t\tfifth\t\t", }, ) } func TestManyEdits(t *testing.T) { check( t, []string{ "{\"x\" : 1}", }, []operation{ { start: Loc{1, 0}, end: Loc{1, 0}, text: []string{ "\n ", }, }, { start: Loc{4, 0}, end: Loc{5, 0}, text: []string{ "", }, }, { start: Loc{8, 0}, end: Loc{8, 0}, text: []string{ "\n", }, }, }, []string{ "{", " \"x\": 1", "}", }, ) } func TestManyEditsReversed(t *testing.T) { check( t, []string{ "{", " \"x\": 1", "}", }, []operation{ { start: Loc{1, 0}, end: Loc{2, 1}, text: []string{ "", }, }, { start: Loc{5, 1}, end: Loc{5, 1}, text: []string{ " ", }, }, { start: Loc{8, 1}, end: Loc{0, 2}, text: []string{ "", }, }, }, []string{ "{\"x\" : 1}", }, ) } func TestReplacingNewlines1(t *testing.T) { check( t, []string{ "{", "\"a\": true,", "", "\"b\": true", "}", }, []operation{ { start: Loc{1, 0}, end: Loc{0, 1}, text: []string{ "", "\t", }, }, { start: Loc{10, 1}, end: Loc{0, 3}, text: []string{ "", "\t", }, }, }, []string{ "{", "\t\"a\": true,", "\t\"b\": true", "}", }, ) } func TestReplacingNewlines2(t *testing.T) { check( t, []string{ "some text", "some more text", "now comes an empty line", "", "after empty line", "and the last line", }, []operation{ { start: Loc{4, 0}, end: Loc{0, 2}, text: []string{ " text", "some more text", "some more text", }, }, { start: Loc{1, 2}, end: Loc{0, 3}, text: []string{ "o more lines", "asd", "asd", "asd", }, }, { start: Loc{0, 4}, end: Loc{5, 4}, text: []string{ "zzzzzzzz", }, }, { start: Loc{10, 4}, end: Loc{15, 5}, text: []string{ "1", "2", "3", "4", }, }, }, []string{ "some text", "some more text", "some more textno more lines", "asd", "asd", "asd", "zzzzzzzz empt1", "2", "3", "4ne", }, ) } func TestAdvanced1(t *testing.T) { check( t, []string{ " { \"d\": [", " null", " ] /*comment*/", " ,\"e\": /*comment*/ [null] }", }, []operation{ { start: Loc{0, 0}, end: Loc{1, 0}, text: []string{ "", }, }, { start: Loc{2, 0}, end: Loc{9, 0}, text: []string{ "", " ", }, }, { start: Loc{15, 0}, end: Loc{13, 1}, text: []string{ "", " ", }, }, { start: Loc{17, 1}, end: Loc{8, 2}, text: []string{ "", " ", }, }, { start: Loc{21, 2}, end: Loc{8, 3}, text: []string{ "", }, }, { start: Loc{9, 3}, end: Loc{9, 3}, text: []string{ "", " ", }, }, { start: Loc{27, 3}, end: Loc{27, 3}, text: []string{ "", " ", }, }, { start: Loc{31, 3}, end: Loc{31, 3}, text: []string{ "", " ", }, }, { start: Loc{32, 3}, end: Loc{33, 3}, text: []string{ "", "", }, }, }, []string{ "{", " \"d\": [", " null", " ] /*comment*/,", " \"e\": /*comment*/ [", " null", " ]", "}", }, ) } func TestAdvancedSimplified(t *testing.T) { check( t, []string{ " abc", " ,def", }, []operation{ { start: Loc{0, 0}, end: Loc{3, 0}, text: []string{ "", }, }, { start: Loc{6, 0}, end: Loc{1, 1}, text: []string{ "", }, }, { start: Loc{2, 1}, end: Loc{2, 1}, text: []string{ "", "", }, }, }, []string{ "abc,", "def", }, ) } func TestIssue144(t *testing.T) { check( t, []string{ "package caddy", "", "func main() {", "\tfmt.Println(\"Hello World! :)\")", "}", "", }, []operation{ { start: Loc{0, 0}, end: Loc{0, 5}, text: []string{ "package caddy", "", "import \"fmt\"", "", "func main() {", "\tfmt.Println(\"Hello World! :)\")", "}", "", }, }, }, []string{ "package caddy", "", "import \"fmt\"", "", "func main() {", "\tfmt.Println(\"Hello World! :)\")", "}", "", }, ) } func TestIssue2586ReplacingSelectedEndOfLineWithNewlineLocksUpTheDocument(t *testing.T) { check( t, []string{ "something", "interesting", }, []operation{ { start: Loc{9, 0}, end: Loc{0, 1}, text: []string{ "", "", }, }, }, []string{ "something", "interesting", }, ) } func TestIssue3980(t *testing.T) { check( t, []string{ "class A {", " someProperty = false;", " someMethod() {", " this.someMethod();", " }", "}", }, []operation{ { start: Loc{7, 0}, end: Loc{8, 0}, text: []string{ "", "", }, }, { start: Loc{16, 2}, end: Loc{17, 2}, text: []string{ "", "", }, }, { start: Loc{17, 2}, end: Loc{17, 2}, text: []string{ " ", }, }, { start: Loc{4, 3}, end: Loc{4, 3}, text: []string{ " ", }, }, }, []string{ "class A", "{", " someProperty = false;", " someMethod()", " {", " this.someMethod();", " }", "}", }, ) } func TestTouchingEditsTwoInsertsAtTheSamePosition(t *testing.T) { check( t, []string{ "hello world", }, []operation{ { start: Loc{0, 0}, end: Loc{0, 0}, text: []string{ "a", }, }, { start: Loc{0, 0}, end: Loc{0, 0}, text: []string{ "b", }, }, }, []string{ "abhello world", }, ) } func TestTouchingEditsInsertAndReplaceTouching(t *testing.T) { check( t, []string{ "hello world", }, []operation{ { start: Loc{0, 0}, end: Loc{0, 0}, text: []string{ "b", }, }, { start: Loc{0, 0}, end: Loc{2, 0}, text: []string{ "ab", }, }, }, []string{ "babllo world", }, ) } func TestTouchingEditsTwoTouchingReplaces(t *testing.T) { check( t, []string{ "hello world", }, []operation{ { start: Loc{0, 0}, end: Loc{1, 0}, text: []string{ "H", }, }, { start: Loc{1, 0}, end: Loc{2, 0}, text: []string{ "E", }, }, }, []string{ "HEllo world", }, ) } func TestTouchingEditsTwoTouchingDeletes(t *testing.T) { check( t, []string{ "hello world", }, []operation{ { start: Loc{0, 0}, end: Loc{1, 0}, text: []string{ "", }, }, { start: Loc{1, 0}, end: Loc{2, 0}, text: []string{ "", }, }, }, []string{ "llo world", }, ) } func TestTouchingEditsInsertAndReplace(t *testing.T) { check( t, []string{ "hello world", }, []operation{ { start: Loc{0, 0}, end: Loc{0, 0}, text: []string{ "H", }, }, { start: Loc{0, 0}, end: Loc{2, 0}, text: []string{ "e", }, }, }, []string{ "Hello world", }, ) } func TestTouchingEditsReplaceAndInsert(t *testing.T) { check( t, []string{ "hello world", }, []operation{ { start: Loc{0, 0}, end: Loc{2, 0}, text: []string{ "H", }, }, { start: Loc{2, 0}, end: Loc{2, 0}, text: []string{ "e", }, }, }, []string{ "Hello world", }, ) } func TestSingleDelete1(t *testing.T) { check( t, []string{ "hello world", }, []operation{ { start: Loc{0, 0}, end: Loc{1, 0}, text: []string{ "", }, }, }, []string{ "ello world", }, ) } func TestSingleDelete2(t *testing.T) { check( t, []string{ "helloworld", }, []operation{ { start: Loc{2, 0}, end: Loc{7, 0}, text: []string{ "", }, }, }, []string{ "herld", }, ) } func TestSingleDelete3(t *testing.T) { check( t, []string{ "hello world", }, []operation{ { start: Loc{0, 0}, end: Loc{5, 0}, text: []string{ "", }, }, }, []string{ " world", }, ) } func TestSingleDelete4(t *testing.T) { check( t, []string{ "hello world", }, []operation{ { start: Loc{1, 0}, end: Loc{6, 0}, text: []string{ "", }, }, }, []string{ "hworld", }, ) } func TestSingleDelete5(t *testing.T) { check( t, []string{ "hello world", }, []operation{ { start: Loc{0, 0}, end: Loc{11, 0}, text: []string{ "", }, }, }, []string{ "", }, ) } func TestMultiDelete6(t *testing.T) { check( t, []string{ "hello world", "hello world", "hello world", }, []operation{ { start: Loc{5, 0}, end: Loc{5, 2}, text: []string{ "", }, }, }, []string{ "hello world", }, ) } func TestMultiDelete7(t *testing.T) { check( t, []string{ "hello world", "hello world", "hello world", }, []operation{ { start: Loc{11, 0}, end: Loc{11, 2}, text: []string{ "", }, }, }, []string{ "hello world", }, ) } func TestMultiDelete8(t *testing.T) { check( t, []string{ "hello world", "hello world", "hello world", }, []operation{ { start: Loc{0, 0}, end: Loc{0, 2}, text: []string{ "", }, }, }, []string{ "hello world", }, ) } func TestMultiDelete9(t *testing.T) { check( t, []string{ "hello world", "hello world", "hello world", }, []operation{ { start: Loc{11, 0}, end: Loc{0, 2}, text: []string{ "", }, }, }, []string{ "hello worldhello world", }, ) } func TestSingleInsert1(t *testing.T) { check( t, []string{ "hello world", }, []operation{ { start: Loc{0, 0}, end: Loc{0, 0}, text: []string{ "xx", }, }, }, []string{ "xxhello world", }, ) } func TestSingleInsert2(t *testing.T) { check( t, []string{ "hello world", }, []operation{ { start: Loc{1, 0}, end: Loc{1, 0}, text: []string{ "xx", }, }, }, []string{ "hxxello world", }, ) } func TestSingleInsert3(t *testing.T) { check( t, []string{ "hello world", }, []operation{ { start: Loc{5, 0}, end: Loc{5, 0}, text: []string{ "xx", }, }, }, []string{ "helloxx world", }, ) } func TestSingleInsert4(t *testing.T) { check( t, []string{ "hello world", }, []operation{ { start: Loc{6, 0}, end: Loc{6, 0}, text: []string{ "xx", }, }, }, []string{ "hello xxworld", }, ) } func TestSingleInsert5(t *testing.T) { check( t, []string{ "hello world", }, []operation{ { start: Loc{11, 0}, end: Loc{11, 0}, text: []string{ "xx", }, }, }, []string{ "hello worldxx", }, ) } func TestMultiInsert6(t *testing.T) { check( t, []string{ "hello world", }, []operation{ { start: Loc{0, 0}, end: Loc{0, 0}, text: []string{ "\n", }, }, }, []string{ "", "hello world", }, ) } func TestMultiInsert7(t *testing.T) { check( t, []string{ "hello world", }, []operation{ { start: Loc{11, 0}, end: Loc{11, 0}, text: []string{ "\n", }, }, }, []string{ "hello world", "", }, ) } func TestMultiInsert8(t *testing.T) { check( t, []string{ "hello world", }, []operation{ { start: Loc{6, 0}, end: Loc{6, 0}, text: []string{ "\n", }, }, }, []string{ "hello ", "world", }, ) } func TestMultiInsert9(t *testing.T) { check( t, []string{ "hello world", "hello world", }, []operation{ { start: Loc{6, 0}, end: Loc{6, 0}, text: []string{ "xx\nyy", }, }, }, []string{ "hello xx", "yyworld", "hello world", }, ) } micro-2.0.14/internal/buffer/buffer_test.go0000664000175000017510000001513214663411671020177 0ustar nileshnileshpackage buffer import ( "math/rand" "strings" "testing" "github.com/stretchr/testify/assert" lua "github.com/yuin/gopher-lua" "github.com/zyedidia/micro/v2/internal/config" ulua "github.com/zyedidia/micro/v2/internal/lua" "github.com/zyedidia/micro/v2/internal/util" ) type operation struct { start Loc end Loc text []string } func init() { ulua.L = lua.NewState() config.InitRuntimeFiles(false) config.InitGlobalSettings() config.GlobalSettings["backup"] = false config.GlobalSettings["fastdirty"] = true } func check(t *testing.T, before []string, operations []operation, after []string) { assert := assert.New(t) b := NewBufferFromString(strings.Join(before, "\n"), "", BTDefault) assert.NotEqual("", b.GetName()) assert.Equal(false, b.ExternallyModified()) assert.Equal(false, b.Modified()) assert.Equal(1, b.NumCursors()) checkText := func(lines []string) { assert.Equal([]byte(strings.Join(lines, "\n")), b.Bytes()) assert.Equal(len(lines), b.LinesNum()) for i, s := range lines { assert.Equal(s, b.Line(i)) assert.Equal([]byte(s), b.LineBytes(i)) } } checkText(before) var cursors []*Cursor for _, op := range operations { cursor := NewCursor(b, op.start) cursor.SetSelectionStart(op.start) cursor.SetSelectionEnd(op.end) b.AddCursor(cursor) cursors = append(cursors, cursor) } assert.Equal(1+len(operations), b.NumCursors()) for i, op := range operations { cursor := cursors[i] b.SetCurCursor(cursor.Num) cursor.DeleteSelection() b.Insert(cursor.Loc, strings.Join(op.text, "\n")) } checkText(after) // must have exactly two events per operation (delete and insert) for range operations { b.UndoOneEvent() b.UndoOneEvent() } checkText(before) for i, op := range operations { cursor := cursors[i] if op.start == op.end { assert.Equal(op.start, cursor.Loc) } else { assert.Equal(op.start, cursor.CurSelection[0]) assert.Equal(op.end, cursor.CurSelection[1]) } } for range operations { b.RedoOneEvent() b.RedoOneEvent() } checkText(after) b.Close() } const maxLineLength = 200 var alphabet = []rune(" abcdeäم📚") func randomString(length int) string { runes := make([]rune, length) for i := range runes { runes[i] = alphabet[rand.Intn(len(alphabet))] } return string(runes) } func randomText(nLines int) string { lines := make([]string, nLines) for i := range lines { lines[i] = randomString(rand.Intn(maxLineLength + 1)) } return strings.Join(lines, "\n") } func benchCreateAndClose(testingB *testing.B, nLines int) { rand.Seed(int64(nLines)) text := randomText(nLines) testingB.ResetTimer() for i := 0; i < testingB.N; i++ { b := NewBufferFromString(text, "", BTDefault) b.Close() } } func benchRead(testingB *testing.B, nLines int) { rand.Seed(int64(nLines)) b := NewBufferFromString(randomText(nLines), "", BTDefault) testingB.ResetTimer() for i := 0; i < testingB.N; i++ { b.Bytes() for j := 0; j < b.LinesNum(); j++ { b.Line(j) b.LineBytes(j) } } testingB.StopTimer() b.Close() } func benchEdit(testingB *testing.B, nLines, nCursors int) { rand.Seed(int64(nLines + nCursors)) b := NewBufferFromString(randomText(nLines), "", BTDefault) regionSize := nLines / nCursors operations := make([]operation, nCursors) for i := range operations { startLine := (i * regionSize) + rand.Intn(regionSize-5) startColumn := rand.Intn(util.CharacterCountInString(b.Line(startLine)) + 1) endLine := startLine + 1 + rand.Intn(5) endColumn := rand.Intn(util.CharacterCountInString(b.Line(endLine)) + 1) operations[i] = operation{ start: Loc{startColumn, startLine}, end: Loc{endColumn, endLine}, text: []string{randomText(2 + rand.Intn(4))}, } } testingB.ResetTimer() for i := 0; i < testingB.N; i++ { b.SetCursors([]*Cursor{}) var cursors []*Cursor for _, op := range operations { cursor := NewCursor(b, op.start) cursor.SetSelectionStart(op.start) cursor.SetSelectionEnd(op.end) b.AddCursor(cursor) cursors = append(cursors, cursor) } for j, op := range operations { cursor := cursors[j] b.SetCurCursor(cursor.Num) cursor.DeleteSelection() b.Insert(cursor.Loc, op.text[0]) } for b.UndoStack.Peek() != nil { b.UndoOneEvent() } } testingB.StopTimer() b.Close() } func BenchmarkCreateAndClose10Lines(b *testing.B) { benchCreateAndClose(b, 10) } func BenchmarkCreateAndClose100Lines(b *testing.B) { benchCreateAndClose(b, 100) } func BenchmarkCreateAndClose1000Lines(b *testing.B) { benchCreateAndClose(b, 1000) } func BenchmarkCreateAndClose10000Lines(b *testing.B) { benchCreateAndClose(b, 10000) } func BenchmarkCreateAndClose100000Lines(b *testing.B) { benchCreateAndClose(b, 100000) } func BenchmarkCreateAndClose1000000Lines(b *testing.B) { benchCreateAndClose(b, 1000000) } func BenchmarkRead10Lines(b *testing.B) { benchRead(b, 10) } func BenchmarkRead100Lines(b *testing.B) { benchRead(b, 100) } func BenchmarkRead1000Lines(b *testing.B) { benchRead(b, 1000) } func BenchmarkRead10000Lines(b *testing.B) { benchRead(b, 10000) } func BenchmarkRead100000Lines(b *testing.B) { benchRead(b, 100000) } func BenchmarkRead1000000Lines(b *testing.B) { benchRead(b, 1000000) } func BenchmarkEdit10Lines1Cursor(b *testing.B) { benchEdit(b, 10, 1) } func BenchmarkEdit100Lines1Cursor(b *testing.B) { benchEdit(b, 100, 1) } func BenchmarkEdit100Lines10Cursors(b *testing.B) { benchEdit(b, 100, 10) } func BenchmarkEdit1000Lines1Cursor(b *testing.B) { benchEdit(b, 1000, 1) } func BenchmarkEdit1000Lines10Cursors(b *testing.B) { benchEdit(b, 1000, 10) } func BenchmarkEdit1000Lines100Cursors(b *testing.B) { benchEdit(b, 1000, 100) } func BenchmarkEdit10000Lines1Cursor(b *testing.B) { benchEdit(b, 10000, 1) } func BenchmarkEdit10000Lines10Cursors(b *testing.B) { benchEdit(b, 10000, 10) } func BenchmarkEdit10000Lines100Cursors(b *testing.B) { benchEdit(b, 10000, 100) } func BenchmarkEdit10000Lines1000Cursors(b *testing.B) { benchEdit(b, 10000, 1000) } func BenchmarkEdit100000Lines1Cursor(b *testing.B) { benchEdit(b, 100000, 1) } func BenchmarkEdit100000Lines10Cursors(b *testing.B) { benchEdit(b, 100000, 10) } func BenchmarkEdit100000Lines100Cursors(b *testing.B) { benchEdit(b, 100000, 100) } func BenchmarkEdit100000Lines1000Cursors(b *testing.B) { benchEdit(b, 100000, 1000) } func BenchmarkEdit1000000Lines1Cursor(b *testing.B) { benchEdit(b, 1000000, 1) } func BenchmarkEdit1000000Lines10Cursors(b *testing.B) { benchEdit(b, 1000000, 10) } func BenchmarkEdit1000000Lines100Cursors(b *testing.B) { benchEdit(b, 1000000, 100) } func BenchmarkEdit1000000Lines1000Cursors(b *testing.B) { benchEdit(b, 1000000, 1000) } micro-2.0.14/internal/buffer/cursor.go0000664000175000017510000003356214663411671017213 0ustar nileshnileshpackage buffer import ( "github.com/zyedidia/micro/v2/internal/clipboard" "github.com/zyedidia/micro/v2/internal/util" ) // InBounds returns whether the given location is a valid character position in the given buffer func InBounds(pos Loc, buf *Buffer) bool { if pos.Y < 0 || pos.Y >= len(buf.lines) || pos.X < 0 || pos.X > util.CharacterCount(buf.LineBytes(pos.Y)) { return false } return true } // The Cursor struct stores the location of the cursor in the buffer // as well as the selection type Cursor struct { buf *Buffer Loc // Last cursor x position LastVisualX int // The current selection as a range of character numbers (inclusive) CurSelection [2]Loc // The original selection as a range of character numbers // This is used for line and word selection where it is necessary // to know what the original selection was OrigSelection [2]Loc // The line number where a new trailing whitespace has been added // or -1 if there is no new trailing whitespace at this cursor. // This is used for checking if a trailing whitespace should be highlighted NewTrailingWsY int // Which cursor index is this (for multiple cursors) Num int } func NewCursor(b *Buffer, l Loc) *Cursor { c := &Cursor{ buf: b, Loc: l, NewTrailingWsY: -1, } c.StoreVisualX() return c } func (c *Cursor) SetBuf(b *Buffer) { c.buf = b } func (c *Cursor) Buf() *Buffer { return c.buf } // Goto puts the cursor at the given cursor's location and gives // the current cursor its selection too func (c *Cursor) Goto(b Cursor) { c.X, c.Y, c.LastVisualX = b.X, b.Y, b.LastVisualX c.OrigSelection, c.CurSelection = b.OrigSelection, b.CurSelection } // GotoLoc puts the cursor at the given cursor's location and gives // the current cursor its selection too func (c *Cursor) GotoLoc(l Loc) { c.X, c.Y = l.X, l.Y c.StoreVisualX() } // GetVisualX returns the x value of the cursor in visual spaces func (c *Cursor) GetVisualX() int { if c.buf.GetVisualX != nil { return c.buf.GetVisualX(c.Loc) } if c.X <= 0 { c.X = 0 return 0 } bytes := c.buf.LineBytes(c.Y) tabsize := int(c.buf.Settings["tabsize"].(float64)) return util.StringWidth(bytes, c.X, tabsize) } // GetCharPosInLine gets the char position of a visual x y // coordinate (this is necessary because tabs are 1 char but // 4 visual spaces) func (c *Cursor) GetCharPosInLine(b []byte, visualPos int) int { tabsize := int(c.buf.Settings["tabsize"].(float64)) return util.GetCharPosInLine(b, visualPos, tabsize) } // Start moves the cursor to the start of the line it is on func (c *Cursor) Start() { c.X = 0 c.LastVisualX = c.GetVisualX() } // StartOfText moves the cursor to the first non-whitespace rune of // the line it is on func (c *Cursor) StartOfText() { c.Start() for util.IsWhitespace(c.RuneUnder(c.X)) { if c.X == util.CharacterCount(c.buf.LineBytes(c.Y)) { break } c.Right() } } // IsStartOfText returns whether the cursor is at the first // non-whitespace rune of the line it is on func (c *Cursor) IsStartOfText() bool { x := 0 for util.IsWhitespace(c.RuneUnder(x)) { if x == util.CharacterCount(c.buf.LineBytes(c.Y)) { break } x++ } return c.X == x } // End moves the cursor to the end of the line it is on func (c *Cursor) End() { c.X = util.CharacterCount(c.buf.LineBytes(c.Y)) c.LastVisualX = c.GetVisualX() } // CopySelection copies the user's selection to either "primary" // or "clipboard" func (c *Cursor) CopySelection(target clipboard.Register) { if c.HasSelection() { if target != clipboard.PrimaryReg || c.buf.Settings["useprimary"].(bool) { clipboard.WriteMulti(string(c.GetSelection()), target, c.Num, c.buf.NumCursors()) } } } // ResetSelection resets the user's selection func (c *Cursor) ResetSelection() { c.CurSelection[0] = c.buf.Start() c.CurSelection[1] = c.buf.Start() } // SetSelectionStart sets the start of the selection func (c *Cursor) SetSelectionStart(pos Loc) { c.CurSelection[0] = pos } // SetSelectionEnd sets the end of the selection func (c *Cursor) SetSelectionEnd(pos Loc) { c.CurSelection[1] = pos } // HasSelection returns whether or not the user has selected anything func (c *Cursor) HasSelection() bool { return c.CurSelection[0] != c.CurSelection[1] } // DeleteSelection deletes the currently selected text func (c *Cursor) DeleteSelection() { if c.CurSelection[0].GreaterThan(c.CurSelection[1]) { c.buf.Remove(c.CurSelection[1], c.CurSelection[0]) c.Loc = c.CurSelection[1] } else if !c.HasSelection() { return } else { c.buf.Remove(c.CurSelection[0], c.CurSelection[1]) c.Loc = c.CurSelection[0] } } // Deselect closes the cursor's current selection // Start indicates whether the cursor should be placed // at the start or end of the selection func (c *Cursor) Deselect(start bool) { if c.HasSelection() { if start { c.Loc = c.CurSelection[0] } else { c.Loc = c.CurSelection[1].Move(-1, c.buf) } c.ResetSelection() c.StoreVisualX() } } // GetSelection returns the cursor's selection func (c *Cursor) GetSelection() []byte { if InBounds(c.CurSelection[0], c.buf) && InBounds(c.CurSelection[1], c.buf) { if c.CurSelection[0].GreaterThan(c.CurSelection[1]) { return c.buf.Substr(c.CurSelection[1], c.CurSelection[0]) } return c.buf.Substr(c.CurSelection[0], c.CurSelection[1]) } return []byte{} } // SelectLine selects the current line func (c *Cursor) SelectLine() { c.Start() c.SetSelectionStart(c.Loc) c.End() if len(c.buf.lines)-1 > c.Y { c.SetSelectionEnd(c.Loc.Move(1, c.buf)) } else { c.SetSelectionEnd(c.Loc) } c.OrigSelection = c.CurSelection } // AddLineToSelection adds the current line to the selection func (c *Cursor) AddLineToSelection() { if c.Loc.LessThan(c.OrigSelection[0]) { c.Start() c.SetSelectionStart(c.Loc) c.SetSelectionEnd(c.OrigSelection[1]) } if c.Loc.GreaterThan(c.OrigSelection[1]) { c.End() c.SetSelectionEnd(c.Loc.Move(1, c.buf)) c.SetSelectionStart(c.OrigSelection[0]) } if c.Loc.LessThan(c.OrigSelection[1]) && c.Loc.GreaterThan(c.OrigSelection[0]) { c.CurSelection = c.OrigSelection } } // UpN moves the cursor up N lines (if possible) func (c *Cursor) UpN(amount int) { proposedY := c.Y - amount if proposedY < 0 { proposedY = 0 } else if proposedY >= len(c.buf.lines) { proposedY = len(c.buf.lines) - 1 } bytes := c.buf.LineBytes(proposedY) c.X = c.GetCharPosInLine(bytes, c.LastVisualX) if c.X > util.CharacterCount(bytes) || (amount < 0 && proposedY == c.Y) { c.X = util.CharacterCount(bytes) c.StoreVisualX() } if c.X < 0 || (amount > 0 && proposedY == c.Y) { c.X = 0 c.StoreVisualX() } c.Y = proposedY } // DownN moves the cursor down N lines (if possible) func (c *Cursor) DownN(amount int) { c.UpN(-amount) } // Up moves the cursor up one line (if possible) func (c *Cursor) Up() { c.UpN(1) } // Down moves the cursor down one line (if possible) func (c *Cursor) Down() { c.DownN(1) } // Left moves the cursor left one cell (if possible) or to // the previous line if it is at the beginning func (c *Cursor) Left() { if c.Loc == c.buf.Start() { return } if c.X > 0 { c.X-- } else { c.Up() c.End() } c.StoreVisualX() } // Right moves the cursor right one cell (if possible) or // to the next line if it is at the end func (c *Cursor) Right() { if c.Loc == c.buf.End() { return } if c.X < util.CharacterCount(c.buf.LineBytes(c.Y)) { c.X++ } else { c.Down() c.Start() } c.StoreVisualX() } // Relocate makes sure that the cursor is inside the bounds // of the buffer If it isn't, it moves it to be within the // buffer's lines func (c *Cursor) Relocate() { if c.Y < 0 { c.Y = 0 } else if c.Y >= len(c.buf.lines) { c.Y = len(c.buf.lines) - 1 } if c.X < 0 { c.X = 0 } else if c.X > util.CharacterCount(c.buf.LineBytes(c.Y)) { c.X = util.CharacterCount(c.buf.LineBytes(c.Y)) } } // SelectWord selects the word the cursor is currently on func (c *Cursor) SelectWord() { if len(c.buf.LineBytes(c.Y)) == 0 { return } if !util.IsWordChar(c.RuneUnder(c.X)) { c.SetSelectionStart(c.Loc) c.SetSelectionEnd(c.Loc.Move(1, c.buf)) c.OrigSelection = c.CurSelection return } forward, backward := c.X, c.X for backward > 0 && util.IsWordChar(c.RuneUnder(backward-1)) { backward-- } c.SetSelectionStart(Loc{backward, c.Y}) c.OrigSelection[0] = c.CurSelection[0] lineLen := util.CharacterCount(c.buf.LineBytes(c.Y)) - 1 for forward < lineLen && util.IsWordChar(c.RuneUnder(forward+1)) { forward++ } c.SetSelectionEnd(Loc{forward, c.Y}.Move(1, c.buf)) c.OrigSelection[1] = c.CurSelection[1] c.Loc = c.CurSelection[1] } // AddWordToSelection adds the word the cursor is currently on // to the selection func (c *Cursor) AddWordToSelection() { if c.Loc.GreaterThan(c.OrigSelection[0]) && c.Loc.LessThan(c.OrigSelection[1]) { c.CurSelection = c.OrigSelection return } if c.Loc.LessThan(c.OrigSelection[0]) { backward := c.X for backward > 0 && util.IsWordChar(c.RuneUnder(backward-1)) { backward-- } c.SetSelectionStart(Loc{backward, c.Y}) c.SetSelectionEnd(c.OrigSelection[1]) } if c.Loc.GreaterThan(c.OrigSelection[1]) { forward := c.X lineLen := util.CharacterCount(c.buf.LineBytes(c.Y)) - 1 for forward < lineLen && util.IsWordChar(c.RuneUnder(forward+1)) { forward++ } c.SetSelectionEnd(Loc{forward, c.Y}.Move(1, c.buf)) c.SetSelectionStart(c.OrigSelection[0]) } c.Loc = c.CurSelection[1] } // SelectTo selects from the current cursor location to the given // location func (c *Cursor) SelectTo(loc Loc) { if loc.GreaterThan(c.OrigSelection[0]) { c.SetSelectionStart(c.OrigSelection[0]) c.SetSelectionEnd(loc) } else { c.SetSelectionStart(loc) c.SetSelectionEnd(c.OrigSelection[0]) } } // WordRight moves the cursor one word to the right func (c *Cursor) WordRight() { if c.X == util.CharacterCount(c.buf.LineBytes(c.Y)) { c.Right() return } for util.IsWhitespace(c.RuneUnder(c.X)) { if c.X == util.CharacterCount(c.buf.LineBytes(c.Y)) { return } c.Right() } if util.IsNonWordChar(c.RuneUnder(c.X)) && !util.IsWhitespace(c.RuneUnder(c.X)) && util.IsNonWordChar(c.RuneUnder(c.X+1)) { for util.IsNonWordChar(c.RuneUnder(c.X)) && !util.IsWhitespace(c.RuneUnder(c.X)) { if c.X == util.CharacterCount(c.buf.LineBytes(c.Y)) { return } c.Right() } return } c.Right() for util.IsWordChar(c.RuneUnder(c.X)) { if c.X == util.CharacterCount(c.buf.LineBytes(c.Y)) { return } c.Right() } } // WordLeft moves the cursor one word to the left func (c *Cursor) WordLeft() { if c.X == 0 { c.Left() return } c.Left() for util.IsWhitespace(c.RuneUnder(c.X)) { if c.X == 0 { return } c.Left() } if util.IsNonWordChar(c.RuneUnder(c.X)) && !util.IsWhitespace(c.RuneUnder(c.X)) && util.IsNonWordChar(c.RuneUnder(c.X-1)) { for util.IsNonWordChar(c.RuneUnder(c.X)) && !util.IsWhitespace(c.RuneUnder(c.X)) { if c.X == 0 { return } c.Left() } c.Right() return } c.Left() for util.IsWordChar(c.RuneUnder(c.X)) { if c.X == 0 { return } c.Left() } c.Right() } // SubWordRight moves the cursor one sub-word to the right func (c *Cursor) SubWordRight() { if c.X == util.CharacterCount(c.buf.LineBytes(c.Y)) { c.Right() return } if util.IsWhitespace(c.RuneUnder(c.X)) { for util.IsWhitespace(c.RuneUnder(c.X)) { if c.X == util.CharacterCount(c.buf.LineBytes(c.Y)) { return } c.Right() } return } if util.IsNonWordChar(c.RuneUnder(c.X)) && !util.IsWhitespace(c.RuneUnder(c.X)) { for util.IsNonWordChar(c.RuneUnder(c.X)) && !util.IsWhitespace(c.RuneUnder(c.X)) { if c.X == util.CharacterCount(c.buf.LineBytes(c.Y)) { return } c.Right() } return } if util.IsSubwordDelimiter(c.RuneUnder(c.X)) { for util.IsSubwordDelimiter(c.RuneUnder(c.X)) { if c.X == util.CharacterCount(c.buf.LineBytes(c.Y)) { return } c.Right() } if util.IsWhitespace(c.RuneUnder(c.X)) { return } } if c.X == util.CharacterCount(c.buf.LineBytes(c.Y)) { return } if util.IsUpperLetter(c.RuneUnder(c.X)) && util.IsUpperLetter(c.RuneUnder(c.X+1)) { for util.IsUpperAlphanumeric(c.RuneUnder(c.X)) { if c.X == util.CharacterCount(c.buf.LineBytes(c.Y)) { return } c.Right() } if util.IsLowerAlphanumeric(c.RuneUnder(c.X)) { c.Left() } } else { c.Right() for util.IsLowerAlphanumeric(c.RuneUnder(c.X)) { if c.X == util.CharacterCount(c.buf.LineBytes(c.Y)) { return } c.Right() } } } // SubWordLeft moves the cursor one sub-word to the left func (c *Cursor) SubWordLeft() { if c.X == 0 { c.Left() return } c.Left() if util.IsWhitespace(c.RuneUnder(c.X)) { for util.IsWhitespace(c.RuneUnder(c.X)) { if c.X == 0 { return } c.Left() } c.Right() return } if util.IsNonWordChar(c.RuneUnder(c.X)) && !util.IsWhitespace(c.RuneUnder(c.X)) { for util.IsNonWordChar(c.RuneUnder(c.X)) && !util.IsWhitespace(c.RuneUnder(c.X)) { if c.X == 0 { return } c.Left() } c.Right() return } if util.IsSubwordDelimiter(c.RuneUnder(c.X)) { for util.IsSubwordDelimiter(c.RuneUnder(c.X)) { if c.X == 0 { return } c.Left() } if util.IsWhitespace(c.RuneUnder(c.X)) { c.Right() return } } if c.X == 0 { return } if util.IsUpperLetter(c.RuneUnder(c.X)) && util.IsUpperLetter(c.RuneUnder(c.X-1)) { for util.IsUpperAlphanumeric(c.RuneUnder(c.X)) { if c.X == 0 { return } c.Left() } if !util.IsUpperAlphanumeric(c.RuneUnder(c.X)) { c.Right() } } else { for util.IsLowerAlphanumeric(c.RuneUnder(c.X)) { if c.X == 0 { return } c.Left() } if !util.IsAlphanumeric(c.RuneUnder(c.X)) { c.Right() } } } // RuneUnder returns the rune under the given x position func (c *Cursor) RuneUnder(x int) rune { line := c.buf.LineBytes(c.Y) if len(line) == 0 || x >= util.CharacterCount(line) { return '\n' } else if x < 0 { x = 0 } i := 0 for len(line) > 0 { r, _, size := util.DecodeCharacter(line) line = line[size:] if i == x { return r } i++ } return '\n' } func (c *Cursor) StoreVisualX() { c.LastVisualX = c.GetVisualX() } micro-2.0.14/internal/buffer/eventhandler.go0000664000175000017510000002370514663411671020353 0ustar nileshnileshpackage buffer import ( "bytes" "time" dmp "github.com/sergi/go-diff/diffmatchpatch" "github.com/zyedidia/micro/v2/internal/config" ulua "github.com/zyedidia/micro/v2/internal/lua" "github.com/zyedidia/micro/v2/internal/screen" "github.com/zyedidia/micro/v2/internal/util" luar "layeh.com/gopher-luar" ) const ( // Opposite and undoing events must have opposite values // TextEventInsert represents an insertion event TextEventInsert = 1 // TextEventRemove represents a deletion event TextEventRemove = -1 // TextEventReplace represents a replace event TextEventReplace = 0 undoThreshold = 1000 // If two events are less than n milliseconds apart, undo both of them ) // TextEvent holds data for a manipulation on some text that can be undone type TextEvent struct { C Cursor EventType int Deltas []Delta Time time.Time } // A Delta is a change to the buffer type Delta struct { Text []byte Start Loc End Loc } // DoTextEvent runs a text event func (eh *EventHandler) DoTextEvent(t *TextEvent, useUndo bool) { oldl := eh.buf.LinesNum() if useUndo { eh.Execute(t) } else { ExecuteTextEvent(t, eh.buf) } if len(t.Deltas) != 1 { return } text := t.Deltas[0].Text start := t.Deltas[0].Start lastnl := -1 var endX int var textX int if t.EventType == TextEventInsert { linecount := eh.buf.LinesNum() - oldl textcount := util.CharacterCount(text) lastnl = bytes.LastIndex(text, []byte{'\n'}) if lastnl >= 0 { endX = util.CharacterCount(text[lastnl+1:]) textX = endX } else { endX = start.X + textcount textX = textcount } t.Deltas[0].End = clamp(Loc{endX, start.Y + linecount}, eh.buf.LineArray) } end := t.Deltas[0].End for _, c := range eh.cursors { move := func(loc Loc) Loc { if t.EventType == TextEventInsert { if start.Y != loc.Y && loc.GreaterThan(start) { loc.Y += end.Y - start.Y } else if loc.Y == start.Y && loc.GreaterEqual(start) { loc.Y += end.Y - start.Y if lastnl >= 0 { loc.X += textX - start.X } else { loc.X += textX } } return loc } else { if loc.Y != end.Y && loc.GreaterThan(end) { loc.Y -= end.Y - start.Y } else if loc.Y == end.Y && loc.GreaterEqual(end) { loc = loc.MoveLA(-DiffLA(start, end, eh.buf.LineArray), eh.buf.LineArray) } return loc } } c.Loc = move(c.Loc) c.CurSelection[0] = move(c.CurSelection[0]) c.CurSelection[1] = move(c.CurSelection[1]) c.OrigSelection[0] = move(c.OrigSelection[0]) c.OrigSelection[1] = move(c.OrigSelection[1]) c.Relocate() c.LastVisualX = c.GetVisualX() } if useUndo { eh.updateTrailingWs(t) } } // ExecuteTextEvent runs a text event func ExecuteTextEvent(t *TextEvent, buf *SharedBuffer) { if t.EventType == TextEventInsert { for _, d := range t.Deltas { buf.insert(d.Start, d.Text) } } else if t.EventType == TextEventRemove { for i, d := range t.Deltas { t.Deltas[i].Text = buf.remove(d.Start, d.End) } } else if t.EventType == TextEventReplace { for i, d := range t.Deltas { t.Deltas[i].Text = buf.remove(d.Start, d.End) buf.insert(d.Start, d.Text) t.Deltas[i].Start = d.Start t.Deltas[i].End = Loc{d.Start.X + util.CharacterCount(d.Text), d.Start.Y} } for i, j := 0, len(t.Deltas)-1; i < j; i, j = i+1, j-1 { t.Deltas[i], t.Deltas[j] = t.Deltas[j], t.Deltas[i] } } } // UndoTextEvent undoes a text event func (eh *EventHandler) UndoTextEvent(t *TextEvent) { t.EventType = -t.EventType eh.DoTextEvent(t, false) } // EventHandler executes text manipulations and allows undoing and redoing type EventHandler struct { buf *SharedBuffer cursors []*Cursor active int UndoStack *TEStack RedoStack *TEStack } // NewEventHandler returns a new EventHandler func NewEventHandler(buf *SharedBuffer, cursors []*Cursor) *EventHandler { eh := new(EventHandler) eh.UndoStack = new(TEStack) eh.RedoStack = new(TEStack) eh.buf = buf eh.cursors = cursors return eh } // ApplyDiff takes a string and runs the necessary insertion and deletion events to make // the buffer equal to that string // This means that we can transform the buffer into any string and still preserve undo/redo // through insert and delete events func (eh *EventHandler) ApplyDiff(new string) { differ := dmp.New() diff := differ.DiffMain(string(eh.buf.Bytes()), new, false) loc := eh.buf.Start() for _, d := range diff { if d.Type == dmp.DiffDelete { eh.Remove(loc, loc.MoveLA(util.CharacterCountInString(d.Text), eh.buf.LineArray)) } else { if d.Type == dmp.DiffInsert { eh.Insert(loc, d.Text) } loc = loc.MoveLA(util.CharacterCountInString(d.Text), eh.buf.LineArray) } } } // Insert creates an insert text event and executes it func (eh *EventHandler) Insert(start Loc, textStr string) { text := []byte(textStr) eh.InsertBytes(start, text) } // InsertBytes creates an insert text event and executes it func (eh *EventHandler) InsertBytes(start Loc, text []byte) { if len(text) == 0 { return } start = clamp(start, eh.buf.LineArray) e := &TextEvent{ C: *eh.cursors[eh.active], EventType: TextEventInsert, Deltas: []Delta{{text, start, Loc{0, 0}}}, Time: time.Now(), } eh.DoTextEvent(e, true) } // Remove creates a remove text event and executes it func (eh *EventHandler) Remove(start, end Loc) { if start == end { return } start = clamp(start, eh.buf.LineArray) end = clamp(end, eh.buf.LineArray) e := &TextEvent{ C: *eh.cursors[eh.active], EventType: TextEventRemove, Deltas: []Delta{{[]byte{}, start, end}}, Time: time.Now(), } eh.DoTextEvent(e, true) } // MultipleReplace creates an multiple insertions executes them func (eh *EventHandler) MultipleReplace(deltas []Delta) { e := &TextEvent{ C: *eh.cursors[eh.active], EventType: TextEventReplace, Deltas: deltas, Time: time.Now(), } eh.Execute(e) } // Replace deletes from start to end and replaces it with the given string func (eh *EventHandler) Replace(start, end Loc, replace string) { eh.Remove(start, end) eh.Insert(start, replace) } // Execute a textevent and add it to the undo stack func (eh *EventHandler) Execute(t *TextEvent) { if eh.RedoStack.Len() > 0 { eh.RedoStack = new(TEStack) } eh.UndoStack.Push(t) b, err := config.RunPluginFnBool(nil, "onBeforeTextEvent", luar.New(ulua.L, eh.buf), luar.New(ulua.L, t)) if err != nil { screen.TermMessage(err) } if !b { return } ExecuteTextEvent(t, eh.buf) } // Undo the first event in the undo stack. Returns false if the stack is empty. func (eh *EventHandler) Undo() bool { t := eh.UndoStack.Peek() if t == nil { return false } startTime := t.Time.UnixNano() / int64(time.Millisecond) endTime := startTime - (startTime % undoThreshold) for { t = eh.UndoStack.Peek() if t == nil { break } if t.Time.UnixNano()/int64(time.Millisecond) < endTime { break } eh.UndoOneEvent() } return true } // UndoOneEvent undoes one event func (eh *EventHandler) UndoOneEvent() { // This event should be undone // Pop it off the stack t := eh.UndoStack.Pop() if t == nil { return } // Undo it // Modifies the text event eh.UndoTextEvent(t) // Set the cursor in the right place if t.C.Num >= 0 && t.C.Num < len(eh.cursors) { eh.cursors[t.C.Num].Goto(t.C) eh.cursors[t.C.Num].NewTrailingWsY = t.C.NewTrailingWsY } // Push it to the redo stack eh.RedoStack.Push(t) } // Redo the first event in the redo stack. Returns false if the stack is empty. func (eh *EventHandler) Redo() bool { t := eh.RedoStack.Peek() if t == nil { return false } startTime := t.Time.UnixNano() / int64(time.Millisecond) endTime := startTime - (startTime % undoThreshold) + undoThreshold for { t = eh.RedoStack.Peek() if t == nil { break } if t.Time.UnixNano()/int64(time.Millisecond) > endTime { break } eh.RedoOneEvent() } return true } // RedoOneEvent redoes one event func (eh *EventHandler) RedoOneEvent() { t := eh.RedoStack.Pop() if t == nil { return } if t.C.Num >= 0 && t.C.Num < len(eh.cursors) { eh.cursors[t.C.Num].Goto(t.C) eh.cursors[t.C.Num].NewTrailingWsY = t.C.NewTrailingWsY } // Modifies the text event eh.UndoTextEvent(t) eh.UndoStack.Push(t) } // updateTrailingWs updates the cursor's trailing whitespace status after a text event func (eh *EventHandler) updateTrailingWs(t *TextEvent) { if len(t.Deltas) != 1 { return } text := t.Deltas[0].Text start := t.Deltas[0].Start end := t.Deltas[0].End c := eh.cursors[eh.active] isEol := func(loc Loc) bool { return loc.X == util.CharacterCount(eh.buf.LineBytes(loc.Y)) } if t.EventType == TextEventInsert && c.Loc == end && isEol(end) { var addedTrailingWs bool addedAfterWs := false addedWsOnly := false if start.Y == end.Y { addedTrailingWs = util.HasTrailingWhitespace(text) addedWsOnly = util.IsBytesWhitespace(text) addedAfterWs = start.X > 0 && util.IsWhitespace(c.buf.RuneAt(Loc{start.X - 1, start.Y})) } else { lastnl := bytes.LastIndex(text, []byte{'\n'}) addedTrailingWs = util.HasTrailingWhitespace(text[lastnl+1:]) } if addedTrailingWs && !(addedAfterWs && addedWsOnly) { c.NewTrailingWsY = c.Y } else if !addedTrailingWs { c.NewTrailingWsY = -1 } } else if t.EventType == TextEventRemove && c.Loc == start && isEol(start) { removedAfterWs := util.HasTrailingWhitespace(eh.buf.LineBytes(start.Y)) var removedWsOnly bool if start.Y == end.Y { removedWsOnly = util.IsBytesWhitespace(text) } else { firstnl := bytes.Index(text, []byte{'\n'}) removedWsOnly = util.IsBytesWhitespace(text[:firstnl]) } if removedAfterWs && !removedWsOnly { c.NewTrailingWsY = c.Y } else if !removedAfterWs { c.NewTrailingWsY = -1 } } else if c.NewTrailingWsY != -1 && start.Y != end.Y && c.Loc.GreaterThan(start) && ((t.EventType == TextEventInsert && c.Y == c.NewTrailingWsY+(end.Y-start.Y)) || (t.EventType == TextEventRemove && c.Y == c.NewTrailingWsY-(end.Y-start.Y))) { // The cursor still has its new trailingws // but its line number was shifted by insert or remove of lines above c.NewTrailingWsY = c.Y } } micro-2.0.14/internal/buffer/line_array.go0000664000175000017510000002727714663411671020031 0ustar nileshnileshpackage buffer import ( "bufio" "bytes" "io" "sync" "github.com/zyedidia/micro/v2/internal/util" "github.com/zyedidia/micro/v2/pkg/highlight" ) // Finds the byte index of the nth rune in a byte slice func runeToByteIndex(n int, txt []byte) int { if n == 0 { return 0 } count := 0 i := 0 for len(txt) > 0 { _, _, size := util.DecodeCharacter(txt) txt = txt[size:] count += size i++ if i == n { break } } return count } // A searchState contains the search match info for a single line type searchState struct { search string useRegex bool ignorecase bool match [][2]int done bool } // A Line contains the data in bytes as well as a highlight state, match // and a flag for whether the highlighting needs to be updated type Line struct { data []byte state highlight.State match highlight.LineMatch lock sync.Mutex // The search states for the line, used for highlighting of search matches, // separately from the syntax highlighting. // A map is used because the line array may be shared between multiple buffers // (multiple instances of the same file opened in different edit panes) // which have distinct searches, so in the general case there are multiple // searches per a line, one search per a Buffer containing this line. search map[*Buffer]*searchState } const ( // Line ending file formats FFAuto = 0 // Autodetect format FFUnix = 1 // LF line endings (unix style '\n') FFDos = 2 // CRLF line endings (dos style '\r\n') ) type FileFormat byte // A LineArray simply stores and array of lines and makes it easy to insert // and delete in it type LineArray struct { lines []Line Endings FileFormat initsize uint64 lock sync.Mutex } // Append efficiently appends lines together // It allocates an additional 10000 lines if the original estimate // is incorrect func Append(slice []Line, data ...Line) []Line { l := len(slice) if l+len(data) > cap(slice) { // reallocate newSlice := make([]Line, (l+len(data))+10000) copy(newSlice, slice) slice = newSlice } slice = slice[0 : l+len(data)] for i, c := range data { slice[l+i] = c } return slice } // NewLineArray returns a new line array from an array of bytes func NewLineArray(size uint64, endings FileFormat, reader io.Reader) *LineArray { la := new(LineArray) la.lines = make([]Line, 0, 1000) la.initsize = size br := bufio.NewReader(reader) var loaded int la.Endings = endings n := 0 for { data, err := br.ReadBytes('\n') // Detect the line ending by checking to see if there is a '\r' char // before the '\n' // Even if the file format is set to DOS, the '\r' is removed so // that all lines end with '\n' dlen := len(data) if dlen > 1 && data[dlen-2] == '\r' { data = append(data[:dlen-2], '\n') if la.Endings == FFAuto { la.Endings = FFDos } dlen = len(data) } else if dlen > 0 { if la.Endings == FFAuto { la.Endings = FFUnix } } // If we are loading a large file (greater than 1000) we use the file // size and the length of the first 1000 lines to try to estimate // how many lines will need to be allocated for the rest of the file // We add an extra 10000 to the original estimate to be safe and give // plenty of room for expansion if n >= 1000 && loaded >= 0 { totalLinesNum := int(float64(size) * (float64(n) / float64(loaded))) newSlice := make([]Line, len(la.lines), totalLinesNum+10000) copy(newSlice, la.lines) la.lines = newSlice loaded = -1 } // Counter for the number of bytes in the first 1000 lines if loaded >= 0 { loaded += dlen } if err != nil { if err == io.EOF { la.lines = Append(la.lines, Line{ data: data, state: nil, match: nil, }) } // Last line was read break } else { la.lines = Append(la.lines, Line{ data: data[:dlen-1], state: nil, match: nil, }) } n++ } return la } // Bytes returns the string that should be written to disk when // the line array is saved func (la *LineArray) Bytes() []byte { b := new(bytes.Buffer) // initsize should provide a good estimate b.Grow(int(la.initsize + 4096)) for i, l := range la.lines { b.Write(l.data) if i != len(la.lines)-1 { if la.Endings == FFDos { b.WriteByte('\r') } b.WriteByte('\n') } } return b.Bytes() } // newlineBelow adds a newline below the given line number func (la *LineArray) newlineBelow(y int) { la.lines = append(la.lines, Line{ data: []byte{' '}, state: nil, match: nil, }) copy(la.lines[y+2:], la.lines[y+1:]) la.lines[y+1] = Line{ data: []byte{}, state: la.lines[y].state, match: nil, } } // Inserts a byte array at a given location func (la *LineArray) insert(pos Loc, value []byte) { la.lock.Lock() defer la.lock.Unlock() x, y := runeToByteIndex(pos.X, la.lines[pos.Y].data), pos.Y for i := 0; i < len(value); i++ { if value[i] == '\n' || (value[i] == '\r' && i < len(value)-1 && value[i+1] == '\n') { la.split(Loc{x, y}) x = 0 y++ if value[i] == '\r' { i++ } continue } la.insertByte(Loc{x, y}, value[i]) x++ } } // InsertByte inserts a byte at a given location func (la *LineArray) insertByte(pos Loc, value byte) { la.lines[pos.Y].data = append(la.lines[pos.Y].data, 0) copy(la.lines[pos.Y].data[pos.X+1:], la.lines[pos.Y].data[pos.X:]) la.lines[pos.Y].data[pos.X] = value } // joinLines joins the two lines a and b func (la *LineArray) joinLines(a, b int) { la.lines[a].data = append(la.lines[a].data, la.lines[b].data...) la.deleteLine(b) } // split splits a line at a given position func (la *LineArray) split(pos Loc) { la.newlineBelow(pos.Y) la.lines[pos.Y+1].data = append(la.lines[pos.Y+1].data, la.lines[pos.Y].data[pos.X:]...) la.lines[pos.Y+1].state = la.lines[pos.Y].state la.lines[pos.Y].state = nil la.lines[pos.Y].match = nil la.lines[pos.Y+1].match = nil la.deleteToEnd(Loc{pos.X, pos.Y}) } // removes from start to end func (la *LineArray) remove(start, end Loc) []byte { la.lock.Lock() defer la.lock.Unlock() sub := la.Substr(start, end) startX := runeToByteIndex(start.X, la.lines[start.Y].data) endX := runeToByteIndex(end.X, la.lines[end.Y].data) if start.Y == end.Y { la.lines[start.Y].data = append(la.lines[start.Y].data[:startX], la.lines[start.Y].data[endX:]...) } else { la.deleteLines(start.Y+1, end.Y-1) la.deleteToEnd(Loc{startX, start.Y}) la.deleteFromStart(Loc{endX - 1, start.Y + 1}) la.joinLines(start.Y, start.Y+1) } return sub } // deleteToEnd deletes from the end of a line to the position func (la *LineArray) deleteToEnd(pos Loc) { la.lines[pos.Y].data = la.lines[pos.Y].data[:pos.X] } // deleteFromStart deletes from the start of a line to the position func (la *LineArray) deleteFromStart(pos Loc) { la.lines[pos.Y].data = la.lines[pos.Y].data[pos.X+1:] } // deleteLine deletes the line number func (la *LineArray) deleteLine(y int) { la.lines = la.lines[:y+copy(la.lines[y:], la.lines[y+1:])] } func (la *LineArray) deleteLines(y1, y2 int) { la.lines = la.lines[:y1+copy(la.lines[y1:], la.lines[y2+1:])] } // DeleteByte deletes the byte at a position func (la *LineArray) deleteByte(pos Loc) { la.lines[pos.Y].data = la.lines[pos.Y].data[:pos.X+copy(la.lines[pos.Y].data[pos.X:], la.lines[pos.Y].data[pos.X+1:])] } // Substr returns the string representation between two locations func (la *LineArray) Substr(start, end Loc) []byte { startX := runeToByteIndex(start.X, la.lines[start.Y].data) endX := runeToByteIndex(end.X, la.lines[end.Y].data) if start.Y == end.Y { src := la.lines[start.Y].data[startX:endX] dest := make([]byte, len(src)) copy(dest, src) return dest } str := make([]byte, 0, len(la.lines[start.Y+1].data)*(end.Y-start.Y)) str = append(str, la.lines[start.Y].data[startX:]...) str = append(str, '\n') for i := start.Y + 1; i <= end.Y-1; i++ { str = append(str, la.lines[i].data...) str = append(str, '\n') } str = append(str, la.lines[end.Y].data[:endX]...) return str } // LinesNum returns the number of lines in the buffer func (la *LineArray) LinesNum() int { return len(la.lines) } // Start returns the start of the buffer func (la *LineArray) Start() Loc { return Loc{0, 0} } // End returns the location of the last character in the buffer func (la *LineArray) End() Loc { numlines := len(la.lines) return Loc{util.CharacterCount(la.lines[numlines-1].data), numlines - 1} } // LineBytes returns line n as an array of bytes func (la *LineArray) LineBytes(lineN int) []byte { if lineN >= len(la.lines) || lineN < 0 { return []byte{} } return la.lines[lineN].data } // State gets the highlight state for the given line number func (la *LineArray) State(lineN int) highlight.State { la.lines[lineN].lock.Lock() defer la.lines[lineN].lock.Unlock() return la.lines[lineN].state } // SetState sets the highlight state at the given line number func (la *LineArray) SetState(lineN int, s highlight.State) { la.lines[lineN].lock.Lock() defer la.lines[lineN].lock.Unlock() la.lines[lineN].state = s } // SetMatch sets the match at the given line number func (la *LineArray) SetMatch(lineN int, m highlight.LineMatch) { la.lines[lineN].lock.Lock() defer la.lines[lineN].lock.Unlock() la.lines[lineN].match = m } // Match retrieves the match for the given line number func (la *LineArray) Match(lineN int) highlight.LineMatch { la.lines[lineN].lock.Lock() defer la.lines[lineN].lock.Unlock() return la.lines[lineN].match } // Locks the whole LineArray func (la *LineArray) Lock() { la.lock.Lock() } // Unlocks the whole LineArray func (la *LineArray) Unlock() { la.lock.Unlock() } // SearchMatch returns true if the location `pos` is within a match // of the last search for the buffer `b`. // It is used for efficient highlighting of search matches (separately // from the syntax highlighting). // SearchMatch searches for the matches if it is called first time // for the given line or if the line was modified. Otherwise the // previously found matches are used. // // The buffer `b` needs to be passed because the line array may be shared // between multiple buffers (multiple instances of the same file opened // in different edit panes) which have distinct searches, so SearchMatch // needs to know which search to match against. func (la *LineArray) SearchMatch(b *Buffer, pos Loc) bool { if b.LastSearch == "" { return false } lineN := pos.Y if la.lines[lineN].search == nil { la.lines[lineN].search = make(map[*Buffer]*searchState) } s, ok := la.lines[lineN].search[b] if !ok { // Note: here is a small harmless leak: when the buffer `b` is closed, // `s` is not deleted from the map. It means that the buffer // will not be garbage-collected until the line array is garbage-collected, // i.e. until all the buffers sharing this file are closed. s = new(searchState) la.lines[lineN].search[b] = s } if !ok || s.search != b.LastSearch || s.useRegex != b.LastSearchRegex || s.ignorecase != b.Settings["ignorecase"].(bool) { s.search = b.LastSearch s.useRegex = b.LastSearchRegex s.ignorecase = b.Settings["ignorecase"].(bool) s.done = false } if !s.done { s.match = nil start := Loc{0, lineN} end := Loc{util.CharacterCount(la.lines[lineN].data), lineN} for start.X < end.X { m, found, _ := b.FindNext(b.LastSearch, start, end, start, true, b.LastSearchRegex) if !found { break } s.match = append(s.match, [2]int{m[0].X, m[1].X}) start.X = m[1].X if m[1].X == m[0].X { start.X = m[1].X + 1 } } s.done = true } for _, m := range s.match { if pos.X >= m[0] && pos.X < m[1] { return true } } return false } // invalidateSearchMatches marks search matches for the given line as outdated. // It is called when the line is modified. func (la *LineArray) invalidateSearchMatches(lineN int) { if la.lines[lineN].search != nil { for _, s := range la.lines[lineN].search { s.done = false } } } micro-2.0.14/internal/buffer/line_array_test.go0000664000175000017510000000352314663411671021054 0ustar nileshnileshpackage buffer import ( "strings" "testing" "github.com/stretchr/testify/assert" ) var unicode_txt = `An preost wes on leoden, LaÈamon was ihoten He wes Leovenaðes sone -- liðe him be Drihten. He wonede at ErnleÈe at æðelen are chirechen, Uppen Sevarne staþe, sel þar him þuhte, Onfest Radestone, þer he bock radde.` var la *LineArray func init() { reader := strings.NewReader(unicode_txt) la = NewLineArray(uint64(len(unicode_txt)), FFAuto, reader) } func TestSplit(t *testing.T) { la.insert(Loc{17, 1}, []byte{'\n'}) assert.Equal(t, len(la.lines), 6) sub1 := la.Substr(Loc{0, 1}, Loc{17, 1}) sub2 := la.Substr(Loc{0, 2}, Loc{30, 2}) assert.Equal(t, []byte("He wes Leovenaðes"), sub1) assert.Equal(t, []byte(" sone -- liðe him be Drihten."), sub2) } func TestJoin(t *testing.T) { la.remove(Loc{47, 1}, Loc{0, 2}) assert.Equal(t, len(la.lines), 5) sub := la.Substr(Loc{0, 1}, Loc{47, 1}) bytes := la.Bytes() assert.Equal(t, []byte("He wes Leovenaðes sone -- liðe him be Drihten."), sub) assert.Equal(t, unicode_txt, string(bytes)) } func TestInsert(t *testing.T) { la.insert(Loc{20, 3}, []byte(" foobar")) sub1 := la.Substr(Loc{0, 3}, Loc{50, 3}) assert.Equal(t, []byte("Uppen Sevarne staþe, foobar sel þar him þuhte,"), sub1) la.insert(Loc{25, 2}, []byte("H̼̥̯͇͙̕͘͞e̸̦̞̠̣̰͙̼̥̦̼̖̬͕͕̰̯̫͇̕ĺ̜̠̩̯̯͙̼̭̠͕̮̞͜l̶͓̫̞̮͈͞ͅo̸͔͙̳̠͈̮̼̳͙̥̲͜͠")) sub2 := la.Substr(Loc{0, 2}, Loc{60, 2}) assert.Equal(t, []byte("He wonede at ErnleÈe at æH̼̥̯͇͙̕͘͞e̸̦̞̠̣̰͙̼̥̦̼̖̬͕͕̰̯̫͇̕ĺ̜̠̩̯̯͙̼̭̠͕̮̞͜l̶͓̫̞̮͈͞ͅo̸͔͙̳̠͈̮̼̳͙̥̲͜͠ðelen are chirechen,"), sub2) } func TestRemove(t *testing.T) { la.remove(Loc{20, 3}, Loc{27, 3}) la.remove(Loc{25, 2}, Loc{30, 2}) bytes := la.Bytes() assert.Equal(t, unicode_txt, string(bytes)) } micro-2.0.14/internal/buffer/loc.go0000664000175000017510000000563414663411671016452 0ustar nileshnileshpackage buffer import ( "github.com/zyedidia/micro/v2/internal/util" ) // Loc stores a location type Loc struct { X, Y int } // LessThan returns true if b is smaller func (l Loc) LessThan(b Loc) bool { if l.Y < b.Y { return true } return l.Y == b.Y && l.X < b.X } // GreaterThan returns true if b is bigger func (l Loc) GreaterThan(b Loc) bool { if l.Y > b.Y { return true } return l.Y == b.Y && l.X > b.X } // GreaterEqual returns true if b is greater than or equal to b func (l Loc) GreaterEqual(b Loc) bool { if l.Y > b.Y { return true } if l.Y == b.Y && l.X > b.X { return true } return l == b } // LessEqual returns true if b is less than or equal to b func (l Loc) LessEqual(b Loc) bool { if l.Y < b.Y { return true } if l.Y == b.Y && l.X < b.X { return true } return l == b } // The following functions require a buffer to know where newlines are // Diff returns the distance between two locations func DiffLA(a, b Loc, buf *LineArray) int { if a.Y == b.Y { if a.X > b.X { return a.X - b.X } return b.X - a.X } // Make sure a is guaranteed to be less than b if b.LessThan(a) { a, b = b, a } loc := 0 for i := a.Y + 1; i < b.Y; i++ { // + 1 for the newline loc += util.CharacterCount(buf.LineBytes(i)) + 1 } loc += util.CharacterCount(buf.LineBytes(a.Y)) - a.X + b.X + 1 return loc } // This moves the location one character to the right func (l Loc) right(buf *LineArray) Loc { if l == buf.End() { return Loc{l.X + 1, l.Y} } var res Loc if l.X < util.CharacterCount(buf.LineBytes(l.Y)) { res = Loc{l.X + 1, l.Y} } else { res = Loc{0, l.Y + 1} } return res } // This moves the given location one character to the left func (l Loc) left(buf *LineArray) Loc { if l == buf.Start() { return Loc{l.X - 1, l.Y} } var res Loc if l.X > 0 { res = Loc{l.X - 1, l.Y} } else { res = Loc{util.CharacterCount(buf.LineBytes(l.Y - 1)), l.Y - 1} } return res } // MoveLA moves the cursor n characters to the left or right // It moves the cursor left if n is negative func (l Loc) MoveLA(n int, buf *LineArray) Loc { if n > 0 { for i := 0; i < n; i++ { l = l.right(buf) } return l } for i := 0; i < util.Abs(n); i++ { l = l.left(buf) } return l } // Diff returns the difference between two locs func (l Loc) Diff(b Loc, buf *Buffer) int { return DiffLA(l, b, buf.LineArray) } // Move moves a loc n characters func (l Loc) Move(n int, buf *Buffer) Loc { return l.MoveLA(n, buf.LineArray) } // ByteOffset is just like ToCharPos except it counts bytes instead of runes func ByteOffset(pos Loc, buf *Buffer) int { x, y := pos.X, pos.Y loc := 0 for i := 0; i < y; i++ { // + 1 for the newline loc += len(buf.Line(i)) + 1 } loc += len(buf.Line(y)[:x]) return loc } // clamps a loc within a buffer func clamp(pos Loc, la *LineArray) Loc { if pos.GreaterEqual(la.End()) { return la.End() } else if pos.LessThan(la.Start()) { return la.Start() } return pos } micro-2.0.14/internal/buffer/message.go0000664000175000017510000000352414663411671017315 0ustar nileshnileshpackage buffer import ( "github.com/zyedidia/micro/v2/internal/config" "github.com/zyedidia/tcell/v2" ) type MsgType int const ( MTInfo = iota MTWarning MTError ) // Message represents the information for a gutter message type Message struct { // The Msg iteslf Msg string // Start and End locations for the message Start, End Loc // The Kind stores the message type Kind MsgType // The Owner of the message Owner string } // NewMessage creates a new gutter message func NewMessage(owner string, msg string, start, end Loc, kind MsgType) *Message { return &Message{ Msg: msg, Start: start, End: end, Kind: kind, Owner: owner, } } // NewMessageAtLine creates a new gutter message at a given line func NewMessageAtLine(owner string, msg string, line int, kind MsgType) *Message { start := Loc{-1, line - 1} end := start return NewMessage(owner, msg, start, end, kind) } func (m *Message) Style() tcell.Style { switch m.Kind { case MTInfo: if style, ok := config.Colorscheme["gutter-info"]; ok { return style } case MTWarning: if style, ok := config.Colorscheme["gutter-warning"]; ok { return style } case MTError: if style, ok := config.Colorscheme["gutter-error"]; ok { return style } } return config.DefStyle } func (b *Buffer) AddMessage(m *Message) { b.Messages = append(b.Messages, m) } func (b *Buffer) removeMsg(i int) { copy(b.Messages[i:], b.Messages[i+1:]) b.Messages[len(b.Messages)-1] = nil b.Messages = b.Messages[:len(b.Messages)-1] } func (b *Buffer) ClearMessages(owner string) { for i := len(b.Messages) - 1; i >= 0; i-- { if b.Messages[i].Owner == owner { b.removeMsg(i) } } } func (b *Buffer) ClearAllMessages() { b.Messages = make([]*Message, 0) } type Messager interface { Message(msg ...interface{}) } var prompt Messager func SetMessager(m Messager) { prompt = m } micro-2.0.14/internal/buffer/save.go0000664000175000017510000001336714663411671016635 0ustar nileshnileshpackage buffer import ( "bufio" "bytes" "errors" "io" "os" "os/exec" "os/signal" "path/filepath" "runtime" "unicode" "github.com/zyedidia/micro/v2/internal/config" "github.com/zyedidia/micro/v2/internal/screen" "github.com/zyedidia/micro/v2/internal/util" "golang.org/x/text/encoding" "golang.org/x/text/encoding/htmlindex" "golang.org/x/text/transform" ) // LargeFileThreshold is the number of bytes when fastdirty is forced // because hashing is too slow const LargeFileThreshold = 50000 // overwriteFile opens the given file for writing, truncating if one exists, and then calls // the supplied function with the file as io.Writer object, also making sure the file is // closed afterwards. func overwriteFile(name string, enc encoding.Encoding, fn func(io.Writer) error, withSudo bool) (err error) { var writeCloser io.WriteCloser var screenb bool var cmd *exec.Cmd if withSudo { cmd = exec.Command(config.GlobalSettings["sucmd"].(string), "dd", "bs=4k", "of="+name) if writeCloser, err = cmd.StdinPipe(); err != nil { return } c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt) go func() { <-c cmd.Process.Kill() }() screenb = screen.TempFini() // need to start the process now, otherwise when we flush the file // contents to its stdin it might hang because the kernel's pipe size // is too small to handle the full file contents all at once if e := cmd.Start(); e != nil && err == nil { screen.TempStart(screenb) return err } } else if writeCloser, err = os.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666); err != nil { return } w := bufio.NewWriter(transform.NewWriter(writeCloser, enc.NewEncoder())) err = fn(w) if err2 := w.Flush(); err2 != nil && err == nil { err = err2 } // Call Sync() on the file to make sure the content is safely on disk. // Does not work with sudo as we don't have direct access to the file. if !withSudo { f := writeCloser.(*os.File) if err2 := f.Sync(); err2 != nil && err == nil { err = err2 } } if err2 := writeCloser.Close(); err2 != nil && err == nil { err = err2 } if withSudo { // wait for dd to finish and restart the screen if we used sudo err := cmd.Wait() screen.TempStart(screenb) if err != nil { return err } } return } // Save saves the buffer to its default path func (b *Buffer) Save() error { return b.SaveAs(b.Path) } // AutoSave saves the buffer to its default path func (b *Buffer) AutoSave() error { // Doing full b.Modified() check every time would be costly, due to the hash // calculation. So use just isModified even if fastdirty is not set. if !b.isModified { return nil } return b.saveToFile(b.Path, false, true) } // SaveAs saves the buffer to a specified path (filename), creating the file if it does not exist func (b *Buffer) SaveAs(filename string) error { return b.saveToFile(filename, false, false) } func (b *Buffer) SaveWithSudo() error { return b.SaveAsWithSudo(b.Path) } func (b *Buffer) SaveAsWithSudo(filename string) error { return b.saveToFile(filename, true, false) } func (b *Buffer) saveToFile(filename string, withSudo bool, autoSave bool) error { var err error if b.Type.Readonly { return errors.New("Cannot save readonly buffer") } if b.Type.Scratch { return errors.New("Cannot save scratch buffer") } if withSudo && runtime.GOOS == "windows" { return errors.New("Save with sudo not supported on Windows") } if !autoSave && b.Settings["rmtrailingws"].(bool) { for i, l := range b.lines { leftover := util.CharacterCount(bytes.TrimRightFunc(l.data, unicode.IsSpace)) linelen := util.CharacterCount(l.data) b.Remove(Loc{leftover, i}, Loc{linelen, i}) } b.RelocateCursors() } if b.Settings["eofnewline"].(bool) { end := b.End() if b.RuneAt(Loc{end.X - 1, end.Y}) != '\n' { b.insert(end, []byte{'\n'}) } } // Update the last time this file was updated after saving defer func() { b.ModTime, _ = util.GetModTime(filename) err = b.Serialize() }() // Removes any tilde and replaces with the absolute path to home absFilename, _ := util.ReplaceHome(filename) // Get the leading path to the file | "." is returned if there's no leading path provided if dirname := filepath.Dir(absFilename); dirname != "." { // Check if the parent dirs don't exist if _, statErr := os.Stat(dirname); os.IsNotExist(statErr) { // Prompt to make sure they want to create the dirs that are missing if b.Settings["mkparents"].(bool) { // Create all leading dir(s) since they don't exist if mkdirallErr := os.MkdirAll(dirname, os.ModePerm); mkdirallErr != nil { // If there was an error creating the dirs return mkdirallErr } } else { return errors.New("Parent dirs don't exist, enable 'mkparents' for auto creation") } } } var fileSize int enc, err := htmlindex.Get(b.Settings["encoding"].(string)) if err != nil { return err } fwriter := func(file io.Writer) (e error) { if len(b.lines) == 0 { return } // end of line var eol []byte if b.Endings == FFDos { eol = []byte{'\r', '\n'} } else { eol = []byte{'\n'} } // write lines if fileSize, e = file.Write(b.lines[0].data); e != nil { return } for _, l := range b.lines[1:] { if _, e = file.Write(eol); e != nil { return } if _, e = file.Write(l.data); e != nil { return } fileSize += len(eol) + len(l.data) } return } if err = overwriteFile(absFilename, enc, fwriter, withSudo); err != nil { return err } if !b.Settings["fastdirty"].(bool) { if fileSize > LargeFileThreshold { // For large files 'fastdirty' needs to be on b.Settings["fastdirty"] = true } else { calcHash(b, &b.origHash) } } b.Path = filename absPath, _ := filepath.Abs(filename) b.AbsPath = absPath b.isModified = false b.UpdateRules() return err } micro-2.0.14/internal/buffer/search.go0000664000175000017510000001150014663411671017127 0ustar nileshnileshpackage buffer import ( "regexp" "github.com/zyedidia/micro/v2/internal/util" ) func (b *Buffer) findDown(r *regexp.Regexp, start, end Loc) ([2]Loc, bool) { lastcn := util.CharacterCount(b.LineBytes(b.LinesNum() - 1)) if start.Y > b.LinesNum()-1 { start.X = lastcn - 1 } if end.Y > b.LinesNum()-1 { end.X = lastcn } start.Y = util.Clamp(start.Y, 0, b.LinesNum()-1) end.Y = util.Clamp(end.Y, 0, b.LinesNum()-1) if start.GreaterThan(end) { start, end = end, start } for i := start.Y; i <= end.Y; i++ { l := b.LineBytes(i) charpos := 0 if i == start.Y && start.Y == end.Y { nchars := util.CharacterCount(l) start.X = util.Clamp(start.X, 0, nchars) end.X = util.Clamp(end.X, 0, nchars) l = util.SliceStart(l, end.X) l = util.SliceEnd(l, start.X) charpos = start.X } else if i == start.Y { nchars := util.CharacterCount(l) start.X = util.Clamp(start.X, 0, nchars) l = util.SliceEnd(l, start.X) charpos = start.X } else if i == end.Y { nchars := util.CharacterCount(l) end.X = util.Clamp(end.X, 0, nchars) l = util.SliceStart(l, end.X) } match := r.FindIndex(l) if match != nil { start := Loc{charpos + util.RunePos(l, match[0]), i} end := Loc{charpos + util.RunePos(l, match[1]), i} return [2]Loc{start, end}, true } } return [2]Loc{}, false } func (b *Buffer) findUp(r *regexp.Regexp, start, end Loc) ([2]Loc, bool) { lastcn := util.CharacterCount(b.LineBytes(b.LinesNum() - 1)) if start.Y > b.LinesNum()-1 { start.X = lastcn - 1 } if end.Y > b.LinesNum()-1 { end.X = lastcn } start.Y = util.Clamp(start.Y, 0, b.LinesNum()-1) end.Y = util.Clamp(end.Y, 0, b.LinesNum()-1) if start.GreaterThan(end) { start, end = end, start } for i := end.Y; i >= start.Y; i-- { l := b.LineBytes(i) charpos := 0 if i == start.Y && start.Y == end.Y { nchars := util.CharacterCount(l) start.X = util.Clamp(start.X, 0, nchars) end.X = util.Clamp(end.X, 0, nchars) l = util.SliceStart(l, end.X) l = util.SliceEnd(l, start.X) charpos = start.X } else if i == start.Y { nchars := util.CharacterCount(l) start.X = util.Clamp(start.X, 0, nchars) l = util.SliceEnd(l, start.X) charpos = start.X } else if i == end.Y { nchars := util.CharacterCount(l) end.X = util.Clamp(end.X, 0, nchars) l = util.SliceStart(l, end.X) } allMatches := r.FindAllIndex(l, -1) if allMatches != nil { match := allMatches[len(allMatches)-1] start := Loc{charpos + util.RunePos(l, match[0]), i} end := Loc{charpos + util.RunePos(l, match[1]), i} return [2]Loc{start, end}, true } } return [2]Loc{}, false } // FindNext finds the next occurrence of a given string in the buffer // It returns the start and end location of the match (if found) and // a boolean indicating if it was found // May also return an error if the search regex is invalid func (b *Buffer) FindNext(s string, start, end, from Loc, down bool, useRegex bool) ([2]Loc, bool, error) { if s == "" { return [2]Loc{}, false, nil } var r *regexp.Regexp var err error if !useRegex { s = regexp.QuoteMeta(s) } if b.Settings["ignorecase"].(bool) { r, err = regexp.Compile("(?i)" + s) } else { r, err = regexp.Compile(s) } if err != nil { return [2]Loc{}, false, err } var found bool var l [2]Loc if down { l, found = b.findDown(r, from, end) if !found { l, found = b.findDown(r, start, end) } } else { l, found = b.findUp(r, from, start) if !found { l, found = b.findUp(r, end, start) } } return l, found, nil } // ReplaceRegex replaces all occurrences of 'search' with 'replace' in the given area // and returns the number of replacements made and the number of runes // added or removed on the last line of the range func (b *Buffer) ReplaceRegex(start, end Loc, search *regexp.Regexp, replace []byte, captureGroups bool) (int, int) { if start.GreaterThan(end) { start, end = end, start } netrunes := 0 found := 0 var deltas []Delta for i := start.Y; i <= end.Y; i++ { l := b.lines[i].data charpos := 0 if start.Y == end.Y && i == start.Y { l = util.SliceStart(l, end.X) l = util.SliceEnd(l, start.X) charpos = start.X } else if i == start.Y { l = util.SliceEnd(l, start.X) charpos = start.X } else if i == end.Y { l = util.SliceStart(l, end.X) } newText := search.ReplaceAllFunc(l, func(in []byte) []byte { var result []byte if captureGroups { for _, submatches := range search.FindAllSubmatchIndex(in, -1) { result = search.Expand(result, replace, in, submatches) } } else { result = replace } found++ if i == end.Y { netrunes += util.CharacterCount(result) - util.CharacterCount(in) } return result }) from := Loc{charpos, i} to := Loc{charpos + util.CharacterCount(l), i} deltas = append(deltas, Delta{newText, from, to}) } b.MultipleReplace(deltas) return found, netrunes } micro-2.0.14/internal/buffer/serialize.go0000664000175000017510000000433414663411671017660 0ustar nileshnileshpackage buffer import ( "encoding/gob" "errors" "io" "os" "path/filepath" "time" "golang.org/x/text/encoding" "github.com/zyedidia/micro/v2/internal/config" "github.com/zyedidia/micro/v2/internal/util" ) // The SerializedBuffer holds the types that get serialized when a buffer is saved // These are used for the savecursor and saveundo options type SerializedBuffer struct { EventHandler *EventHandler Cursor Loc ModTime time.Time } // Serialize serializes the buffer to config.ConfigDir/buffers func (b *Buffer) Serialize() error { if !b.Settings["savecursor"].(bool) && !b.Settings["saveundo"].(bool) { return nil } if b.Path == "" { return nil } name := filepath.Join(config.ConfigDir, "buffers", util.EscapePath(b.AbsPath)) return overwriteFile(name, encoding.Nop, func(file io.Writer) error { err := gob.NewEncoder(file).Encode(SerializedBuffer{ b.EventHandler, b.GetActiveCursor().Loc, b.ModTime, }) return err }, false) } // Unserialize loads the buffer info from config.ConfigDir/buffers func (b *Buffer) Unserialize() error { // If either savecursor or saveundo is turned on, we need to load the serialized information // from ~/.config/micro/buffers if b.Path == "" { return nil } file, err := os.Open(filepath.Join(config.ConfigDir, "buffers", util.EscapePath(b.AbsPath))) if err == nil { defer file.Close() var buffer SerializedBuffer decoder := gob.NewDecoder(file) err = decoder.Decode(&buffer) if err != nil { return errors.New(err.Error() + "\nYou may want to remove the files in ~/.config/micro/buffers (these files\nstore the information for the 'saveundo' and 'savecursor' options) if\nthis problem persists.\nThis may be caused by upgrading to version 2.0, and removing the 'buffers'\ndirectory will reset the cursor and undo history and solve the problem.") } if b.Settings["savecursor"].(bool) { b.StartCursor = buffer.Cursor } if b.Settings["saveundo"].(bool) { // We should only use last time's eventhandler if the file wasn't modified by someone else in the meantime if b.ModTime == buffer.ModTime { b.EventHandler = buffer.EventHandler b.EventHandler.cursors = b.cursors b.EventHandler.buf = b.SharedBuffer } } } return nil } micro-2.0.14/internal/buffer/settings.go0000664000175000017510000000665714663411671017543 0ustar nileshnileshpackage buffer import ( "crypto/md5" "reflect" "github.com/zyedidia/micro/v2/internal/config" "github.com/zyedidia/micro/v2/internal/screen" ) func (b *Buffer) ReloadSettings(reloadFiletype bool) { settings := config.ParsedSettings() if _, ok := b.LocalSettings["filetype"]; !ok && reloadFiletype { // need to update filetype before updating other settings based on it b.Settings["filetype"] = "unknown" if v, ok := settings["filetype"]; ok { b.Settings["filetype"] = v } } // update syntax rules, which will also update filetype if needed b.UpdateRules() settings["filetype"] = b.Settings["filetype"] config.InitLocalSettings(settings, b.Path) for k, v := range config.DefaultCommonSettings() { if k == "filetype" { // prevent recursion continue } if _, ok := config.VolatileSettings[k]; ok { // reload should not override volatile settings continue } if _, ok := b.LocalSettings[k]; ok { // reload should not override local settings continue } if _, ok := settings[k]; ok { b.DoSetOptionNative(k, settings[k]) } else { b.DoSetOptionNative(k, v) } } } func (b *Buffer) DoSetOptionNative(option string, nativeValue interface{}) { if reflect.DeepEqual(b.Settings[option], nativeValue) { return } b.Settings[option] = nativeValue if option == "fastdirty" { if !nativeValue.(bool) { if b.Size() > LargeFileThreshold { b.Settings["fastdirty"] = true } else { if !b.isModified { calcHash(b, &b.origHash) } else { // prevent using an old stale origHash value b.origHash = [md5.Size]byte{} } } } } else if option == "statusline" { screen.Redraw() } else if option == "filetype" { b.ReloadSettings(false) } else if option == "fileformat" { switch b.Settings["fileformat"].(string) { case "unix": b.Endings = FFUnix case "dos": b.Endings = FFDos } b.isModified = true } else if option == "syntax" { if !nativeValue.(bool) { b.ClearMatches() } else { b.UpdateRules() } } else if option == "encoding" { b.isModified = true } else if option == "readonly" && b.Type.Kind == BTDefault.Kind { b.Type.Readonly = nativeValue.(bool) } else if option == "hlsearch" { for _, buf := range OpenBuffers { if b.SharedBuffer == buf.SharedBuffer { buf.HighlightSearch = nativeValue.(bool) } } } else { for _, pl := range config.Plugins { if option == pl.Name { if nativeValue.(bool) { if !pl.Loaded { pl.Load() } _, err := pl.Call("init") if err != nil && err != config.ErrNoSuchFunction { screen.TermMessage(err) } } else if !nativeValue.(bool) && pl.Loaded { _, err := pl.Call("deinit") if err != nil && err != config.ErrNoSuchFunction { screen.TermMessage(err) } } } } } if b.OptionCallback != nil { b.OptionCallback(option, nativeValue) } } func (b *Buffer) SetOptionNative(option string, nativeValue interface{}) error { if err := config.OptionIsValid(option, nativeValue); err != nil { return err } b.DoSetOptionNative(option, nativeValue) b.LocalSettings[option] = true return nil } // SetOption sets a given option to a value just for this buffer func (b *Buffer) SetOption(option, value string) error { if _, ok := b.Settings[option]; !ok { return config.ErrInvalidOption } nativeValue, err := config.GetNativeValue(option, b.Settings[option], value) if err != nil { return err } return b.SetOptionNative(option, nativeValue) } micro-2.0.14/internal/buffer/stack.go0000664000175000017510000000152114663411671016771 0ustar nileshnileshpackage buffer // TEStack is a simple implementation of a LIFO stack for text events type TEStack struct { Top *Element Size int } // An Element which is stored in the Stack type Element struct { Value *TextEvent Next *Element } // Len returns the stack's length func (s *TEStack) Len() int { return s.Size } // Push a new element onto the stack func (s *TEStack) Push(value *TextEvent) { s.Top = &Element{value, s.Top} s.Size++ } // Pop removes the top element from the stack and returns its value // If the stack is empty, return nil func (s *TEStack) Pop() (value *TextEvent) { if s.Size > 0 { value, s.Top = s.Top.Value, s.Top.Next s.Size-- return } return nil } // Peek returns the top element of the stack without removing it func (s *TEStack) Peek() *TextEvent { if s.Size > 0 { return s.Top.Value } return nil } micro-2.0.14/internal/buffer/stack_test.go0000664000175000017510000000116214663411671020031 0ustar nileshnileshpackage buffer import ( "testing" "time" "github.com/stretchr/testify/assert" ) func TestStack(t *testing.T) { s := new(TEStack) e1 := &TextEvent{ EventType: TextEventReplace, Time: time.Now(), } e2 := &TextEvent{ EventType: TextEventInsert, Time: time.Now(), } s.Push(e1) s.Push(e2) p := s.Peek() assert.Equal(t, p.EventType, TextEventInsert) p = s.Pop() assert.Equal(t, p.EventType, TextEventInsert) p = s.Peek() assert.Equal(t, p.EventType, TextEventReplace) p = s.Pop() assert.Equal(t, p.EventType, TextEventReplace) p = s.Pop() assert.Nil(t, p) p = s.Peek() assert.Nil(t, p) } micro-2.0.14/internal/clipboard/0000775000175000017510000000000014663411671016024 5ustar nileshnileshmicro-2.0.14/internal/clipboard/clipboard.go0000664000175000017510000001013514663411671020312 0ustar nileshnileshpackage clipboard import ( "errors" "github.com/zyedidia/clipper" ) type Method int const ( // External relies on external tools for accessing the clipboard // These include xclip, xsel, wl-clipboard for linux, pbcopy/pbpaste on Mac, // and Syscalls on Windows. External Method = iota // Terminal uses the terminal to manage the clipboard via OSC 52. Many // terminals do not support OSC 52, in which case this method won't work. Terminal // Internal just manages the clipboard with an internal buffer and doesn't // attempt to interface with the system clipboard Internal ) // CurrentMethod is the method used to store clipboard information var CurrentMethod Method = Internal // A Register is a buffer used to store text. The system clipboard has the 'clipboard' // and 'primary' (linux-only) registers, but other registers may be used internal to micro. type Register int const ( // ClipboardReg is the main system clipboard ClipboardReg Register = -1 // PrimaryReg is the system primary clipboard (linux only) PrimaryReg = -2 ) var clipboard clipper.Clipboard // Initialize attempts to initialize the clipboard using the given method func Initialize(m Method) error { var err error switch m { case External: clips := make([]clipper.Clipboard, 0, len(clipper.Clipboards)+1) clips = append(clips, &clipper.Custom{ Name: "micro-clip", }) clips = append(clips, clipper.Clipboards...) clipboard, err = clipper.GetClipboard(clips...) } if err != nil { CurrentMethod = Internal } return err } // SetMethod changes the clipboard access method func SetMethod(m string) Method { switch m { case "internal": CurrentMethod = Internal case "external": CurrentMethod = External case "terminal": CurrentMethod = Terminal } return CurrentMethod } // Read reads from a clipboard register func Read(r Register) (string, error) { return read(r, CurrentMethod) } // Write writes text to a clipboard register func Write(text string, r Register) error { return write(text, r, CurrentMethod) } // ReadMulti reads text from a clipboard register for a certain multi-cursor func ReadMulti(r Register, num, ncursors int) (string, error) { clip, err := Read(r) if err != nil { return "", err } if ValidMulti(r, clip, ncursors) { return multi.getText(r, num), nil } return clip, nil } // WriteMulti writes text to a clipboard register for a certain multi-cursor func WriteMulti(text string, r Register, num int, ncursors int) error { return writeMulti(text, r, num, ncursors, CurrentMethod) } // ValidMulti checks if the internal multi-clipboard is valid and up-to-date // with the system clipboard func ValidMulti(r Register, clip string, ncursors int) bool { return multi.isValid(r, clip, ncursors) } func writeMulti(text string, r Register, num int, ncursors int, m Method) error { multi.writeText(text, r, num, ncursors) return write(multi.getAllText(r), r, m) } func read(r Register, m Method) (string, error) { switch m { case External: switch r { case ClipboardReg: b, e := clipboard.ReadAll(clipper.RegClipboard) return string(b), e case PrimaryReg: b, e := clipboard.ReadAll(clipper.RegPrimary) return string(b), e default: return internal.read(r), nil } case Internal: return internal.read(r), nil case Terminal: switch r { case ClipboardReg: // terminal paste works by sending an esc sequence to the // terminal to trigger a paste event return terminal.read("clipboard") case PrimaryReg: return terminal.read("primary") default: return internal.read(r), nil } } return "", errors.New("Invalid clipboard method") } func write(text string, r Register, m Method) error { switch m { case External: switch r { case ClipboardReg: return clipboard.WriteAll(clipper.RegClipboard, []byte(text)) case PrimaryReg: return clipboard.WriteAll(clipper.RegPrimary, []byte(text)) default: internal.write(text, r) } case Internal: internal.write(text, r) case Terminal: switch r { case ClipboardReg: return terminal.write(text, "c") case PrimaryReg: return terminal.write(text, "p") default: internal.write(text, r) } } return nil } micro-2.0.14/internal/clipboard/internal.go0000664000175000017510000000044414663411671020171 0ustar nileshnileshpackage clipboard type internalClipboard map[Register]string var internal internalClipboard func init() { internal = make(internalClipboard) } func (c internalClipboard) read(r Register) string { return c[r] } func (c internalClipboard) write(text string, r Register) { c[r] = text } micro-2.0.14/internal/clipboard/multi.go0000664000175000017510000000244114663411671017506 0ustar nileshnileshpackage clipboard import ( "bytes" ) // For storing multi cursor clipboard contents type multiClipboard map[Register][]string var multi multiClipboard func (c multiClipboard) getAllText(r Register) string { content := c[r] if content == nil { return "" } buf := &bytes.Buffer{} for _, s := range content { buf.WriteString(s) } return buf.String() } func (c multiClipboard) getText(r Register, num int) string { content := c[r] if content == nil || len(content) <= num { return "" } return content[num] } // isValid checks if the text stored in this multi-clipboard is the same as the // text stored in the system clipboard (provided as an argument), and therefore // if it is safe to use the multi-clipboard for pasting instead of the system // clipboard. func (c multiClipboard) isValid(r Register, clipboard string, ncursors int) bool { content := c[r] if content == nil || len(content) != ncursors { return false } return clipboard == c.getAllText(r) } func (c multiClipboard) writeText(text string, r Register, num int, ncursors int) { content := c[r] if content == nil || len(content) != ncursors { content = make([]string, ncursors, ncursors) c[r] = content } if num >= ncursors { return } content[num] = text } func init() { multi = make(multiClipboard) } micro-2.0.14/internal/clipboard/terminal.go0000664000175000017510000000125214663411671020166 0ustar nileshnileshpackage clipboard import ( "errors" "time" "github.com/zyedidia/micro/v2/internal/screen" "github.com/zyedidia/tcell/v2" ) type terminalClipboard struct{} var terminal terminalClipboard func (t terminalClipboard) read(reg string) (string, error) { screen.Screen.GetClipboard(reg) // wait at most 200ms for response for { select { case event := <-screen.Events: e, ok := event.(*tcell.EventPaste) if ok { return e.Text(), nil } case <-time.After(200 * time.Millisecond): return "", errors.New("No clipboard received from terminal") } } } func (t terminalClipboard) write(text, reg string) error { return screen.Screen.SetClipboard(text, reg) } micro-2.0.14/internal/config/0000775000175000017510000000000014663411671015332 5ustar nileshnileshmicro-2.0.14/internal/config/autosave.go0000664000175000017510000000141314663411671017507 0ustar nileshnileshpackage config import ( "time" ) var Autosave chan bool var autotime chan float64 func init() { Autosave = make(chan bool) autotime = make(chan float64) } func SetAutoTime(a float64) { autotime <- a } func StartAutoSave() { go func() { var a float64 var t *time.Timer var elapsed <-chan time.Time for { select { case a = <-autotime: if t != nil { t.Stop() for len(elapsed) > 0 { <-elapsed } } if a > 0 { if t != nil { t.Reset(time.Duration(a * float64(time.Second))) } else { t = time.NewTimer(time.Duration(a * float64(time.Second))) elapsed = t.C } } case <-elapsed: if a > 0 { t.Reset(time.Duration(a * float64(time.Second))) Autosave <- true } } } }() } micro-2.0.14/internal/config/colorscheme.go0000664000175000017510000001533614663411671020174 0ustar nileshnileshpackage config import ( "errors" "regexp" "strconv" "strings" "github.com/zyedidia/tcell/v2" ) // DefStyle is Micro's default style var DefStyle tcell.Style = tcell.StyleDefault // Colorscheme is the current colorscheme var Colorscheme map[string]tcell.Style // GetColor takes in a syntax group and returns the colorscheme's style for that group func GetColor(color string) tcell.Style { st := DefStyle if color == "" { return st } groups := strings.Split(color, ".") if len(groups) > 1 { curGroup := "" for i, g := range groups { if i != 0 { curGroup += "." } curGroup += g if style, ok := Colorscheme[curGroup]; ok { st = style } } } else if style, ok := Colorscheme[color]; ok { st = style } else { st = StringToStyle(color) } return st } // ColorschemeExists checks if a given colorscheme exists func ColorschemeExists(colorschemeName string) bool { return FindRuntimeFile(RTColorscheme, colorschemeName) != nil } // InitColorscheme picks and initializes the colorscheme when micro starts func InitColorscheme() error { Colorscheme = make(map[string]tcell.Style) DefStyle = tcell.StyleDefault c, err := LoadDefaultColorscheme() if err == nil { Colorscheme = c } return err } // LoadDefaultColorscheme loads the default colorscheme from $(ConfigDir)/colorschemes func LoadDefaultColorscheme() (map[string]tcell.Style, error) { var parsedColorschemes []string return LoadColorscheme(GlobalSettings["colorscheme"].(string), &parsedColorschemes) } // LoadColorscheme loads the given colorscheme from a directory func LoadColorscheme(colorschemeName string, parsedColorschemes *[]string) (map[string]tcell.Style, error) { c := make(map[string]tcell.Style) file := FindRuntimeFile(RTColorscheme, colorschemeName) if file == nil { return c, errors.New(colorschemeName + " is not a valid colorscheme") } if data, err := file.Data(); err != nil { return c, errors.New("Error loading colorscheme: " + err.Error()) } else { var err error c, err = ParseColorscheme(file.Name(), string(data), parsedColorschemes) if err != nil { return c, err } } return c, nil } // ParseColorscheme parses the text definition for a colorscheme and returns the corresponding object // Colorschemes are made up of color-link statements linking a color group to a list of colors // For example, color-link keyword (blue,red) makes all keywords have a blue foreground and // red background func ParseColorscheme(name string, text string, parsedColorschemes *[]string) (map[string]tcell.Style, error) { var err error colorParser := regexp.MustCompile(`color-link\s+(\S*)\s+"(.*)"`) includeParser := regexp.MustCompile(`include\s+"(.*)"`) lines := strings.Split(text, "\n") c := make(map[string]tcell.Style) if parsedColorschemes != nil { *parsedColorschemes = append(*parsedColorschemes, name) } lineLoop: for _, line := range lines { if strings.TrimSpace(line) == "" || strings.TrimSpace(line)[0] == '#' { // Ignore this line continue } matches := includeParser.FindSubmatch([]byte(line)) if len(matches) == 2 { // support includes only in case parsedColorschemes are given if parsedColorschemes != nil { include := string(matches[1]) for _, name := range *parsedColorschemes { // check for circular includes... if name == include { // ...and prevent them continue lineLoop } } includeScheme, err := LoadColorscheme(include, parsedColorschemes) if err != nil { return c, err } for k, v := range includeScheme { c[k] = v } } continue } matches = colorParser.FindSubmatch([]byte(line)) if len(matches) == 3 { link := string(matches[1]) colors := string(matches[2]) style := StringToStyle(colors) c[link] = style if link == "default" { DefStyle = style } } else { err = errors.New("Color-link statement is not valid: " + line) } } return c, err } // StringToStyle returns a style from a string // The strings must be in the format "extra foregroundcolor,backgroundcolor" // The 'extra' can be bold, reverse, italic or underline func StringToStyle(str string) tcell.Style { var fg, bg string spaceSplit := strings.Split(str, " ") split := strings.Split(spaceSplit[len(spaceSplit)-1], ",") if len(split) > 1 { fg, bg = split[0], split[1] } else { fg = split[0] } fg = strings.TrimSpace(fg) bg = strings.TrimSpace(bg) var fgColor, bgColor tcell.Color var ok bool if fg == "" || fg == "default" { fgColor, _, _ = DefStyle.Decompose() } else { fgColor, ok = StringToColor(fg) if !ok { fgColor, _, _ = DefStyle.Decompose() } } if bg == "" || bg == "default" { _, bgColor, _ = DefStyle.Decompose() } else { bgColor, ok = StringToColor(bg) if !ok { _, bgColor, _ = DefStyle.Decompose() } } style := DefStyle.Foreground(fgColor).Background(bgColor) if strings.Contains(str, "bold") { style = style.Bold(true) } if strings.Contains(str, "italic") { style = style.Italic(true) } if strings.Contains(str, "reverse") { style = style.Reverse(true) } if strings.Contains(str, "underline") { style = style.Underline(true) } return style } // StringToColor returns a tcell color from a string representation of a color // We accept either bright... or light... to mean the brighter version of a color func StringToColor(str string) (tcell.Color, bool) { switch str { case "black": return tcell.ColorBlack, true case "red": return tcell.ColorMaroon, true case "green": return tcell.ColorGreen, true case "yellow": return tcell.ColorOlive, true case "blue": return tcell.ColorNavy, true case "magenta": return tcell.ColorPurple, true case "cyan": return tcell.ColorTeal, true case "white": return tcell.ColorSilver, true case "brightblack", "lightblack": return tcell.ColorGray, true case "brightred", "lightred": return tcell.ColorRed, true case "brightgreen", "lightgreen": return tcell.ColorLime, true case "brightyellow", "lightyellow": return tcell.ColorYellow, true case "brightblue", "lightblue": return tcell.ColorBlue, true case "brightmagenta", "lightmagenta": return tcell.ColorFuchsia, true case "brightcyan", "lightcyan": return tcell.ColorAqua, true case "brightwhite", "lightwhite": return tcell.ColorWhite, true case "default": return tcell.ColorDefault, true default: // Check if this is a 256 color if num, err := strconv.Atoi(str); err == nil { return GetColor256(num), true } // Check if this is a truecolor hex value if len(str) == 7 && str[0] == '#' { return tcell.GetColor(str), true } return tcell.ColorDefault, false } } // GetColor256 returns the tcell color for a number between 0 and 255 func GetColor256(color int) tcell.Color { if color == 0 { return tcell.ColorDefault } return tcell.PaletteColor(color) } micro-2.0.14/internal/config/colorscheme_test.go0000664000175000017510000000355414663411671021232 0ustar nileshnileshpackage config import ( "testing" "github.com/stretchr/testify/assert" "github.com/zyedidia/tcell/v2" ) func TestSimpleStringToStyle(t *testing.T) { s := StringToStyle("lightblue,magenta") fg, bg, _ := s.Decompose() assert.Equal(t, tcell.ColorBlue, fg) assert.Equal(t, tcell.ColorPurple, bg) } func TestAttributeStringToStyle(t *testing.T) { s := StringToStyle("bold cyan,brightcyan") fg, bg, attr := s.Decompose() assert.Equal(t, tcell.ColorTeal, fg) assert.Equal(t, tcell.ColorAqua, bg) assert.NotEqual(t, 0, attr&tcell.AttrBold) } func TestMultiAttributesStringToStyle(t *testing.T) { s := StringToStyle("bold italic underline cyan,brightcyan") fg, bg, attr := s.Decompose() assert.Equal(t, tcell.ColorTeal, fg) assert.Equal(t, tcell.ColorAqua, bg) assert.NotEqual(t, 0, attr&tcell.AttrBold) assert.NotEqual(t, 0, attr&tcell.AttrItalic) assert.NotEqual(t, 0, attr&tcell.AttrUnderline) } func TestColor256StringToStyle(t *testing.T) { s := StringToStyle("128,60") fg, bg, _ := s.Decompose() assert.Equal(t, tcell.Color128, fg) assert.Equal(t, tcell.Color60, bg) } func TestColorHexStringToStyle(t *testing.T) { s := StringToStyle("#deadbe,#ef1234") fg, bg, _ := s.Decompose() assert.Equal(t, tcell.NewRGBColor(222, 173, 190), fg) assert.Equal(t, tcell.NewRGBColor(239, 18, 52), bg) } func TestColorschemeParser(t *testing.T) { testColorscheme := `color-link default "#F8F8F2,#282828" color-link comment "#75715E,#282828" # comment color-link identifier "#66D9EF,#282828" #comment color-link constant "#AE81FF,#282828" color-link constant.string "#E6DB74,#282828" color-link constant.string.char "#BDE6AD,#282828"` c, err := ParseColorscheme("testColorscheme", testColorscheme, nil) assert.Nil(t, err) fg, bg, _ := c["comment"].Decompose() assert.Equal(t, tcell.NewRGBColor(117, 113, 94), fg) assert.Equal(t, tcell.NewRGBColor(40, 40, 40), bg) } micro-2.0.14/internal/config/config.go0000664000175000017510000000263114663411671017130 0ustar nileshnileshpackage config import ( "errors" "os" "path/filepath" homedir "github.com/mitchellh/go-homedir" ) var ConfigDir string // InitConfigDir finds the configuration directory for micro according to the XDG spec. // If no directory is found, it creates one. func InitConfigDir(flagConfigDir string) error { var e error microHome := os.Getenv("MICRO_CONFIG_HOME") if microHome == "" { // The user has not set $MICRO_CONFIG_HOME so we'll try $XDG_CONFIG_HOME xdgHome := os.Getenv("XDG_CONFIG_HOME") if xdgHome == "" { // The user has not set $XDG_CONFIG_HOME so we should act like it was set to ~/.config home, err := homedir.Dir() if err != nil { return errors.New("Error finding your home directory\nCan't load config files: " + err.Error()) } xdgHome = filepath.Join(home, ".config") } microHome = filepath.Join(xdgHome, "micro") } ConfigDir = microHome if len(flagConfigDir) > 0 { if _, err := os.Stat(flagConfigDir); os.IsNotExist(err) { e = errors.New("Error: " + flagConfigDir + " does not exist. Defaulting to " + ConfigDir + ".") } else { ConfigDir = flagConfigDir return nil } } // Create micro config home directory if it does not exist // This creates parent directories and does nothing if it already exists err := os.MkdirAll(ConfigDir, os.ModePerm) if err != nil { return errors.New("Error creating configuration directory: " + err.Error()) } return e } micro-2.0.14/internal/config/globals.go0000664000175000017510000000054114663411671017304 0ustar nileshnileshpackage config const ( DoubleClickThreshold = 400 // How many milliseconds to wait before a second click is not a double click ) var Bindings map[string]map[string]string func init() { Bindings = map[string]map[string]string{ "command": make(map[string]string), "buffer": make(map[string]string), "terminal": make(map[string]string), } } micro-2.0.14/internal/config/plugin.go0000664000175000017510000000706714663411671017171 0ustar nileshnileshpackage config import ( "errors" "log" lua "github.com/yuin/gopher-lua" ulua "github.com/zyedidia/micro/v2/internal/lua" ) // ErrNoSuchFunction is returned when Call is executed on a function that does not exist var ErrNoSuchFunction = errors.New("No such function exists") // LoadAllPlugins loads all detected plugins (in runtime/plugins and ConfigDir/plugins) func LoadAllPlugins() error { var reterr error for _, p := range Plugins { err := p.Load() if err != nil { reterr = err } } return reterr } // RunPluginFn runs a given function in all plugins // returns an error if any of the plugins had an error func RunPluginFn(fn string, args ...lua.LValue) error { var reterr error for _, p := range Plugins { if !p.IsLoaded() { continue } _, err := p.Call(fn, args...) if err != nil && err != ErrNoSuchFunction { reterr = errors.New("Plugin " + p.Name + ": " + err.Error()) } } return reterr } // RunPluginFnBool runs a function in all plugins and returns // false if any one of them returned false // also returns an error if any of the plugins had an error func RunPluginFnBool(settings map[string]interface{}, fn string, args ...lua.LValue) (bool, error) { var reterr error retbool := true for _, p := range Plugins { if !p.IsLoaded() || (settings != nil && settings[p.Name] == false) { continue } val, err := p.Call(fn, args...) if err == ErrNoSuchFunction { continue } if err != nil { reterr = errors.New("Plugin " + p.Name + ": " + err.Error()) continue } if v, ok := val.(lua.LBool); ok { retbool = retbool && bool(v) } } return retbool, reterr } // Plugin stores information about the source files/info for a plugin type Plugin struct { DirName string // name of plugin folder Name string // name of plugin Info *PluginInfo // json file containing info Srcs []RuntimeFile // lua files Loaded bool Default bool // pre-installed plugin } // IsLoaded returns if a plugin is enabled func (p *Plugin) IsLoaded() bool { if v, ok := GlobalSettings[p.Name]; ok { return v.(bool) && p.Loaded } return true } // Plugins is a list of all detected plugins (enabled or disabled) var Plugins []*Plugin // Load creates an option for the plugin and runs all source files func (p *Plugin) Load() error { if v, ok := GlobalSettings[p.Name]; ok && !v.(bool) { return nil } for _, f := range p.Srcs { dat, err := f.Data() if err != nil { return err } err = ulua.LoadFile(p.Name, f.Name(), dat) if err != nil { return err } } p.Loaded = true RegisterCommonOption(p.Name, true) return nil } // Call calls a given function in this plugin func (p *Plugin) Call(fn string, args ...lua.LValue) (lua.LValue, error) { plug := ulua.L.GetGlobal(p.Name) if plug == lua.LNil { log.Println("Plugin does not exist:", p.Name, "at", p.DirName, ":", p) return nil, nil } luafn := ulua.L.GetField(plug, fn) if luafn == lua.LNil { return nil, ErrNoSuchFunction } err := ulua.L.CallByParam(lua.P{ Fn: luafn, NRet: 1, Protect: true, }, args...) if err != nil { return nil, err } ret := ulua.L.Get(-1) ulua.L.Pop(1) return ret, nil } // FindPlugin returns the plugin with the given name func FindPlugin(name string) *Plugin { var pl *Plugin for _, p := range Plugins { if !p.IsLoaded() { continue } if p.Name == name { pl = p break } } return pl } // FindAnyPlugin does not require the plugin to be enabled func FindAnyPlugin(name string) *Plugin { var pl *Plugin for _, p := range Plugins { if p.Name == name { pl = p break } } return pl } micro-2.0.14/internal/config/plugin_installer.go0000664000175000017510000004310314663411671021235 0ustar nileshnileshpackage config import ( "archive/zip" "bytes" "fmt" "io" "io/ioutil" "net/http" "os" "path/filepath" "sort" "strings" "sync" "github.com/blang/semver" lua "github.com/yuin/gopher-lua" "github.com/zyedidia/json5" ulua "github.com/zyedidia/micro/v2/internal/lua" "github.com/zyedidia/micro/v2/internal/util" ) var ( allPluginPackages PluginPackages ) // CorePluginName is a plugin dependency name for the micro core. const CorePluginName = "micro" // PluginChannel contains an url to a json list of PluginRepository type PluginChannel string // PluginChannels is a slice of PluginChannel type PluginChannels []PluginChannel // PluginRepository contains an url to json file containing PluginPackages type PluginRepository string // PluginPackage contains the meta-data of a plugin and all available versions type PluginPackage struct { Name string Description string Author string Tags []string Versions PluginVersions } // PluginPackages is a list of PluginPackage instances. type PluginPackages []*PluginPackage // PluginVersion descripes a version of a PluginPackage. Containing a version, download url and also dependencies. type PluginVersion struct { pack *PluginPackage Version semver.Version Url string Require PluginDependencies } func (pv *PluginVersion) Pack() *PluginPackage { return pv.pack } // PluginVersions is a slice of PluginVersion type PluginVersions []*PluginVersion // PluginDependency descripes a dependency to another plugin or micro itself. type PluginDependency struct { Name string Range semver.Range } // PluginDependencies is a slice of PluginDependency type PluginDependencies []*PluginDependency func (pp *PluginPackage) String() string { buf := new(bytes.Buffer) buf.WriteString("Plugin: ") buf.WriteString(pp.Name) buf.WriteRune('\n') if pp.Author != "" { buf.WriteString("Author: ") buf.WriteString(pp.Author) buf.WriteRune('\n') } if pp.Description != "" { buf.WriteRune('\n') buf.WriteString(pp.Description) } return buf.String() } func fetchAllSources(count int, fetcher func(i int) PluginPackages) PluginPackages { wgQuery := new(sync.WaitGroup) wgQuery.Add(count) results := make(chan PluginPackages) wgDone := new(sync.WaitGroup) wgDone.Add(1) var packages PluginPackages for i := 0; i < count; i++ { go func(i int) { results <- fetcher(i) wgQuery.Done() }(i) } go func() { packages = make(PluginPackages, 0) for res := range results { packages = append(packages, res...) } wgDone.Done() }() wgQuery.Wait() close(results) wgDone.Wait() return packages } // Fetch retrieves all available PluginPackages from the given channels func (pc PluginChannels) Fetch(out io.Writer) PluginPackages { return fetchAllSources(len(pc), func(i int) PluginPackages { return pc[i].Fetch(out) }) } // Fetch retrieves all available PluginPackages from the given channel func (pc PluginChannel) Fetch(out io.Writer) PluginPackages { resp, err := http.Get(string(pc)) if err != nil { fmt.Fprintln(out, "Failed to query plugin channel:\n", err) return PluginPackages{} } defer resp.Body.Close() decoder := json5.NewDecoder(resp.Body) var repositories []PluginRepository if err := decoder.Decode(&repositories); err != nil { fmt.Fprintln(out, "Failed to decode channel data:\n", err) return PluginPackages{} } return fetchAllSources(len(repositories), func(i int) PluginPackages { return repositories[i].Fetch(out) }) } // Fetch retrieves all available PluginPackages from the given repository func (pr PluginRepository) Fetch(out io.Writer) PluginPackages { resp, err := http.Get(string(pr)) if err != nil { fmt.Fprintln(out, "Failed to query plugin repository:\n", err) return PluginPackages{} } defer resp.Body.Close() decoder := json5.NewDecoder(resp.Body) var plugins PluginPackages if err := decoder.Decode(&plugins); err != nil { fmt.Fprintln(out, "Failed to decode repository data:\n", err) return PluginPackages{} } if len(plugins) > 0 { return PluginPackages{plugins[0]} } return nil // return plugins } // UnmarshalJSON unmarshals raw json to a PluginVersion func (pv *PluginVersion) UnmarshalJSON(data []byte) error { var values struct { Version semver.Version Url string Require map[string]string } if err := json5.Unmarshal(data, &values); err != nil { return err } pv.Version = values.Version pv.Url = values.Url pv.Require = make(PluginDependencies, 0) for k, v := range values.Require { // don't add the dependency if it's the core and // we have a unknown version number. // in that case just accept that dependency (which equals to not adding it.) if k != CorePluginName || !isUnknownCoreVersion() { if vRange, err := semver.ParseRange(v); err == nil { pv.Require = append(pv.Require, &PluginDependency{k, vRange}) } } } return nil } // UnmarshalJSON unmarshals raw json to a PluginPackage func (pp *PluginPackage) UnmarshalJSON(data []byte) error { var values struct { Name string Description string Author string Tags []string Versions PluginVersions } if err := json5.Unmarshal(data, &values); err != nil { return err } pp.Name = values.Name pp.Description = values.Description pp.Author = values.Author pp.Tags = values.Tags pp.Versions = values.Versions for _, v := range pp.Versions { v.pack = pp } return nil } // GetAllPluginPackages gets all PluginPackages which may be available. func GetAllPluginPackages(out io.Writer) PluginPackages { if allPluginPackages == nil { getOption := func(name string) []string { data := GetGlobalOption(name) if strs, ok := data.([]string); ok { return strs } if ifs, ok := data.([]interface{}); ok { result := make([]string, len(ifs)) for i, urlIf := range ifs { if url, ok := urlIf.(string); ok { result[i] = url } else { return nil } } return result } return nil } channels := PluginChannels{} for _, url := range getOption("pluginchannels") { channels = append(channels, PluginChannel(url)) } repos := []PluginRepository{} for _, url := range getOption("pluginrepos") { repos = append(repos, PluginRepository(url)) } allPluginPackages = fetchAllSources(len(repos)+1, func(i int) PluginPackages { if i == 0 { return channels.Fetch(out) } return repos[i-1].Fetch(out) }) } return allPluginPackages } func (pv PluginVersions) find(ppName string) *PluginVersion { for _, v := range pv { if v.pack.Name == ppName { return v } } return nil } // Len returns the number of pluginversions in this slice func (pv PluginVersions) Len() int { return len(pv) } // Swap two entries of the slice func (pv PluginVersions) Swap(i, j int) { pv[i], pv[j] = pv[j], pv[i] } // Less returns true if the version at position i is greater then the version at position j (used for sorting) func (pv PluginVersions) Less(i, j int) bool { return pv[i].Version.GT(pv[j].Version) } // Match returns true if the package matches a given search text func (pp PluginPackage) Match(text string) bool { text = strings.ToLower(text) for _, t := range pp.Tags { if strings.ToLower(t) == text { return true } } if strings.Contains(strings.ToLower(pp.Name), text) { return true } if strings.Contains(strings.ToLower(pp.Description), text) { return true } return false } // IsInstallable returns true if the package can be installed. func (pp PluginPackage) IsInstallable(out io.Writer) error { _, err := GetAllPluginPackages(out).Resolve(GetInstalledVersions(true), PluginDependencies{ &PluginDependency{ Name: pp.Name, Range: semver.Range(func(v semver.Version) bool { return true }), }}) return err } // SearchPlugin retrieves a list of all PluginPackages which match the given search text and // could be or are already installed func SearchPlugin(out io.Writer, texts []string) (plugins PluginPackages) { plugins = make(PluginPackages, 0) pluginLoop: for _, pp := range GetAllPluginPackages(out) { for _, text := range texts { if !pp.Match(text) { continue pluginLoop } } if err := pp.IsInstallable(out); err == nil { plugins = append(plugins, pp) } } return } func isUnknownCoreVersion() bool { _, err := semver.ParseTolerant(util.Version) return err != nil } func newStaticPluginVersion(name, version string) *PluginVersion { vers, err := semver.ParseTolerant(version) if err != nil { if vers, err = semver.ParseTolerant("0.0.0-" + version); err != nil { vers = semver.MustParse("0.0.0-unknown") } } pl := &PluginPackage{ Name: name, } pv := &PluginVersion{ pack: pl, Version: vers, } pl.Versions = PluginVersions{pv} return pv } // GetInstalledVersions returns a list of all currently installed plugins including an entry for // micro itself. This can be used to resolve dependencies. func GetInstalledVersions(withCore bool) PluginVersions { result := PluginVersions{} if withCore { result = append(result, newStaticPluginVersion(CorePluginName, util.Version)) } for _, p := range Plugins { if !p.IsLoaded() { continue } version := GetInstalledPluginVersion(p.Name) if pv := newStaticPluginVersion(p.Name, version); pv != nil { result = append(result, pv) } } return result } // GetInstalledPluginVersion returns the string of the exported VERSION variable of a loaded plugin func GetInstalledPluginVersion(name string) string { plugin := ulua.L.GetGlobal(name) if plugin != lua.LNil { version := ulua.L.GetField(plugin, "VERSION") if str, ok := version.(lua.LString); ok { return string(str) } } return "" } // DownloadAndInstall downloads and installs the given plugin and version func (pv *PluginVersion) DownloadAndInstall(out io.Writer) error { fmt.Fprintf(out, "Downloading %q (%s) from %q\n", pv.pack.Name, pv.Version, pv.Url) resp, err := http.Get(pv.Url) if err != nil { return err } defer resp.Body.Close() data, err := ioutil.ReadAll(resp.Body) if err != nil { return err } zipbuf := bytes.NewReader(data) z, err := zip.NewReader(zipbuf, zipbuf.Size()) if err != nil { return err } targetDir := filepath.Join(ConfigDir, "plug", pv.pack.Name) dirPerm := os.FileMode(0755) if err = os.MkdirAll(targetDir, dirPerm); err != nil { return err } // Check if all files in zip are in the same directory. // this might be the case if the plugin zip contains the whole plugin dir // instead of its content. var prefix string allPrefixed := false for i, f := range z.File { parts := strings.Split(f.Name, "/") if i == 0 { prefix = parts[0] } else if parts[0] != prefix { allPrefixed = false break } else { // switch to true since we have at least a second file allPrefixed = true } } // Install files and directory's for _, f := range z.File { parts := strings.Split(f.Name, "/") if allPrefixed { parts = parts[1:] } targetName := filepath.Join(targetDir, filepath.Join(parts...)) if f.FileInfo().IsDir() { if err := os.MkdirAll(targetName, dirPerm); err != nil { return err } } else { basepath := filepath.Dir(targetName) if err := os.MkdirAll(basepath, dirPerm); err != nil { return err } content, err := f.Open() if err != nil { return err } defer content.Close() target, err := os.Create(targetName) if err != nil { return err } defer target.Close() if _, err = io.Copy(target, content); err != nil { return err } } } return nil } func (pl PluginPackages) Get(name string) *PluginPackage { for _, p := range pl { if p.Name == name { return p } } return nil } func (pl PluginPackages) GetAllVersions(name string) PluginVersions { result := make(PluginVersions, 0) p := pl.Get(name) if p != nil { result = append(result, p.Versions...) } return result } func (req PluginDependencies) Join(other PluginDependencies) PluginDependencies { m := make(map[string]*PluginDependency) for _, r := range req { m[r.Name] = r } for _, o := range other { cur, ok := m[o.Name] if ok { m[o.Name] = &PluginDependency{ o.Name, o.Range.AND(cur.Range), } } else { m[o.Name] = o } } result := make(PluginDependencies, 0, len(m)) for _, v := range m { result = append(result, v) } return result } // Resolve resolves dependencies between different plugins func (all PluginPackages) Resolve(selectedVersions PluginVersions, open PluginDependencies) (PluginVersions, error) { if len(open) == 0 { return selectedVersions, nil } currentRequirement, stillOpen := open[0], open[1:] if currentRequirement != nil { if selVersion := selectedVersions.find(currentRequirement.Name); selVersion != nil { if currentRequirement.Range(selVersion.Version) { return all.Resolve(selectedVersions, stillOpen) } return nil, fmt.Errorf("unable to find a matching version for \"%s\"", currentRequirement.Name) } availableVersions := all.GetAllVersions(currentRequirement.Name) sort.Sort(availableVersions) for _, version := range availableVersions { if currentRequirement.Range(version.Version) { resolved, err := all.Resolve(append(selectedVersions, version), stillOpen.Join(version.Require)) if err == nil { return resolved, nil } } } return nil, fmt.Errorf("unable to find a matching version for \"%s\"", currentRequirement.Name) } return selectedVersions, nil } func (pv PluginVersions) install(out io.Writer) { anyInstalled := false currentlyInstalled := GetInstalledVersions(true) for _, sel := range pv { if sel.pack.Name != CorePluginName { shouldInstall := true if pv := currentlyInstalled.find(sel.pack.Name); pv != nil { if pv.Version.NE(sel.Version) { fmt.Fprintln(out, "Uninstalling", sel.pack.Name) UninstallPlugin(out, sel.pack.Name) } else { shouldInstall = false } } if shouldInstall { if err := sel.DownloadAndInstall(out); err != nil { fmt.Fprintln(out, err) return } anyInstalled = true } } } if anyInstalled { fmt.Fprintln(out, "One or more plugins installed.") } else { fmt.Fprintln(out, "Nothing to install / update") } } // UninstallPlugin deletes the plugin folder of the given plugin func UninstallPlugin(out io.Writer, name string) { for _, p := range Plugins { if !p.IsLoaded() { continue } if p.Name == name { p.Loaded = false if err := os.RemoveAll(filepath.Join(ConfigDir, "plug", p.DirName)); err != nil { fmt.Fprintln(out, err) return } break } } } // Install installs the plugin func (pl PluginPackage) Install(out io.Writer) { selected, err := GetAllPluginPackages(out).Resolve(GetInstalledVersions(true), PluginDependencies{ &PluginDependency{ Name: pl.Name, Range: semver.Range(func(v semver.Version) bool { return true }), }}) if err != nil { fmt.Fprintln(out, err) return } selected.install(out) } // UpdatePlugins updates the given plugins func UpdatePlugins(out io.Writer, plugins []string) { // if no plugins are specified, update all installed plugins. if len(plugins) == 0 { for _, p := range Plugins { if !p.IsLoaded() || p.Default { continue } plugins = append(plugins, p.Name) } } fmt.Fprintln(out, "Checking for plugin updates") microVersion := PluginVersions{ newStaticPluginVersion(CorePluginName, util.Version), } var updates = make(PluginDependencies, 0) for _, name := range plugins { pv := GetInstalledPluginVersion(name) r, err := semver.ParseRange(">=" + pv) // Try to get newer versions. if err == nil { updates = append(updates, &PluginDependency{ Name: name, Range: r, }) } } selected, err := GetAllPluginPackages(out).Resolve(microVersion, updates) if err != nil { fmt.Fprintln(out, err) return } selected.install(out) } func PluginCommand(out io.Writer, cmd string, args []string) { switch cmd { case "install": installedVersions := GetInstalledVersions(false) for _, plugin := range args { pp := GetAllPluginPackages(out).Get(plugin) if pp == nil { fmt.Fprintln(out, "Unknown plugin \""+plugin+"\"") } else if err := pp.IsInstallable(out); err != nil { fmt.Fprintln(out, "Error installing ", plugin, ": ", err) } else { for _, installed := range installedVersions { if pp.Name == installed.Pack().Name { if pp.Versions[0].Version.Compare(installed.Version) == 1 { fmt.Fprintln(out, pp.Name, " is already installed but out-of-date: use 'plugin update ", pp.Name, "' to update") } else { fmt.Fprintln(out, pp.Name, " is already installed") } } } pp.Install(out) } } case "remove": removed := "" for _, plugin := range args { // check if the plugin exists. for _, p := range Plugins { if p.Name == plugin && p.Default { fmt.Fprintln(out, "Default plugins cannot be removed, but can be disabled via settings.") continue } if p.Name == plugin { UninstallPlugin(out, plugin) removed += plugin + " " continue } } } if removed != "" { fmt.Fprintln(out, "Removed ", removed) } else { fmt.Fprintln(out, "No plugins removed") } case "update": UpdatePlugins(out, args) case "list": plugins := GetInstalledVersions(false) fmt.Fprintln(out, "The following plugins are currently installed:") for _, p := range plugins { fmt.Fprintf(out, "%s (%s)\n", p.Pack().Name, p.Version) } case "search": plugins := SearchPlugin(out, args) fmt.Fprintln(out, len(plugins), " plugins found") for _, p := range plugins { fmt.Fprintln(out, "----------------") fmt.Fprintln(out, p.String()) } fmt.Fprintln(out, "----------------") case "available": packages := GetAllPluginPackages(out) fmt.Fprintln(out, "Available Plugins:") for _, pkg := range packages { fmt.Fprintln(out, pkg.Name) } default: fmt.Fprintln(out, "Invalid plugin command") } } micro-2.0.14/internal/config/plugin_installer_test.go0000664000175000017510000000241314663411671022273 0ustar nileshnileshpackage config import ( "testing" "github.com/blang/semver" "github.com/zyedidia/json5" ) func TestDependencyResolving(t *testing.T) { js := ` [{ "Name": "Foo", "Versions": [{ "Version": "1.0.0" }, { "Version": "1.5.0" },{ "Version": "2.0.0" }] }, { "Name": "Bar", "Versions": [{ "Version": "1.0.0", "Require": {"Foo": ">1.0.0 <2.0.0"} }] }, { "Name": "Unresolvable", "Versions": [{ "Version": "1.0.0", "Require": {"Foo": "<=1.0.0", "Bar": ">0.0.0"} }] }] ` var all PluginPackages err := json5.Unmarshal([]byte(js), &all) if err != nil { t.Error(err) } selected, err := all.Resolve(PluginVersions{}, PluginDependencies{ &PluginDependency{"Bar", semver.MustParseRange(">=1.0.0")}, }) check := func(name, version string) { v := selected.find(name) expected := semver.MustParse(version) if v == nil { t.Errorf("Failed to resolve %s", name) } else if expected.NE(v.Version) { t.Errorf("%s resolved in wrong version %v", name, v) } } if err != nil { t.Error(err) } else { check("Foo", "1.5.0") check("Bar", "1.0.0") } selected, err = all.Resolve(PluginVersions{}, PluginDependencies{ &PluginDependency{"Unresolvable", semver.MustParseRange(">0.0.0")}, }) if err == nil { t.Error("Unresolvable package resolved:", selected) } } micro-2.0.14/internal/config/plugin_manager.go0000664000175000017510000000255714663411671020662 0ustar nileshnileshpackage config import ( "bytes" "encoding/json" "errors" ) var ( ErrMissingName = errors.New("Missing or empty name field") ErrMissingDesc = errors.New("Missing or empty description field") ErrMissingSite = errors.New("Missing or empty website field") ) // PluginInfo contains all the needed info about a plugin // The info is just strings and are not used beyond that (except // the Site and Install fields should be valid URLs). This means // that the requirements for example can be formatted however the // plugin maker decides, the fields will only be parsed by humans // Name: name of plugin // Desc: description of plugin // Site: home website of plugin // Install: install link for plugin (can be link to repo or zip file) // Vstr: version // Require: list of dependencies and requirements type PluginInfo struct { Name string `json:"Name"` Desc string `json:"Description"` Site string `json:"Website"` } // NewPluginInfo parses a JSON input into a valid PluginInfo struct // Returns an error if there are any missing fields or any invalid fields // There are no optional fields in a plugin info json file func NewPluginInfo(data []byte) (*PluginInfo, error) { var info []PluginInfo dec := json.NewDecoder(bytes.NewReader(data)) // dec.DisallowUnknownFields() // Force errors if err := dec.Decode(&info); err != nil { return nil, err } return &info[0], nil } micro-2.0.14/internal/config/rtfiles.go0000664000175000017510000002167114663411671017340 0ustar nileshnileshpackage config import ( "errors" "io/ioutil" "log" "os" "path" "path/filepath" "regexp" "strings" rt "github.com/zyedidia/micro/v2/runtime" ) const ( RTColorscheme = 0 RTSyntax = 1 RTHelp = 2 RTPlugin = 3 RTSyntaxHeader = 4 ) var ( NumTypes = 5 // How many filetypes are there ) type RTFiletype int // RuntimeFile allows the program to read runtime data like colorschemes or syntax files type RuntimeFile interface { // Name returns a name of the file without paths or extensions Name() string // Data returns the content of the file. Data() ([]byte, error) } // allFiles contains all available files, mapped by filetype var allFiles [][]RuntimeFile var realFiles [][]RuntimeFile func init() { initRuntimeVars() } func initRuntimeVars() { allFiles = make([][]RuntimeFile, NumTypes) realFiles = make([][]RuntimeFile, NumTypes) } // NewRTFiletype creates a new RTFiletype func NewRTFiletype() int { NumTypes++ allFiles = append(allFiles, []RuntimeFile{}) realFiles = append(realFiles, []RuntimeFile{}) return NumTypes - 1 } // some file on filesystem type realFile string // some asset file type assetFile string // some file on filesystem but with a different name type namedFile struct { realFile name string } // a file with the data stored in memory type memoryFile struct { name string data []byte } func (mf memoryFile) Name() string { return mf.name } func (mf memoryFile) Data() ([]byte, error) { return mf.data, nil } func (rf realFile) Name() string { fn := filepath.Base(string(rf)) return fn[:len(fn)-len(filepath.Ext(fn))] } func (rf realFile) Data() ([]byte, error) { return ioutil.ReadFile(string(rf)) } func (af assetFile) Name() string { fn := path.Base(string(af)) return fn[:len(fn)-len(path.Ext(fn))] } func (af assetFile) Data() ([]byte, error) { return rt.Asset(string(af)) } func (nf namedFile) Name() string { return nf.name } // AddRuntimeFile registers a file for the given filetype func AddRuntimeFile(fileType RTFiletype, file RuntimeFile) { allFiles[fileType] = append(allFiles[fileType], file) } // AddRealRuntimeFile registers a file for the given filetype func AddRealRuntimeFile(fileType RTFiletype, file RuntimeFile) { allFiles[fileType] = append(allFiles[fileType], file) realFiles[fileType] = append(realFiles[fileType], file) } // AddRuntimeFilesFromDirectory registers each file from the given directory for // the filetype which matches the file-pattern func AddRuntimeFilesFromDirectory(fileType RTFiletype, directory, pattern string) { files, _ := ioutil.ReadDir(directory) for _, f := range files { if ok, _ := filepath.Match(pattern, f.Name()); !f.IsDir() && ok { fullPath := filepath.Join(directory, f.Name()) AddRealRuntimeFile(fileType, realFile(fullPath)) } } } // AddRuntimeFilesFromAssets registers each file from the given asset-directory for // the filetype which matches the file-pattern func AddRuntimeFilesFromAssets(fileType RTFiletype, directory, pattern string) { files, err := rt.AssetDir(directory) if err != nil { return } assetLoop: for _, f := range files { if ok, _ := path.Match(pattern, f); ok { af := assetFile(path.Join(directory, f)) for _, rf := range realFiles[fileType] { if af.Name() == rf.Name() { continue assetLoop } } AddRuntimeFile(fileType, af) } } } // FindRuntimeFile finds a runtime file of the given filetype and name // will return nil if no file was found func FindRuntimeFile(fileType RTFiletype, name string) RuntimeFile { for _, f := range ListRuntimeFiles(fileType) { if f.Name() == name { return f } } return nil } // ListRuntimeFiles lists all known runtime files for the given filetype func ListRuntimeFiles(fileType RTFiletype) []RuntimeFile { return allFiles[fileType] } // ListRealRuntimeFiles lists all real runtime files (on disk) for a filetype // these runtime files will be ones defined by the user and loaded from the config directory func ListRealRuntimeFiles(fileType RTFiletype) []RuntimeFile { return realFiles[fileType] } // InitRuntimeFiles initializes all assets files and the config directory. // If `user` is false, InitRuntimeFiles ignores the config directory and // initializes asset files only. func InitRuntimeFiles(user bool) { add := func(fileType RTFiletype, dir, pattern string) { if user { AddRuntimeFilesFromDirectory(fileType, filepath.Join(ConfigDir, dir), pattern) } AddRuntimeFilesFromAssets(fileType, path.Join("runtime", dir), pattern) } initRuntimeVars() add(RTColorscheme, "colorschemes", "*.micro") add(RTSyntax, "syntax", "*.yaml") add(RTSyntaxHeader, "syntax", "*.hdr") add(RTHelp, "help", "*.md") } // InitPlugins initializes the plugins func InitPlugins() { Plugins = Plugins[:0] initlua := filepath.Join(ConfigDir, "init.lua") if _, err := os.Stat(initlua); !os.IsNotExist(err) { p := new(Plugin) p.Name = "initlua" p.DirName = "initlua" p.Srcs = append(p.Srcs, realFile(initlua)) Plugins = append(Plugins, p) } // Search ConfigDir for plugin-scripts plugdir := filepath.Join(ConfigDir, "plug") files, _ := ioutil.ReadDir(plugdir) isID := regexp.MustCompile(`^[_A-Za-z0-9]+$`).MatchString for _, d := range files { plugpath := filepath.Join(plugdir, d.Name()) if stat, err := os.Stat(plugpath); err == nil && stat.IsDir() { srcs, _ := ioutil.ReadDir(plugpath) p := new(Plugin) p.Name = d.Name() p.DirName = d.Name() for _, f := range srcs { if strings.HasSuffix(f.Name(), ".lua") { p.Srcs = append(p.Srcs, realFile(filepath.Join(plugdir, d.Name(), f.Name()))) } else if strings.HasSuffix(f.Name(), ".json") { data, err := ioutil.ReadFile(filepath.Join(plugdir, d.Name(), f.Name())) if err != nil { continue } p.Info, err = NewPluginInfo(data) if err != nil { continue } p.Name = p.Info.Name } } if !isID(p.Name) || len(p.Srcs) <= 0 { log.Println(p.Name, "is not a plugin") continue } Plugins = append(Plugins, p) } } plugdir = filepath.Join("runtime", "plugins") if files, err := rt.AssetDir(plugdir); err == nil { outer: for _, d := range files { for _, p := range Plugins { if p.Name == d { log.Println(p.Name, "built-in plugin overridden by user-defined one") continue outer } } if srcs, err := rt.AssetDir(filepath.Join(plugdir, d)); err == nil { p := new(Plugin) p.Name = d p.DirName = d p.Default = true for _, f := range srcs { if strings.HasSuffix(f, ".lua") { p.Srcs = append(p.Srcs, assetFile(filepath.Join(plugdir, d, f))) } else if strings.HasSuffix(f, ".json") { data, err := rt.Asset(filepath.Join(plugdir, d, f)) if err != nil { continue } p.Info, err = NewPluginInfo(data) if err != nil { continue } p.Name = p.Info.Name } } if !isID(p.Name) || len(p.Srcs) <= 0 { log.Println(p.Name, "is not a plugin") continue } Plugins = append(Plugins, p) } } } } // PluginReadRuntimeFile allows plugin scripts to read the content of a runtime file func PluginReadRuntimeFile(fileType RTFiletype, name string) string { if file := FindRuntimeFile(fileType, name); file != nil { if data, err := file.Data(); err == nil { return string(data) } } return "" } // PluginListRuntimeFiles allows plugins to lists all runtime files of the given type func PluginListRuntimeFiles(fileType RTFiletype) []string { files := ListRuntimeFiles(fileType) result := make([]string, len(files)) for i, f := range files { result[i] = f.Name() } return result } // PluginAddRuntimeFile adds a file to the runtime files for a plugin func PluginAddRuntimeFile(plugin string, filetype RTFiletype, filePath string) error { pl := FindPlugin(plugin) if pl == nil { return errors.New("Plugin " + plugin + " does not exist") } pldir := pl.DirName fullpath := filepath.Join(ConfigDir, "plug", pldir, filePath) if _, err := os.Stat(fullpath); err == nil { AddRealRuntimeFile(filetype, realFile(fullpath)) } else { fullpath = path.Join("runtime", "plugins", pldir, filePath) AddRuntimeFile(filetype, assetFile(fullpath)) } return nil } // PluginAddRuntimeFilesFromDirectory adds files from a directory to the runtime files for a plugin func PluginAddRuntimeFilesFromDirectory(plugin string, filetype RTFiletype, directory, pattern string) error { pl := FindPlugin(plugin) if pl == nil { return errors.New("Plugin " + plugin + " does not exist") } pldir := pl.DirName fullpath := filepath.Join(ConfigDir, "plug", pldir, directory) if _, err := os.Stat(fullpath); err == nil { AddRuntimeFilesFromDirectory(filetype, fullpath, pattern) } else { fullpath = path.Join("runtime", "plugins", pldir, directory) AddRuntimeFilesFromAssets(filetype, fullpath, pattern) } return nil } // PluginAddRuntimeFileFromMemory adds a file to the runtime files for a plugin from a given string func PluginAddRuntimeFileFromMemory(filetype RTFiletype, filename, data string) { AddRealRuntimeFile(filetype, memoryFile{filename, []byte(data)}) } micro-2.0.14/internal/config/rtfiles_test.go0000664000175000017510000000172514663411671020375 0ustar nileshnileshpackage config import ( "testing" "github.com/stretchr/testify/assert" ) func init() { InitRuntimeFiles(false) } func TestAddFile(t *testing.T) { AddRuntimeFile(RTPlugin, memoryFile{"foo.lua", []byte("hello world\n")}) AddRuntimeFile(RTSyntax, memoryFile{"bar", []byte("some syntax file\n")}) f1 := FindRuntimeFile(RTPlugin, "foo.lua") assert.NotNil(t, f1) assert.Equal(t, "foo.lua", f1.Name()) data, err := f1.Data() assert.Nil(t, err) assert.Equal(t, []byte("hello world\n"), data) f2 := FindRuntimeFile(RTSyntax, "bar") assert.NotNil(t, f2) assert.Equal(t, "bar", f2.Name()) data, err = f2.Data() assert.Nil(t, err) assert.Equal(t, []byte("some syntax file\n"), data) } func TestFindFile(t *testing.T) { f := FindRuntimeFile(RTSyntax, "go") assert.NotNil(t, f) assert.Equal(t, "go", f.Name()) data, err := f.Data() assert.Nil(t, err) assert.Equal(t, []byte("filetype: go"), data[:12]) e := FindRuntimeFile(RTSyntax, "foobar") assert.Nil(t, e) } micro-2.0.14/internal/config/settings.go0000664000175000017510000003467514663411671017540 0ustar nileshnileshpackage config import ( "encoding/json" "errors" "fmt" "io/ioutil" "os" "path/filepath" "reflect" "runtime" "strconv" "strings" "github.com/zyedidia/glob" "github.com/zyedidia/json5" "github.com/zyedidia/micro/v2/internal/util" "golang.org/x/text/encoding/htmlindex" ) type optionValidator func(string, interface{}) error // a list of settings that need option validators var optionValidators = map[string]optionValidator{ "autosave": validateNonNegativeValue, "clipboard": validateChoice, "colorcolumn": validateNonNegativeValue, "colorscheme": validateColorscheme, "detectlimit": validateNonNegativeValue, "encoding": validateEncoding, "fileformat": validateChoice, "matchbracestyle": validateChoice, "multiopen": validateChoice, "reload": validateChoice, "scrollmargin": validateNonNegativeValue, "scrollspeed": validateNonNegativeValue, "tabsize": validatePositiveValue, } // a list of settings with pre-defined choices var OptionChoices = map[string][]string{ "clipboard": {"internal", "external", "terminal"}, "fileformat": {"unix", "dos"}, "matchbracestyle": {"underline", "highlight"}, "multiopen": {"tab", "hsplit", "vsplit"}, "reload": {"prompt", "auto", "disabled"}, } // a list of settings that can be globally and locally modified and their // default values var defaultCommonSettings = map[string]interface{}{ "autoindent": true, "autosu": false, "backup": true, "backupdir": "", "basename": false, "colorcolumn": float64(0), "cursorline": true, "detectlimit": float64(100), "diffgutter": false, "encoding": "utf-8", "eofnewline": true, "fastdirty": false, "fileformat": defaultFileFormat(), "filetype": "unknown", "hlsearch": false, "hltaberrors": false, "hltrailingws": false, "incsearch": true, "ignorecase": true, "indentchar": " ", "keepautoindent": false, "matchbrace": true, "matchbraceleft": true, "matchbracestyle": "underline", "mkparents": false, "permbackup": false, "readonly": false, "reload": "prompt", "rmtrailingws": false, "ruler": true, "relativeruler": false, "savecursor": false, "saveundo": false, "scrollbar": false, "scrollmargin": float64(3), "scrollspeed": float64(2), "smartpaste": true, "softwrap": false, "splitbottom": true, "splitright": true, "statusformatl": "$(filename) $(modified)($(line),$(col)) $(status.paste)| ft:$(opt:filetype) | $(opt:fileformat) | $(opt:encoding)", "statusformatr": "$(bind:ToggleKeyMenu): bindings, $(bind:ToggleHelp): help", "statusline": true, "syntax": true, "tabmovement": false, "tabsize": float64(4), "tabstospaces": false, "useprimary": true, "wordwrap": false, } // a list of settings that should only be globally modified and their // default values var DefaultGlobalOnlySettings = map[string]interface{}{ "autosave": float64(0), "clipboard": "external", "colorscheme": "default", "divchars": "|-", "divreverse": true, "fakecursor": false, "infobar": true, "keymenu": false, "mouse": true, "multiopen": "tab", "parsecursor": false, "paste": false, "pluginchannels": []string{"https://raw.githubusercontent.com/micro-editor/plugin-channel/master/channel.json"}, "pluginrepos": []string{}, "savehistory": true, "scrollbarchar": "|", "sucmd": "sudo", "tabhighlight": false, "tabreverse": true, "xterm": false, } // a list of settings that should never be globally modified var LocalSettings = []string{ "filetype", "readonly", } var ( ErrInvalidOption = errors.New("Invalid option") ErrInvalidValue = errors.New("Invalid value") // The options that the user can set GlobalSettings map[string]interface{} // This is the raw parsed json parsedSettings map[string]interface{} settingsParseError bool // ModifiedSettings is a map of settings which should be written to disk // because they have been modified by the user in this session ModifiedSettings map[string]bool // VolatileSettings is a map of settings which should not be written to disk // because they have been temporarily set for this session only VolatileSettings map[string]bool ) func init() { ModifiedSettings = make(map[string]bool) VolatileSettings = make(map[string]bool) } func validateParsedSettings() error { var err error defaults := DefaultAllSettings() for k, v := range parsedSettings { if strings.HasPrefix(reflect.TypeOf(v).String(), "map") { if strings.HasPrefix(k, "ft:") { for k1, v1 := range v.(map[string]interface{}) { if _, ok := defaults[k1]; ok { if e := verifySetting(k1, v1, defaults[k1]); e != nil { err = e parsedSettings[k].(map[string]interface{})[k1] = defaults[k1] continue } } } } else { if _, e := glob.Compile(k); e != nil { err = errors.New("Error with glob setting " + k + ": " + e.Error()) delete(parsedSettings, k) continue } for k1, v1 := range v.(map[string]interface{}) { if _, ok := defaults[k1]; ok { if e := verifySetting(k1, v1, defaults[k1]); e != nil { err = e parsedSettings[k].(map[string]interface{})[k1] = defaults[k1] continue } } } } continue } if k == "autosave" { // if autosave is a boolean convert it to float s, ok := v.(bool) if ok { if s { parsedSettings["autosave"] = 8.0 } else { parsedSettings["autosave"] = 0.0 } } continue } if _, ok := defaults[k]; ok { if e := verifySetting(k, v, defaults[k]); e != nil { err = e parsedSettings[k] = defaults[k] continue } } } return err } func ReadSettings() error { parsedSettings = make(map[string]interface{}) filename := filepath.Join(ConfigDir, "settings.json") if _, e := os.Stat(filename); e == nil { input, err := ioutil.ReadFile(filename) if err != nil { settingsParseError = true return errors.New("Error reading settings.json file: " + err.Error()) } if !strings.HasPrefix(string(input), "null") { // Unmarshal the input into the parsed map err = json5.Unmarshal(input, &parsedSettings) if err != nil { settingsParseError = true return errors.New("Error reading settings.json: " + err.Error()) } err = validateParsedSettings() if err != nil { return err } } } return nil } func ParsedSettings() map[string]interface{} { s := make(map[string]interface{}) for k, v := range parsedSettings { s[k] = v } return s } func verifySetting(option string, value interface{}, def interface{}) error { var interfaceArr []interface{} valType := reflect.TypeOf(value) defType := reflect.TypeOf(def) assignable := false switch option { case "pluginrepos", "pluginchannels": assignable = valType.AssignableTo(reflect.TypeOf(interfaceArr)) default: assignable = defType.AssignableTo(valType) } if !assignable { return fmt.Errorf("Error: setting '%s' has incorrect type (%s), using default value: %v (%s)", option, valType, def, defType) } if err := OptionIsValid(option, value); err != nil { return err } return nil } // InitGlobalSettings initializes the options map and sets all options to their default values // Must be called after ReadSettings func InitGlobalSettings() error { var err error GlobalSettings = DefaultAllSettings() for k, v := range parsedSettings { if !strings.HasPrefix(reflect.TypeOf(v).String(), "map") { GlobalSettings[k] = v } } return err } // InitLocalSettings scans the json in settings.json and sets the options locally based // on whether the filetype or path matches ft or glob local settings // Must be called after ReadSettings func InitLocalSettings(settings map[string]interface{}, path string) { for k, v := range parsedSettings { if strings.HasPrefix(reflect.TypeOf(v).String(), "map") { if strings.HasPrefix(k, "ft:") { if settings["filetype"].(string) == k[3:] { for k1, v1 := range v.(map[string]interface{}) { settings[k1] = v1 } } } else { g, _ := glob.Compile(k) if g.MatchString(path) { for k1, v1 := range v.(map[string]interface{}) { settings[k1] = v1 } } } } } } // WriteSettings writes the settings to the specified filename as JSON func WriteSettings(filename string) error { if settingsParseError { // Don't write settings if there was a parse error // because this will delete the settings.json if it // is invalid. Instead we should allow the user to fix // it manually. return nil } var err error if _, e := os.Stat(ConfigDir); e == nil { defaults := DefaultAllSettings() // remove any options froms parsedSettings that have since been marked as default for k, v := range parsedSettings { if !strings.HasPrefix(reflect.TypeOf(v).String(), "map") { cur, okcur := GlobalSettings[k] _, vol := VolatileSettings[k] if def, ok := defaults[k]; ok && okcur && !vol && reflect.DeepEqual(cur, def) { delete(parsedSettings, k) } } } // add any options to parsedSettings that have since been marked as non-default for k, v := range GlobalSettings { if def, ok := defaults[k]; !ok || !reflect.DeepEqual(v, def) { if _, wr := ModifiedSettings[k]; wr { parsedSettings[k] = v } } } txt, _ := json.MarshalIndent(parsedSettings, "", " ") err = ioutil.WriteFile(filename, append(txt, '\n'), 0644) } return err } // OverwriteSettings writes the current settings to settings.json and // resets any user configuration of local settings present in settings.json func OverwriteSettings(filename string) error { settings := make(map[string]interface{}) var err error if _, e := os.Stat(ConfigDir); e == nil { defaults := DefaultAllSettings() for k, v := range GlobalSettings { if def, ok := defaults[k]; !ok || !reflect.DeepEqual(v, def) { if _, wr := ModifiedSettings[k]; wr { settings[k] = v } } } txt, _ := json.MarshalIndent(settings, "", " ") err = ioutil.WriteFile(filename, append(txt, '\n'), 0644) } return err } // RegisterCommonOptionPlug creates a new option (called pl.name). This is meant to be called by plugins to add options. func RegisterCommonOptionPlug(pl string, name string, defaultvalue interface{}) error { return RegisterCommonOption(pl+"."+name, defaultvalue) } // RegisterGlobalOptionPlug creates a new global-only option (named pl.name) func RegisterGlobalOptionPlug(pl string, name string, defaultvalue interface{}) error { return RegisterGlobalOption(pl+"."+name, defaultvalue) } // RegisterCommonOption creates a new option func RegisterCommonOption(name string, defaultvalue interface{}) error { if _, ok := GlobalSettings[name]; !ok { GlobalSettings[name] = defaultvalue } defaultCommonSettings[name] = defaultvalue return nil } // RegisterGlobalOption creates a new global-only option func RegisterGlobalOption(name string, defaultvalue interface{}) error { if _, ok := GlobalSettings[name]; !ok { GlobalSettings[name] = defaultvalue } DefaultGlobalOnlySettings[name] = defaultvalue return nil } // GetGlobalOption returns the global value of the given option func GetGlobalOption(name string) interface{} { return GlobalSettings[name] } func defaultFileFormat() string { if runtime.GOOS == "windows" { return "dos" } return "unix" } func GetInfoBarOffset() int { offset := 0 if GetGlobalOption("infobar").(bool) { offset++ } if GetGlobalOption("keymenu").(bool) { offset += 2 } return offset } // DefaultCommonSettings returns a map of all common buffer settings // and their default values func DefaultCommonSettings() map[string]interface{} { commonsettings := make(map[string]interface{}) for k, v := range defaultCommonSettings { commonsettings[k] = v } return commonsettings } // DefaultAllSettings returns a map of all common buffer & global-only settings // and their default values func DefaultAllSettings() map[string]interface{} { allsettings := make(map[string]interface{}) for k, v := range defaultCommonSettings { allsettings[k] = v } for k, v := range DefaultGlobalOnlySettings { allsettings[k] = v } return allsettings } // GetNativeValue parses and validates a value for a given option func GetNativeValue(option string, realValue interface{}, value string) (interface{}, error) { var native interface{} kind := reflect.TypeOf(realValue).Kind() if kind == reflect.Bool { b, err := util.ParseBool(value) if err != nil { return nil, ErrInvalidValue } native = b } else if kind == reflect.String { native = value } else if kind == reflect.Float64 { f, err := strconv.ParseFloat(value, 64) if err != nil { return nil, ErrInvalidValue } native = f } else { return nil, ErrInvalidValue } return native, nil } // OptionIsValid checks if a value is valid for a certain option func OptionIsValid(option string, value interface{}) error { if validator, ok := optionValidators[option]; ok { return validator(option, value) } return nil } // Option validators func validatePositiveValue(option string, value interface{}) error { nativeValue, ok := value.(float64) if !ok { return errors.New("Expected numeric type for " + option) } if nativeValue < 1 { return errors.New(option + " must be greater than 0") } return nil } func validateNonNegativeValue(option string, value interface{}) error { nativeValue, ok := value.(float64) if !ok { return errors.New("Expected numeric type for " + option) } if nativeValue < 0 { return errors.New(option + " must be non-negative") } return nil } func validateChoice(option string, value interface{}) error { if choices, ok := OptionChoices[option]; ok { val, ok := value.(string) if !ok { return errors.New("Expected string type for " + option) } for _, v := range choices { if val == v { return nil } } choicesStr := strings.Join(choices, ", ") return errors.New(option + " must be one of: " + choicesStr) } return errors.New("Option has no pre-defined choices") } func validateColorscheme(option string, value interface{}) error { colorscheme, ok := value.(string) if !ok { return errors.New("Expected string type for colorscheme") } if !ColorschemeExists(colorscheme) { return errors.New(colorscheme + " is not a valid colorscheme") } return nil } func validateEncoding(option string, value interface{}) error { _, err := htmlindex.Get(value.(string)) return err } micro-2.0.14/internal/display/0000775000175000017510000000000014663411671015532 5ustar nileshnileshmicro-2.0.14/internal/display/bufwindow.go0000664000175000017510000005057714663411671020103 0ustar nileshnileshpackage display import ( "strconv" runewidth "github.com/mattn/go-runewidth" "github.com/zyedidia/micro/v2/internal/buffer" "github.com/zyedidia/micro/v2/internal/config" "github.com/zyedidia/micro/v2/internal/screen" "github.com/zyedidia/micro/v2/internal/util" "github.com/zyedidia/tcell/v2" ) // The BufWindow provides a way of displaying a certain section of a buffer. type BufWindow struct { *View // Buffer being shown in this window Buf *buffer.Buffer active bool sline *StatusLine bufWidth int bufHeight int gutterOffset int hasMessage bool maxLineNumLength int drawDivider bool } // NewBufWindow creates a new window at a location in the screen with a width and height func NewBufWindow(x, y, width, height int, buf *buffer.Buffer) *BufWindow { w := new(BufWindow) w.View = new(View) w.X, w.Y, w.Width, w.Height = x, y, width, height w.SetBuffer(buf) w.active = true w.sline = NewStatusLine(w) return w } // SetBuffer sets this window's buffer. func (w *BufWindow) SetBuffer(b *buffer.Buffer) { w.Buf = b b.OptionCallback = func(option string, nativeValue interface{}) { if option == "softwrap" { if nativeValue.(bool) { w.StartCol = 0 } else { w.StartLine.Row = 0 } } if option == "softwrap" || option == "wordwrap" { w.Relocate() for _, c := range w.Buf.GetCursors() { c.LastVisualX = c.GetVisualX() } } } b.GetVisualX = func(loc buffer.Loc) int { return w.VLocFromLoc(loc).VisualX } } // GetView gets the view. func (w *BufWindow) GetView() *View { return w.View } // GetView sets the view. func (w *BufWindow) SetView(view *View) { w.View = view } // Resize resizes this window. func (w *BufWindow) Resize(width, height int) { w.Width, w.Height = width, height w.updateDisplayInfo() w.Relocate() } // SetActive marks the window as active. func (w *BufWindow) SetActive(b bool) { w.active = b } // IsActive returns true if this window is active. func (w *BufWindow) IsActive() bool { return w.active } // BufView returns the width, height and x,y location of the actual buffer. // It is not exactly the same as the whole window which also contains gutter, // ruler, scrollbar and statusline. func (w *BufWindow) BufView() View { return View{ X: w.X + w.gutterOffset, Y: w.Y, Width: w.bufWidth, Height: w.bufHeight, StartLine: w.StartLine, StartCol: w.StartCol, } } func (w *BufWindow) updateDisplayInfo() { b := w.Buf w.drawDivider = false if !b.Settings["statusline"].(bool) { _, h := screen.Screen.Size() infoY := h if config.GetGlobalOption("infobar").(bool) { infoY-- } if w.Y+w.Height != infoY { w.drawDivider = true } } w.bufHeight = w.Height if b.Settings["statusline"].(bool) || w.drawDivider { w.bufHeight-- } scrollbarWidth := 0 if w.Buf.Settings["scrollbar"].(bool) && w.Buf.LinesNum() > w.Height && w.Width > 0 { scrollbarWidth = 1 } w.hasMessage = len(b.Messages) > 0 // We need to know the string length of the largest line number // so we can pad appropriately when displaying line numbers w.maxLineNumLength = len(strconv.Itoa(b.LinesNum())) w.gutterOffset = 0 if w.hasMessage { w.gutterOffset += 2 } if b.Settings["diffgutter"].(bool) { w.gutterOffset++ } if b.Settings["ruler"].(bool) { w.gutterOffset += w.maxLineNumLength + 1 } if w.gutterOffset > w.Width-scrollbarWidth { w.gutterOffset = w.Width - scrollbarWidth } prevBufWidth := w.bufWidth w.bufWidth = w.Width - w.gutterOffset - scrollbarWidth if w.bufWidth != prevBufWidth && w.Buf.Settings["softwrap"].(bool) { for _, c := range w.Buf.GetCursors() { c.LastVisualX = c.GetVisualX() } } } func (w *BufWindow) getStartInfo(n, lineN int) ([]byte, int, int, *tcell.Style) { tabsize := util.IntOpt(w.Buf.Settings["tabsize"]) width := 0 bloc := buffer.Loc{0, lineN} b := w.Buf.LineBytes(lineN) curStyle := config.DefStyle var s *tcell.Style for len(b) > 0 { r, _, size := util.DecodeCharacter(b) curStyle, found := w.getStyle(curStyle, bloc) if found { s = &curStyle } w := 0 switch r { case '\t': ts := tabsize - (width % tabsize) w = ts default: w = runewidth.RuneWidth(r) } if width+w > n { return b, n - width, bloc.X, s } width += w b = b[size:] bloc.X++ } return b, n - width, bloc.X, s } // Clear resets all cells in this window to the default style func (w *BufWindow) Clear() { for y := 0; y < w.Height; y++ { for x := 0; x < w.Width; x++ { screen.SetContent(w.X+x, w.Y+y, ' ', nil, config.DefStyle) } } } // Relocate moves the view window so that the cursor is in view // This is useful if the user has scrolled far away, and then starts typing // Returns true if the window location is moved func (w *BufWindow) Relocate() bool { b := w.Buf height := w.bufHeight ret := false activeC := w.Buf.GetActiveCursor() scrollmargin := int(b.Settings["scrollmargin"].(float64)) c := w.SLocFromLoc(activeC.Loc) bStart := SLoc{0, 0} bEnd := w.SLocFromLoc(b.End()) if c.LessThan(w.Scroll(w.StartLine, scrollmargin)) && c.GreaterThan(w.Scroll(bStart, scrollmargin-1)) { w.StartLine = w.Scroll(c, -scrollmargin) ret = true } else if c.LessThan(w.StartLine) { w.StartLine = c ret = true } if c.GreaterThan(w.Scroll(w.StartLine, height-1-scrollmargin)) && c.LessEqual(w.Scroll(bEnd, -scrollmargin)) { w.StartLine = w.Scroll(c, -height+1+scrollmargin) ret = true } else if c.GreaterThan(w.Scroll(bEnd, -scrollmargin)) && c.GreaterThan(w.Scroll(w.StartLine, height-1)) { w.StartLine = w.Scroll(bEnd, -height+1) ret = true } // horizontal relocation (scrolling) if !b.Settings["softwrap"].(bool) { cx := activeC.GetVisualX() rw := runewidth.RuneWidth(activeC.RuneUnder(activeC.X)) if rw == 0 { rw = 1 // tab or newline } if cx < w.StartCol { w.StartCol = cx ret = true } if cx+w.gutterOffset+rw > w.StartCol+w.Width { w.StartCol = cx - w.Width + w.gutterOffset + rw ret = true } } return ret } // LocFromVisual takes a visual location (x and y position) and returns the // position in the buffer corresponding to the visual location // If the requested position does not correspond to a buffer location it returns // the nearest position func (w *BufWindow) LocFromVisual(svloc buffer.Loc) buffer.Loc { vx := svloc.X - w.X - w.gutterOffset if vx < 0 { vx = 0 } vloc := VLoc{ SLoc: w.Scroll(w.StartLine, svloc.Y-w.Y), VisualX: vx + w.StartCol, } return w.LocFromVLoc(vloc) } func (w *BufWindow) drawGutter(vloc *buffer.Loc, bloc *buffer.Loc) { char := ' ' s := config.DefStyle for _, m := range w.Buf.Messages { if m.Start.Y == bloc.Y || m.End.Y == bloc.Y { s = m.Style() char = '>' break } } for i := 0; i < 2 && vloc.X < w.gutterOffset; i++ { screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, char, nil, s) vloc.X++ } } func (w *BufWindow) drawDiffGutter(backgroundStyle tcell.Style, softwrapped bool, vloc *buffer.Loc, bloc *buffer.Loc) { if vloc.X >= w.gutterOffset { return } symbol := ' ' styleName := "" switch w.Buf.DiffStatus(bloc.Y) { case buffer.DSAdded: symbol = '\u258C' // Left half block styleName = "diff-added" case buffer.DSModified: symbol = '\u258C' // Left half block styleName = "diff-modified" case buffer.DSDeletedAbove: if !softwrapped { symbol = '\u2594' // Upper one eighth block styleName = "diff-deleted" } } style := backgroundStyle if s, ok := config.Colorscheme[styleName]; ok { foreground, _, _ := s.Decompose() style = style.Foreground(foreground) } screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, symbol, nil, style) vloc.X++ } func (w *BufWindow) drawLineNum(lineNumStyle tcell.Style, softwrapped bool, vloc *buffer.Loc, bloc *buffer.Loc) { cursorLine := w.Buf.GetActiveCursor().Loc.Y var lineInt int if w.Buf.Settings["relativeruler"] == false || cursorLine == bloc.Y { lineInt = bloc.Y + 1 } else { lineInt = bloc.Y - cursorLine } lineNum := []rune(strconv.Itoa(util.Abs(lineInt))) // Write the spaces before the line number if necessary for i := 0; i < w.maxLineNumLength-len(lineNum) && vloc.X < w.gutterOffset; i++ { screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ' ', nil, lineNumStyle) vloc.X++ } // Write the actual line number for i := 0; i < len(lineNum) && vloc.X < w.gutterOffset; i++ { if softwrapped || (w.bufWidth == 0 && w.Buf.Settings["softwrap"] == true) { screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ' ', nil, lineNumStyle) } else { screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, lineNum[i], nil, lineNumStyle) } vloc.X++ } // Write the extra space if vloc.X < w.gutterOffset { screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ' ', nil, lineNumStyle) vloc.X++ } } // getStyle returns the highlight style for the given character position // If there is no change to the current highlight style it just returns that func (w *BufWindow) getStyle(style tcell.Style, bloc buffer.Loc) (tcell.Style, bool) { if group, ok := w.Buf.Match(bloc.Y)[bloc.X]; ok { s := config.GetColor(group.String()) return s, true } return style, false } func (w *BufWindow) showCursor(x, y int, main bool) { if w.active { if main { screen.ShowCursor(x, y) } else { screen.ShowFakeCursorMulti(x, y) } } } // displayBuffer draws the buffer being shown in this window on the screen.Screen func (w *BufWindow) displayBuffer() { b := w.Buf if w.Height <= 0 || w.Width <= 0 { return } maxWidth := w.gutterOffset + w.bufWidth if b.ModifiedThisFrame { if b.Settings["diffgutter"].(bool) { b.UpdateDiff() } b.ModifiedThisFrame = false } var matchingBraces []buffer.Loc // bracePairs is defined in buffer.go if b.Settings["matchbrace"].(bool) { for _, c := range b.GetCursors() { if c.HasSelection() { continue } mb, left, found := b.FindMatchingBrace(c.Loc) if found { matchingBraces = append(matchingBraces, mb) if !left { if b.Settings["matchbracestyle"].(string) != "highlight" { matchingBraces = append(matchingBraces, c.Loc) } } else { matchingBraces = append(matchingBraces, c.Loc.Move(-1, b)) } } } } lineNumStyle := config.DefStyle if style, ok := config.Colorscheme["line-number"]; ok { lineNumStyle = style } curNumStyle := config.DefStyle if style, ok := config.Colorscheme["current-line-number"]; ok { if !b.Settings["cursorline"].(bool) { curNumStyle = lineNumStyle } else { curNumStyle = style } } softwrap := b.Settings["softwrap"].(bool) wordwrap := softwrap && b.Settings["wordwrap"].(bool) tabsize := util.IntOpt(b.Settings["tabsize"]) colorcolumn := util.IntOpt(b.Settings["colorcolumn"]) // this represents the current draw position // within the current window vloc := buffer.Loc{X: 0, Y: 0} if softwrap { // the start line may be partially out of the current window vloc.Y = -w.StartLine.Row } // this represents the current draw position in the buffer (char positions) bloc := buffer.Loc{X: -1, Y: w.StartLine.Line} cursors := b.GetCursors() curStyle := config.DefStyle for ; vloc.Y < w.bufHeight; vloc.Y++ { vloc.X = 0 currentLine := false for _, c := range cursors { if bloc.Y == c.Y && w.active { currentLine = true break } } s := lineNumStyle if currentLine { s = curNumStyle } if vloc.Y >= 0 { if w.hasMessage { w.drawGutter(&vloc, &bloc) } if b.Settings["diffgutter"].(bool) { w.drawDiffGutter(s, false, &vloc, &bloc) } if b.Settings["ruler"].(bool) { w.drawLineNum(s, false, &vloc, &bloc) } } else { vloc.X = w.gutterOffset } bline := b.LineBytes(bloc.Y) blineLen := util.CharacterCount(bline) leadingwsEnd := len(util.GetLeadingWhitespace(bline)) trailingwsStart := blineLen - util.CharacterCount(util.GetTrailingWhitespace(bline)) line, nColsBeforeStart, bslice, startStyle := w.getStartInfo(w.StartCol, bloc.Y) if startStyle != nil { curStyle = *startStyle } bloc.X = bslice draw := func(r rune, combc []rune, style tcell.Style, highlight bool, showcursor bool) { if nColsBeforeStart <= 0 && vloc.Y >= 0 { if highlight { if w.Buf.HighlightSearch && w.Buf.SearchMatch(bloc) { style = config.DefStyle.Reverse(true) if s, ok := config.Colorscheme["hlsearch"]; ok { style = s } } _, origBg, _ := style.Decompose() _, defBg, _ := config.DefStyle.Decompose() // syntax or hlsearch highlighting with non-default background takes precedence // over cursor-line and color-column dontOverrideBackground := origBg != defBg if b.Settings["hltaberrors"].(bool) { if s, ok := config.Colorscheme["tab-error"]; ok { isTab := (r == '\t') || (r == ' ' && !showcursor) if (b.Settings["tabstospaces"].(bool) && isTab) || (!b.Settings["tabstospaces"].(bool) && bloc.X < leadingwsEnd && r == ' ' && !isTab) { fg, _, _ := s.Decompose() style = style.Background(fg) dontOverrideBackground = true } } } if b.Settings["hltrailingws"].(bool) { if s, ok := config.Colorscheme["trailingws"]; ok { if bloc.X >= trailingwsStart && bloc.X < blineLen { hl := true for _, c := range cursors { if c.NewTrailingWsY == bloc.Y { hl = false break } } if hl { fg, _, _ := s.Decompose() style = style.Background(fg) dontOverrideBackground = true } } } } for _, c := range cursors { if c.HasSelection() && (bloc.GreaterEqual(c.CurSelection[0]) && bloc.LessThan(c.CurSelection[1]) || bloc.LessThan(c.CurSelection[0]) && bloc.GreaterEqual(c.CurSelection[1])) { // The current character is selected style = config.DefStyle.Reverse(true) if s, ok := config.Colorscheme["selection"]; ok { style = s } } if b.Settings["cursorline"].(bool) && w.active && !dontOverrideBackground && !c.HasSelection() && c.Y == bloc.Y { if s, ok := config.Colorscheme["cursor-line"]; ok { fg, _, _ := s.Decompose() style = style.Background(fg) } } } for _, m := range b.Messages { if bloc.GreaterEqual(m.Start) && bloc.LessThan(m.End) || bloc.LessThan(m.End) && bloc.GreaterEqual(m.Start) { style = style.Underline(true) break } } if r == '\t' { indentrunes := []rune(b.Settings["indentchar"].(string)) // if empty indentchar settings, use space if len(indentrunes) == 0 { indentrunes = []rune{' '} } r = indentrunes[0] if s, ok := config.Colorscheme["indent-char"]; ok && r != ' ' { fg, _, _ := s.Decompose() style = style.Foreground(fg) } } if s, ok := config.Colorscheme["color-column"]; ok { if colorcolumn != 0 && vloc.X-w.gutterOffset+w.StartCol == colorcolumn && !dontOverrideBackground { fg, _, _ := s.Decompose() style = style.Background(fg) } } for _, mb := range matchingBraces { if mb.X == bloc.X && mb.Y == bloc.Y { if b.Settings["matchbracestyle"].(string) == "highlight" { if s, ok := config.Colorscheme["match-brace"]; ok { style = s } else { style = style.Reverse(true) } } else { style = style.Underline(true) } } } } screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, r, combc, style) if showcursor { for _, c := range cursors { if c.X == bloc.X && c.Y == bloc.Y && !c.HasSelection() { w.showCursor(w.X+vloc.X, w.Y+vloc.Y, c.Num == 0) } } } } if nColsBeforeStart <= 0 { vloc.X++ } nColsBeforeStart-- } wrap := func() { vloc.X = 0 if w.hasMessage { w.drawGutter(&vloc, &bloc) } if b.Settings["diffgutter"].(bool) { w.drawDiffGutter(lineNumStyle, true, &vloc, &bloc) } // This will draw an empty line number because the current line is wrapped if b.Settings["ruler"].(bool) { w.drawLineNum(lineNumStyle, true, &vloc, &bloc) } } type glyph struct { r rune combc []rune style tcell.Style width int } var word []glyph if wordwrap { word = make([]glyph, 0, w.bufWidth) } else { word = make([]glyph, 0, 1) } wordwidth := 0 totalwidth := w.StartCol - nColsBeforeStart for len(line) > 0 && vloc.X < maxWidth { r, combc, size := util.DecodeCharacter(line) line = line[size:] loc := buffer.Loc{X: bloc.X + len(word), Y: bloc.Y} curStyle, _ = w.getStyle(curStyle, loc) width := 0 switch r { case '\t': ts := tabsize - (totalwidth % tabsize) width = util.Min(ts, maxWidth-vloc.X) totalwidth += ts default: width = runewidth.RuneWidth(r) totalwidth += width } word = append(word, glyph{r, combc, curStyle, width}) wordwidth += width // Collect a complete word to know its width. // If wordwrap is off, every single character is a complete "word". if wordwrap { if !util.IsWhitespace(r) && len(line) > 0 && wordwidth < w.bufWidth { continue } } // If a word (or just a wide rune) does not fit in the window if vloc.X+wordwidth > maxWidth && vloc.X > w.gutterOffset { for vloc.X < maxWidth { draw(' ', nil, config.DefStyle, false, false) } // We either stop or we wrap to draw the word in the next line if !softwrap { break } else { vloc.Y++ if vloc.Y >= w.bufHeight { break } wrap() } } for _, r := range word { draw(r.r, r.combc, r.style, true, true) // Draw any extra characters either spaces for tabs or @ for incomplete wide runes if r.width > 1 { char := ' ' if r.r != '\t' { char = '@' } for i := 1; i < r.width; i++ { draw(char, nil, r.style, true, false) } } bloc.X++ } word = word[:0] wordwidth = 0 // If we reach the end of the window then we either stop or we wrap for softwrap if vloc.X >= maxWidth { if !softwrap { break } else { vloc.Y++ if vloc.Y >= w.bufHeight { break } wrap() } } } style := config.DefStyle for _, c := range cursors { if b.Settings["cursorline"].(bool) && w.active && !c.HasSelection() && c.Y == bloc.Y { if s, ok := config.Colorscheme["cursor-line"]; ok { fg, _, _ := s.Decompose() style = style.Background(fg) } } } for i := vloc.X; i < maxWidth; i++ { curStyle := style if s, ok := config.Colorscheme["color-column"]; ok { if colorcolumn != 0 && i-w.gutterOffset+w.StartCol == colorcolumn { fg, _, _ := s.Decompose() curStyle = style.Background(fg) } } screen.SetContent(i+w.X, vloc.Y+w.Y, ' ', nil, curStyle) } if vloc.X != maxWidth { // Display newline within a selection draw(' ', nil, config.DefStyle, true, true) } bloc.X = w.StartCol bloc.Y++ if bloc.Y >= b.LinesNum() { break } } } func (w *BufWindow) displayStatusLine() { if w.Buf.Settings["statusline"].(bool) { w.sline.Display() } else if w.drawDivider { divchars := config.GetGlobalOption("divchars").(string) if util.CharacterCountInString(divchars) != 2 { divchars = "|-" } _, _, size := util.DecodeCharacterInString(divchars) divchar, combc, _ := util.DecodeCharacterInString(divchars[size:]) dividerStyle := config.DefStyle if style, ok := config.Colorscheme["divider"]; ok { dividerStyle = style } divreverse := config.GetGlobalOption("divreverse").(bool) if divreverse { dividerStyle = dividerStyle.Reverse(true) } for x := w.X; x < w.X+w.Width; x++ { screen.SetContent(x, w.Y+w.Height-1, divchar, combc, dividerStyle) } } } func (w *BufWindow) displayScrollBar() { if w.Buf.Settings["scrollbar"].(bool) && w.Buf.LinesNum() > w.Height { scrollX := w.X + w.Width - 1 barsize := int(float64(w.Height) / float64(w.Buf.LinesNum()) * float64(w.Height)) if barsize < 1 { barsize = 1 } barstart := w.Y + int(float64(w.StartLine.Line)/float64(w.Buf.LinesNum())*float64(w.Height)) scrollBarStyle := config.DefStyle.Reverse(true) if style, ok := config.Colorscheme["scrollbar"]; ok { scrollBarStyle = style } scrollBarChar := config.GetGlobalOption("scrollbarchar").(string) if util.CharacterCountInString(scrollBarChar) != 1 { scrollBarChar = "|" } scrollBarRune := []rune(scrollBarChar) for y := barstart; y < util.Min(barstart+barsize, w.Y+w.bufHeight); y++ { screen.SetContent(scrollX, y, scrollBarRune[0], nil, scrollBarStyle) } } } // Display displays the buffer and the statusline func (w *BufWindow) Display() { w.updateDisplayInfo() w.displayStatusLine() w.displayScrollBar() w.displayBuffer() } micro-2.0.14/internal/display/infowindow.go0000664000175000017510000001552114663411671020250 0ustar nileshnileshpackage display import ( runewidth "github.com/mattn/go-runewidth" "github.com/zyedidia/micro/v2/internal/buffer" "github.com/zyedidia/micro/v2/internal/config" "github.com/zyedidia/micro/v2/internal/info" "github.com/zyedidia/micro/v2/internal/screen" "github.com/zyedidia/micro/v2/internal/util" "github.com/zyedidia/tcell/v2" ) type InfoWindow struct { *info.InfoBuf *View hscroll int } func (i *InfoWindow) errStyle() tcell.Style { errStyle := config.DefStyle. Foreground(tcell.ColorBlack). Background(tcell.ColorMaroon) if _, ok := config.Colorscheme["error-message"]; ok { errStyle = config.Colorscheme["error-message"] } return errStyle } func (i *InfoWindow) defStyle() tcell.Style { defStyle := config.DefStyle if _, ok := config.Colorscheme["message"]; ok { defStyle = config.Colorscheme["message"] } return defStyle } func NewInfoWindow(b *info.InfoBuf) *InfoWindow { iw := new(InfoWindow) iw.InfoBuf = b iw.View = new(View) iw.Width, iw.Y = screen.Screen.Size() iw.Y-- return iw } func (i *InfoWindow) Resize(w, h int) { i.Width = w i.Y = h } func (i *InfoWindow) SetBuffer(b *buffer.Buffer) { i.InfoBuf.Buffer = b } func (i *InfoWindow) Relocate() bool { return false } func (i *InfoWindow) GetView() *View { return i.View } func (i *InfoWindow) SetView(v *View) {} func (i *InfoWindow) SetActive(b bool) {} func (i *InfoWindow) IsActive() bool { return true } func (i *InfoWindow) LocFromVisual(vloc buffer.Loc) buffer.Loc { c := i.Buffer.GetActiveCursor() l := i.Buffer.LineBytes(0) n := util.CharacterCountInString(i.Msg) return buffer.Loc{c.GetCharPosInLine(l, vloc.X-n), 0} } func (i *InfoWindow) BufView() View { return View{ X: 0, Y: i.Y, Width: i.Width, Height: 1, StartLine: SLoc{0, 0}, StartCol: 0, } } func (i *InfoWindow) Scroll(s SLoc, n int) SLoc { return s } func (i *InfoWindow) Diff(s1, s2 SLoc) int { return 0 } func (i *InfoWindow) SLocFromLoc(loc buffer.Loc) SLoc { return SLoc{0, 0} } func (i *InfoWindow) VLocFromLoc(loc buffer.Loc) VLoc { return VLoc{SLoc{0, 0}, loc.X} } func (i *InfoWindow) LocFromVLoc(vloc VLoc) buffer.Loc { return buffer.Loc{vloc.VisualX, 0} } func (i *InfoWindow) Clear() { for x := 0; x < i.Width; x++ { screen.SetContent(x, i.Y, ' ', nil, i.defStyle()) } } func (i *InfoWindow) displayBuffer() { b := i.Buffer line := b.LineBytes(0) activeC := b.GetActiveCursor() blocX := 0 vlocX := util.CharacterCountInString(i.Msg) tabsize := 4 line, nColsBeforeStart, bslice := util.SliceVisualEnd(line, blocX, tabsize) blocX = bslice draw := func(r rune, combc []rune, style tcell.Style) { if nColsBeforeStart <= 0 { bloc := buffer.Loc{X: blocX, Y: 0} if activeC.HasSelection() && (bloc.GreaterEqual(activeC.CurSelection[0]) && bloc.LessThan(activeC.CurSelection[1]) || bloc.LessThan(activeC.CurSelection[0]) && bloc.GreaterEqual(activeC.CurSelection[1])) { // The current character is selected style = i.defStyle().Reverse(true) if s, ok := config.Colorscheme["selection"]; ok { style = s } } rw := runewidth.RuneWidth(r) for j := 0; j < rw; j++ { c := r if j > 0 { c = ' ' combc = nil } screen.SetContent(vlocX, i.Y, c, combc, style) } vlocX++ } nColsBeforeStart-- } totalwidth := blocX - nColsBeforeStart for len(line) > 0 { curVX := vlocX curBX := blocX r, combc, size := util.DecodeCharacter(line) draw(r, combc, i.defStyle()) width := 0 char := ' ' switch r { case '\t': ts := tabsize - (totalwidth % tabsize) width = ts default: width = runewidth.RuneWidth(r) char = '@' } blocX++ line = line[size:] // Draw any extra characters either spaces for tabs or @ for incomplete wide runes if width > 1 { for j := 1; j < width; j++ { draw(char, nil, i.defStyle()) } } if activeC.X == curBX { screen.ShowCursor(curVX, i.Y) } totalwidth += width if vlocX >= i.Width { break } } if activeC.X == blocX { screen.ShowCursor(vlocX, i.Y) } } var keydisplay = []string{"^Q Quit, ^S Save, ^O Open, ^G Help, ^E Command Bar, ^K Cut Line", "^F Find, ^Z Undo, ^Y Redo, ^A Select All, ^D Duplicate Line, ^T New Tab"} func (i *InfoWindow) displayKeyMenu() { // TODO: maybe make this based on the actual keybindings for y := 0; y < len(keydisplay); y++ { for x := 0; x < i.Width; x++ { if x < len(keydisplay[y]) { screen.SetContent(x, i.Y-len(keydisplay)+y, rune(keydisplay[y][x]), nil, i.defStyle()) } else { screen.SetContent(x, i.Y-len(keydisplay)+y, ' ', nil, i.defStyle()) } } } } func (i *InfoWindow) totalSize() int { sum := 0 for _, n := range i.Suggestions { sum += runewidth.StringWidth(n) + 1 } return sum } func (i *InfoWindow) scrollToSuggestion() { x := 0 s := i.totalSize() for j, n := range i.Suggestions { c := util.CharacterCountInString(n) if j == i.CurSuggestion { if x+c >= i.hscroll+i.Width { i.hscroll = util.Clamp(x+c+1-i.Width, 0, s-i.Width) } else if x < i.hscroll { i.hscroll = util.Clamp(x-1, 0, s-i.Width) } break } x += c + 1 } if s-i.Width <= 0 { i.hscroll = 0 } } func (i *InfoWindow) Display() { if i.HasPrompt || config.GlobalSettings["infobar"].(bool) { i.Clear() x := 0 if config.GetGlobalOption("keymenu").(bool) { i.displayKeyMenu() } if !i.HasPrompt && !i.HasMessage && !i.HasError { return } i.Clear() style := i.defStyle() if i.HasError { style = i.errStyle() } display := i.Msg for _, c := range display { screen.SetContent(x, i.Y, c, nil, style) x += runewidth.RuneWidth(c) } if i.HasPrompt { i.displayBuffer() } } if i.HasSuggestions && len(i.Suggestions) > 1 { i.scrollToSuggestion() x := -i.hscroll done := false statusLineStyle := config.DefStyle.Reverse(true) if style, ok := config.Colorscheme["statusline.suggestions"]; ok { statusLineStyle = style } else if style, ok := config.Colorscheme["statusline"]; ok { statusLineStyle = style } keymenuOffset := 0 if config.GetGlobalOption("keymenu").(bool) { keymenuOffset = len(keydisplay) } draw := func(r rune, s tcell.Style) { y := i.Y - keymenuOffset - 1 rw := runewidth.RuneWidth(r) for j := 0; j < rw; j++ { c := r if j > 0 { c = ' ' } if x == i.Width-1 && !done { screen.SetContent(i.Width-1, y, '>', nil, s) x++ break } else if x == 0 && i.hscroll > 0 { screen.SetContent(0, y, '<', nil, s) } else if x >= 0 && x < i.Width { screen.SetContent(x, y, c, nil, s) } x++ } } for j, s := range i.Suggestions { style := statusLineStyle if i.CurSuggestion == j { style = style.Reverse(true) } for _, r := range s { draw(r, style) // screen.SetContent(x, i.Y-keymenuOffset-1, r, nil, style) } draw(' ', statusLineStyle) } for x < i.Width { draw(' ', statusLineStyle) } } } micro-2.0.14/internal/display/softwrap.go0000664000175000017510000001662314663411671017736 0ustar nileshnileshpackage display import ( runewidth "github.com/mattn/go-runewidth" "github.com/zyedidia/micro/v2/internal/buffer" "github.com/zyedidia/micro/v2/internal/util" ) // SLoc represents a vertical scrolling location, i.e. a location of a visual line // in the buffer. When softwrap is enabled, a buffer line may be displayed as // multiple visual lines (rows). So SLoc stores a number of a line in the buffer // and a number of a row within this line. type SLoc struct { Line, Row int } // LessThan returns true if s is less b func (s SLoc) LessThan(b SLoc) bool { if s.Line < b.Line { return true } return s.Line == b.Line && s.Row < b.Row } // GreaterThan returns true if s is bigger than b func (s SLoc) GreaterThan(b SLoc) bool { if s.Line > b.Line { return true } return s.Line == b.Line && s.Row > b.Row } // LessEqual returns true if s is less than or equal to b func (s SLoc) LessEqual(b SLoc) bool { if s.Line < b.Line { return true } if s.Line == b.Line && s.Row < b.Row { return true } return s == b } // GreaterEqual returns true if s is bigger than or equal to b func (s SLoc) GreaterEqual(b SLoc) bool { if s.Line > b.Line { return true } if s.Line == b.Line && s.Row > b.Row { return true } return s == b } // VLoc represents a location in the buffer as a visual location in the // linewrapped buffer. type VLoc struct { SLoc VisualX int } type SoftWrap interface { Scroll(s SLoc, n int) SLoc Diff(s1, s2 SLoc) int SLocFromLoc(loc buffer.Loc) SLoc VLocFromLoc(loc buffer.Loc) VLoc LocFromVLoc(vloc VLoc) buffer.Loc } func (w *BufWindow) getVLocFromLoc(loc buffer.Loc) VLoc { vloc := VLoc{SLoc: SLoc{loc.Y, 0}, VisualX: 0} if loc.X <= 0 { return vloc } if w.bufWidth <= 0 { return vloc } wordwrap := w.Buf.Settings["wordwrap"].(bool) tabsize := util.IntOpt(w.Buf.Settings["tabsize"]) line := w.Buf.LineBytes(loc.Y) x := 0 totalwidth := 0 wordwidth := 0 wordoffset := 0 for len(line) > 0 { r, _, size := util.DecodeCharacter(line) line = line[size:] width := 0 switch r { case '\t': ts := tabsize - (totalwidth % tabsize) width = util.Min(ts, w.bufWidth-vloc.VisualX) totalwidth += ts default: width = runewidth.RuneWidth(r) totalwidth += width } wordwidth += width // Collect a complete word to know its width. // If wordwrap is off, every single character is a complete "word". if wordwrap { if !util.IsWhitespace(r) && len(line) > 0 && wordwidth < w.bufWidth { if x < loc.X { wordoffset += width x++ } continue } } // If a word (or just a wide rune) does not fit in the window if vloc.VisualX+wordwidth > w.bufWidth && vloc.VisualX > 0 { vloc.Row++ vloc.VisualX = 0 } if x == loc.X { vloc.VisualX += wordoffset return vloc } x++ vloc.VisualX += wordwidth wordwidth = 0 wordoffset = 0 if vloc.VisualX >= w.bufWidth { vloc.Row++ vloc.VisualX = 0 } } return vloc } func (w *BufWindow) getLocFromVLoc(svloc VLoc) buffer.Loc { loc := buffer.Loc{X: 0, Y: svloc.Line} if w.bufWidth <= 0 { return loc } wordwrap := w.Buf.Settings["wordwrap"].(bool) tabsize := util.IntOpt(w.Buf.Settings["tabsize"]) line := w.Buf.LineBytes(svloc.Line) vloc := VLoc{SLoc: SLoc{svloc.Line, 0}, VisualX: 0} totalwidth := 0 var widths []int if wordwrap { widths = make([]int, 0, w.bufWidth) } else { widths = make([]int, 0, 1) } wordwidth := 0 for len(line) > 0 { r, _, size := util.DecodeCharacter(line) line = line[size:] width := 0 switch r { case '\t': ts := tabsize - (totalwidth % tabsize) width = util.Min(ts, w.bufWidth-vloc.VisualX) totalwidth += ts default: width = runewidth.RuneWidth(r) totalwidth += width } widths = append(widths, width) wordwidth += width // Collect a complete word to know its width. // If wordwrap is off, every single character is a complete "word". if wordwrap { if !util.IsWhitespace(r) && len(line) > 0 && wordwidth < w.bufWidth { continue } } // If a word (or just a wide rune) does not fit in the window if vloc.VisualX+wordwidth > w.bufWidth && vloc.VisualX > 0 { if vloc.Row == svloc.Row { if wordwrap { // it's a word, not a wide rune loc.X-- } return loc } vloc.Row++ vloc.VisualX = 0 } for i := range widths { vloc.VisualX += widths[i] if vloc.Row == svloc.Row && vloc.VisualX > svloc.VisualX { return loc } loc.X++ } widths = widths[:0] wordwidth = 0 if vloc.VisualX >= w.bufWidth { vloc.Row++ vloc.VisualX = 0 } } return loc } func (w *BufWindow) getRowCount(line int) int { eol := buffer.Loc{X: util.CharacterCount(w.Buf.LineBytes(line)), Y: line} return w.getVLocFromLoc(eol).Row + 1 } func (w *BufWindow) scrollUp(s SLoc, n int) SLoc { for n > 0 { if n <= s.Row { s.Row -= n n = 0 } else if s.Line > 0 { s.Line-- n -= s.Row + 1 s.Row = w.getRowCount(s.Line) - 1 } else { s.Row = 0 break } } return s } func (w *BufWindow) scrollDown(s SLoc, n int) SLoc { for n > 0 { rc := w.getRowCount(s.Line) if n < rc-s.Row { s.Row += n n = 0 } else if s.Line < w.Buf.LinesNum()-1 { s.Line++ n -= rc - s.Row s.Row = 0 } else { s.Row = rc - 1 break } } return s } func (w *BufWindow) scroll(s SLoc, n int) SLoc { if n < 0 { return w.scrollUp(s, -n) } return w.scrollDown(s, n) } func (w *BufWindow) diff(s1, s2 SLoc) int { n := 0 for s1.LessThan(s2) { if s1.Line < s2.Line { n += w.getRowCount(s1.Line) - s1.Row s1.Line++ s1.Row = 0 } else { n += s2.Row - s1.Row s1.Row = s2.Row } } return n } // Scroll returns the location which is n visual lines below the location s // i.e. the result of scrolling n lines down. n can be negative, // which means scrolling up. The returned location is guaranteed to be // within the buffer boundaries. func (w *BufWindow) Scroll(s SLoc, n int) SLoc { if !w.Buf.Settings["softwrap"].(bool) { s.Line += n if s.Line < 0 { s.Line = 0 } if s.Line > w.Buf.LinesNum()-1 { s.Line = w.Buf.LinesNum() - 1 } return s } return w.scroll(s, n) } // Diff returns the difference (the vertical distance) between two SLocs. func (w *BufWindow) Diff(s1, s2 SLoc) int { if !w.Buf.Settings["softwrap"].(bool) { return s2.Line - s1.Line } if s1.GreaterThan(s2) { return -w.diff(s2, s1) } return w.diff(s1, s2) } // SLocFromLoc takes a position in the buffer and returns the location // of the visual line containing this position. func (w *BufWindow) SLocFromLoc(loc buffer.Loc) SLoc { if !w.Buf.Settings["softwrap"].(bool) { return SLoc{loc.Y, 0} } return w.getVLocFromLoc(loc).SLoc } // VLocFromLoc takes a position in the buffer and returns the corresponding // visual location in the linewrapped buffer. func (w *BufWindow) VLocFromLoc(loc buffer.Loc) VLoc { if !w.Buf.Settings["softwrap"].(bool) { tabsize := util.IntOpt(w.Buf.Settings["tabsize"]) visualx := util.StringWidth(w.Buf.LineBytes(loc.Y), loc.X, tabsize) return VLoc{SLoc{loc.Y, 0}, visualx} } return w.getVLocFromLoc(loc) } // LocFromVLoc takes a visual location in the linewrapped buffer and returns // the position in the buffer corresponding to this visual location. func (w *BufWindow) LocFromVLoc(vloc VLoc) buffer.Loc { if !w.Buf.Settings["softwrap"].(bool) { tabsize := util.IntOpt(w.Buf.Settings["tabsize"]) x := util.GetCharPosInLine(w.Buf.LineBytes(vloc.Line), vloc.VisualX, tabsize) return buffer.Loc{x, vloc.Line} } return w.getLocFromVLoc(vloc) } micro-2.0.14/internal/display/statusline.go0000664000175000017510000001250414663411671020256 0ustar nileshnileshpackage display import ( "bytes" "fmt" "regexp" "strconv" "strings" luar "layeh.com/gopher-luar" runewidth "github.com/mattn/go-runewidth" lua "github.com/yuin/gopher-lua" "github.com/zyedidia/micro/v2/internal/buffer" "github.com/zyedidia/micro/v2/internal/config" ulua "github.com/zyedidia/micro/v2/internal/lua" "github.com/zyedidia/micro/v2/internal/screen" "github.com/zyedidia/micro/v2/internal/util" ) // StatusLine represents the information line at the bottom // of each window // It gives information such as filename, whether the file has been // modified, filetype, cursor location type StatusLine struct { Info map[string]func(*buffer.Buffer) string win *BufWindow } var statusInfo = map[string]func(*buffer.Buffer) string{ "filename": func(b *buffer.Buffer) string { return b.GetName() }, "line": func(b *buffer.Buffer) string { return strconv.Itoa(b.GetActiveCursor().Y + 1) }, "col": func(b *buffer.Buffer) string { return strconv.Itoa(b.GetActiveCursor().X + 1) }, "modified": func(b *buffer.Buffer) string { if b.Modified() { return "+ " } if b.Type.Readonly { return "[ro] " } return "" }, "lines": func(b *buffer.Buffer) string { return strconv.Itoa(b.LinesNum()) }, "percentage": func(b *buffer.Buffer) string { return strconv.Itoa((b.GetActiveCursor().Y + 1) * 100 / b.LinesNum()) }, } func SetStatusInfoFnLua(fn string) { luaFn := strings.Split(fn, ".") if len(luaFn) <= 1 { return } plName, plFn := luaFn[0], luaFn[1] pl := config.FindPlugin(plName) if pl == nil { return } statusInfo[fn] = func(b *buffer.Buffer) string { if pl == nil || !pl.IsLoaded() { return "" } val, err := pl.Call(plFn, luar.New(ulua.L, b)) if err == nil { if v, ok := val.(lua.LString); !ok { screen.TermMessage(plFn, "should return a string") return "" } else { return string(v) } } return "" } } // NewStatusLine returns a statusline bound to a window func NewStatusLine(win *BufWindow) *StatusLine { s := new(StatusLine) s.win = win return s } // FindOpt finds a given option in the current buffer's settings func (s *StatusLine) FindOpt(opt string) interface{} { if val, ok := s.win.Buf.Settings[opt]; ok { return val } return "null" } var formatParser = regexp.MustCompile(`\$\(.+?\)`) // Display draws the statusline to the screen func (s *StatusLine) Display() { // We'll draw the line at the lowest line in the window y := s.win.Height + s.win.Y - 1 winX := s.win.X b := s.win.Buf // autocomplete suggestions (for the buffer, not for the infowindow) if b.HasSuggestions && len(b.Suggestions) > 1 { statusLineStyle := config.DefStyle.Reverse(true) if style, ok := config.Colorscheme["statusline.suggestions"]; ok { statusLineStyle = style } else if style, ok := config.Colorscheme["statusline"]; ok { statusLineStyle = style } x := 0 for j, sug := range b.Suggestions { style := statusLineStyle if b.CurSuggestion == j { style = style.Reverse(true) } for _, r := range sug { screen.SetContent(winX+x, y, r, nil, style) x++ if x >= s.win.Width { return } } screen.SetContent(winX+x, y, ' ', nil, statusLineStyle) x++ if x >= s.win.Width { return } } for x < s.win.Width { screen.SetContent(winX+x, y, ' ', nil, statusLineStyle) x++ } return } formatter := func(match []byte) []byte { name := match[2 : len(match)-1] if bytes.HasPrefix(name, []byte("opt")) { option := name[4:] return []byte(fmt.Sprint(s.FindOpt(string(option)))) } else if bytes.HasPrefix(name, []byte("bind")) { binding := string(name[5:]) for k, v := range config.Bindings["buffer"] { if v == binding { return []byte(k) } } return []byte("null") } else { if fn, ok := statusInfo[string(name)]; ok { return []byte(fn(s.win.Buf)) } return []byte{} } } leftText := []byte(s.win.Buf.Settings["statusformatl"].(string)) leftText = formatParser.ReplaceAllFunc(leftText, formatter) rightText := []byte(s.win.Buf.Settings["statusformatr"].(string)) rightText = formatParser.ReplaceAllFunc(rightText, formatter) statusLineStyle := config.DefStyle.Reverse(true) if s.win.IsActive() { if style, ok := config.Colorscheme["statusline"]; ok { statusLineStyle = style } } else { if style, ok := config.Colorscheme["statusline.inactive"]; ok { statusLineStyle = style } else if style, ok := config.Colorscheme["statusline"]; ok { statusLineStyle = style } } leftLen := util.StringWidth(leftText, util.CharacterCount(leftText), 1) rightLen := util.StringWidth(rightText, util.CharacterCount(rightText), 1) for x := 0; x < s.win.Width; x++ { if x < leftLen { r, combc, size := util.DecodeCharacter(leftText) leftText = leftText[size:] rw := runewidth.RuneWidth(r) for j := 0; j < rw; j++ { c := r if j > 0 { c = ' ' combc = nil x++ } screen.SetContent(winX+x, y, c, combc, statusLineStyle) } } else if x >= s.win.Width-rightLen && x < rightLen+s.win.Width-rightLen { r, combc, size := util.DecodeCharacter(rightText) rightText = rightText[size:] rw := runewidth.RuneWidth(r) for j := 0; j < rw; j++ { c := r if j > 0 { c = ' ' combc = nil x++ } screen.SetContent(winX+x, y, c, combc, statusLineStyle) } } else { screen.SetContent(winX+x, y, ' ', nil, statusLineStyle) } } } micro-2.0.14/internal/display/tabwindow.go0000664000175000017510000000704614663411671020066 0ustar nileshnileshpackage display import ( runewidth "github.com/mattn/go-runewidth" "github.com/zyedidia/tcell/v2" "github.com/zyedidia/micro/v2/internal/buffer" "github.com/zyedidia/micro/v2/internal/config" "github.com/zyedidia/micro/v2/internal/screen" "github.com/zyedidia/micro/v2/internal/util" ) type TabWindow struct { Names []string active int Y int Width int hscroll int } func NewTabWindow(w int, y int) *TabWindow { tw := new(TabWindow) tw.Width = w tw.Y = y return tw } func (w *TabWindow) Resize(width, height int) { w.Width = width } func (w *TabWindow) LocFromVisual(vloc buffer.Loc) int { x := -w.hscroll for i, n := range w.Names { x++ s := util.CharacterCountInString(n) if vloc.Y == w.Y && vloc.X < x+s { return i } x += s x += 3 if x >= w.Width { break } } return -1 } func (w *TabWindow) Scroll(amt int) { w.hscroll += amt s := w.TotalSize() w.hscroll = util.Clamp(w.hscroll, 0, s-w.Width) if s-w.Width <= 0 { w.hscroll = 0 } } func (w *TabWindow) TotalSize() int { sum := 2 for _, n := range w.Names { sum += runewidth.StringWidth(n) + 4 } return sum - 4 } func (w *TabWindow) Active() int { return w.active } func (w *TabWindow) SetActive(a int) { w.active = a x := 2 s := w.TotalSize() for i, n := range w.Names { c := util.CharacterCountInString(n) if i == a { if x+c >= w.hscroll+w.Width { w.hscroll = util.Clamp(x+c+1-w.Width, 0, s-w.Width) } else if x < w.hscroll { w.hscroll = util.Clamp(x-4, 0, s-w.Width) } break } x += c + 4 } if s-w.Width <= 0 { w.hscroll = 0 } } func (w *TabWindow) Display() { x := -w.hscroll done := false globalTabReverse := config.GetGlobalOption("tabreverse").(bool) globalTabHighlight := config.GetGlobalOption("tabhighlight").(bool) // xor of reverse and tab highlight to get tab character (as in filename and surrounding characters) reverse state tabCharHighlight := (globalTabReverse || globalTabHighlight) && !(globalTabReverse && globalTabHighlight) reverseStyles := func(reverse bool) (tcell.Style, tcell.Style) { tabBarStyle := config.DefStyle.Reverse(reverse) if style, ok := config.Colorscheme["tabbar"]; ok { tabBarStyle = style } tabBarActiveStyle := tabBarStyle if style, ok := config.Colorscheme["tabbar.active"]; ok { tabBarActiveStyle = style } return tabBarStyle, tabBarActiveStyle } draw := func(r rune, n int, active bool, reversed bool) { tabBarStyle, tabBarActiveStyle := reverseStyles(reversed) style := tabBarStyle if active { style = tabBarActiveStyle } for i := 0; i < n; i++ { rw := runewidth.RuneWidth(r) for j := 0; j < rw; j++ { c := r if j > 0 { c = ' ' } if x == w.Width-1 && !done { screen.SetContent(w.Width-1, w.Y, '>', nil, tabBarStyle) x++ break } else if x == 0 && w.hscroll > 0 { screen.SetContent(0, w.Y, '<', nil, tabBarStyle) } else if x >= 0 && x < w.Width { screen.SetContent(x, w.Y, c, nil, style) } x++ } } } for i, n := range w.Names { if i == w.active { draw('[', 1, true, tabCharHighlight) } else { draw(' ', 1, false, tabCharHighlight) } for _, c := range n { draw(c, 1, i == w.active, tabCharHighlight) } if i == len(w.Names)-1 { done = true } if i == w.active { draw(']', 1, true, tabCharHighlight) draw(' ', 2, true, globalTabReverse) } else { draw(' ', 1, false, tabCharHighlight) draw(' ', 2, false, globalTabReverse) } if x >= w.Width { break } } if x < w.Width { draw(' ', w.Width-x, false, globalTabReverse) } } micro-2.0.14/internal/display/termwindow.go0000664000175000017510000000535214663411671020265 0ustar nileshnileshpackage display import ( "github.com/zyedidia/micro/v2/internal/buffer" "github.com/zyedidia/micro/v2/internal/config" "github.com/zyedidia/micro/v2/internal/screen" "github.com/zyedidia/micro/v2/internal/shell" "github.com/zyedidia/micro/v2/internal/util" "github.com/zyedidia/tcell/v2" "github.com/zyedidia/terminal" ) type TermWindow struct { *View *shell.Terminal active bool } func NewTermWindow(x, y, w, h int, term *shell.Terminal) *TermWindow { tw := new(TermWindow) tw.View = new(View) tw.Terminal = term tw.X, tw.Y = x, y tw.Resize(w, h) return tw } // Resize informs the terminal of a resize event func (w *TermWindow) Resize(width, height int) { if config.GetGlobalOption("statusline").(bool) { height-- } w.Term.Resize(width, height) w.Width, w.Height = width, height } func (w *TermWindow) SetActive(b bool) { w.active = b } func (w *TermWindow) IsActive() bool { return w.active } func (w *TermWindow) LocFromVisual(vloc buffer.Loc) buffer.Loc { return vloc } func (w *TermWindow) Clear() { for y := 0; y < w.Height; y++ { for x := 0; x < w.Width; x++ { screen.SetContent(w.X+x, w.Y+y, ' ', nil, config.DefStyle) } } } func (w *TermWindow) Relocate() bool { return true } func (w *TermWindow) GetView() *View { return w.View } func (w *TermWindow) SetView(v *View) { w.View = v } // Display displays this terminal in a view func (w *TermWindow) Display() { w.State.Lock() defer w.State.Unlock() var l buffer.Loc for y := 0; y < w.Height; y++ { for x := 0; x < w.Width; x++ { l.X, l.Y = x, y c, f, b := w.State.Cell(x, y) fg, bg := int(f), int(b) if f == terminal.DefaultFG { fg = int(tcell.ColorDefault) } if b == terminal.DefaultBG { bg = int(tcell.ColorDefault) } st := tcell.StyleDefault.Foreground(config.GetColor256(fg)).Background(config.GetColor256(bg)) if l.LessThan(w.Selection[1]) && l.GreaterEqual(w.Selection[0]) || l.LessThan(w.Selection[0]) && l.GreaterEqual(w.Selection[1]) { st = st.Reverse(true) } screen.SetContent(w.X+x, w.Y+y, c, nil, st) } } if config.GetGlobalOption("statusline").(bool) { statusLineStyle := config.DefStyle.Reverse(true) if style, ok := config.Colorscheme["statusline"]; ok { statusLineStyle = style } text := []byte(w.Name()) textLen := util.CharacterCount(text) for x := 0; x < w.Width; x++ { if x < textLen { r, combc, size := util.DecodeCharacter(text) text = text[size:] screen.SetContent(w.X+x, w.Y+w.Height, r, combc, statusLineStyle) } else { screen.SetContent(w.X+x, w.Y+w.Height, ' ', nil, statusLineStyle) } } } if w.State.CursorVisible() && w.active { curx, cury := w.State.Cursor() if curx < w.Width && cury < w.Height { screen.ShowCursor(curx+w.X, cury+w.Y) } } } micro-2.0.14/internal/display/uiwindow.go0000664000175000017510000000356014663411671017732 0ustar nileshnileshpackage display import ( "github.com/zyedidia/micro/v2/internal/buffer" "github.com/zyedidia/micro/v2/internal/config" "github.com/zyedidia/micro/v2/internal/screen" "github.com/zyedidia/micro/v2/internal/util" "github.com/zyedidia/micro/v2/internal/views" ) type UIWindow struct { root *views.Node } func NewUIWindow(n *views.Node) *UIWindow { uw := new(UIWindow) uw.root = n return uw } func (w *UIWindow) drawNode(n *views.Node) { cs := n.Children() dividerStyle := config.DefStyle if style, ok := config.Colorscheme["divider"]; ok { dividerStyle = style } divchars := config.GetGlobalOption("divchars").(string) if util.CharacterCountInString(divchars) != 2 { divchars = "|-" } divchar, combc, _ := util.DecodeCharacterInString(divchars) divreverse := config.GetGlobalOption("divreverse").(bool) if divreverse { dividerStyle = dividerStyle.Reverse(true) } for i, c := range cs { if c.Kind == views.STVert { if i != len(cs)-1 { for h := 0; h < c.H; h++ { screen.SetContent(c.X+c.W, c.Y+h, divchar, combc, dividerStyle) } } } w.drawNode(c) } } func (w *UIWindow) Display() { w.drawNode(w.root) } func (w *UIWindow) GetMouseSplitNode(vloc buffer.Loc) *views.Node { var mouseLoc func(*views.Node) *views.Node mouseLoc = func(n *views.Node) *views.Node { cs := n.Children() for i, c := range cs { if c.Kind == views.STVert { if i != len(cs)-1 { if vloc.X == c.X+c.W && vloc.Y >= c.Y && vloc.Y < c.Y+c.H { return c } } } else if c.Kind == views.STHoriz { if i != len(cs)-1 { if vloc.Y == c.Y+c.H-1 && vloc.X >= c.X && vloc.X < c.X+c.W { return c } } } } for _, c := range cs { m := mouseLoc(c) if m != nil { return m } } return nil } return mouseLoc(w.root) } func (w *UIWindow) Resize(width, height int) {} func (w *UIWindow) SetActive(b bool) {} micro-2.0.14/internal/display/window.go0000664000175000017510000000140614663411671017371 0ustar nileshnileshpackage display import ( "github.com/zyedidia/micro/v2/internal/buffer" ) type View struct { X, Y int // X,Y location of the view Width, Height int // Width and height of the view // Start line of the view (for vertical scroll) StartLine SLoc // Start column of the view (for horizontal scroll) // note that since the starting column of every line is different if the view // is scrolled, StartCol is a visual index (will be the same for every line) StartCol int } type Window interface { Display() Clear() Relocate() bool GetView() *View SetView(v *View) LocFromVisual(vloc buffer.Loc) buffer.Loc Resize(w, h int) SetActive(b bool) IsActive() bool } type BWindow interface { Window SoftWrap SetBuffer(b *buffer.Buffer) BufView() View } micro-2.0.14/internal/info/0000775000175000017510000000000014663411671015020 5ustar nileshnileshmicro-2.0.14/internal/info/gutter.go0000664000175000017510000000061514663411671016663 0ustar nileshnileshpackage info // A GutterMessage is a message displayed on the side of the editor type GutterMessage struct { lineNum int msg string kind int } // These are the different types of messages const ( // GutterInfo represents a simple info message GutterInfo = iota // GutterWarning represents a compiler warning GutterWarning // GutterError represents a compiler error GutterError ) micro-2.0.14/internal/info/history.go0000664000175000017510000000761414663411671017060 0ustar nileshnileshpackage info import ( "encoding/gob" "os" "path/filepath" "strings" "github.com/zyedidia/micro/v2/internal/config" "github.com/zyedidia/micro/v2/internal/util" ) // LoadHistory attempts to load user history from configDir/buffers/history // into the history map // The savehistory option must be on func (i *InfoBuf) LoadHistory() { if config.GetGlobalOption("savehistory").(bool) { file, err := os.Open(filepath.Join(config.ConfigDir, "buffers", "history")) var decodedMap map[string][]string if err == nil { defer file.Close() decoder := gob.NewDecoder(file) err = decoder.Decode(&decodedMap) if err != nil { i.Error("Error loading history:", err) return } } if decodedMap != nil { i.History = decodedMap } else { i.History = make(map[string][]string) } } else { i.History = make(map[string][]string) } } // SaveHistory saves the user's command history to configDir/buffers/history // only if the savehistory option is on func (i *InfoBuf) SaveHistory() { if config.GetGlobalOption("savehistory").(bool) { // Don't save history past 100 for k, v := range i.History { if len(v) > 100 { i.History[k] = v[len(i.History[k])-100:] } } file, err := os.Create(filepath.Join(config.ConfigDir, "buffers", "history")) if err == nil { defer file.Close() encoder := gob.NewEncoder(file) err = encoder.Encode(i.History) if err != nil { i.Error("Error saving history:", err) return } } } } // AddToHistory adds a new item to the history for the prompt type `ptype`. // This function is not used by micro itself. It is useful for plugins // which add their own items to the history, bypassing the infobar command line. func (i *InfoBuf) AddToHistory(ptype string, item string) { if i.HasPrompt && i.PromptType == ptype { return } if _, ok := i.History[ptype]; !ok { i.History[ptype] = []string{item} } else { i.History[ptype] = append(i.History[ptype], item) // avoid duplicates h := i.History[ptype] for j := len(h) - 2; j >= 0; j-- { if h[j] == h[len(h)-1] { i.History[ptype] = append(h[:j], h[j+1:]...) break } } } } // UpHistory fetches the previous item in the history func (i *InfoBuf) UpHistory(history []string) { if i.HistoryNum > 0 && i.HasPrompt && !i.HasYN { i.HistoryNum-- i.Replace(i.Start(), i.End(), history[i.HistoryNum]) i.Buffer.GetActiveCursor().GotoLoc(i.End()) } } // DownHistory fetches the next item in the history func (i *InfoBuf) DownHistory(history []string) { if i.HistoryNum < len(history)-1 && i.HasPrompt && !i.HasYN { i.HistoryNum++ i.Replace(i.Start(), i.End(), history[i.HistoryNum]) i.Buffer.GetActiveCursor().GotoLoc(i.End()) } } // SearchUpHistory fetches the previous item in the history // beginning with the text in the infobuffer before cursor func (i *InfoBuf) SearchUpHistory(history []string) { if i.HistoryNum > 0 && i.HasPrompt && !i.HasYN { i.searchHistory(history, false) } } // SearchDownHistory fetches the next item in the history // beginning with the text in the infobuffer before cursor func (i *InfoBuf) SearchDownHistory(history []string) { if i.HistoryNum < len(history)-1 && i.HasPrompt && !i.HasYN { i.searchHistory(history, true) } } func (i *InfoBuf) searchHistory(history []string, down bool) { line := string(i.LineBytes(0)) c := i.Buffer.GetActiveCursor() if !i.HistorySearch || !strings.HasPrefix(line, i.HistorySearchPrefix) { i.HistorySearch = true i.HistorySearchPrefix = util.SliceStartStr(line, c.X) } found := -1 if down { for j := i.HistoryNum + 1; j < len(history); j++ { if strings.HasPrefix(history[j], i.HistorySearchPrefix) { found = j break } } } else { for j := i.HistoryNum - 1; j >= 0; j-- { if strings.HasPrefix(history[j], i.HistorySearchPrefix) { found = j break } } } if found != -1 { i.HistoryNum = found i.Replace(i.Start(), i.End(), history[found]) c.GotoLoc(i.End()) } } micro-2.0.14/internal/info/infobuffer.go0000664000175000017510000001136214663411671017477 0ustar nileshnileshpackage info import ( "fmt" "github.com/zyedidia/micro/v2/internal/buffer" ) // The InfoBuf displays messages and other info at the bottom of the screen. // It is represented as a buffer and a message with a style. type InfoBuf struct { *buffer.Buffer HasPrompt bool HasMessage bool HasError bool HasYN bool PromptType string Msg string YNResp bool // This map stores the history for all the different kinds of uses Prompt has // It's a map of history type -> history array History map[string][]string HistoryNum int // HistorySearch indicates whether we are searching for history items // beginning with HistorySearchPrefix HistorySearch bool HistorySearchPrefix string // Is the current message a message from the gutter HasGutter bool PromptCallback func(resp string, canceled bool) EventCallback func(resp string) YNCallback func(yes bool, canceled bool) } // NewBuffer returns a new infobuffer func NewBuffer() *InfoBuf { ib := new(InfoBuf) ib.History = make(map[string][]string) ib.Buffer = buffer.NewBufferFromString("", "", buffer.BTInfo) ib.LoadHistory() return ib } // Close performs any cleanup necessary when shutting down the infobuffer func (i *InfoBuf) Close() { i.SaveHistory() } // Message sends a message to the user func (i *InfoBuf) Message(msg ...interface{}) { // only display a new message if there isn't an active prompt // this is to prevent overwriting an existing prompt to the user if !i.HasPrompt { displayMessage := fmt.Sprint(msg...) // if there is no active prompt then style and display the message as normal i.Msg = displayMessage i.HasMessage, i.HasError = true, false } } // GutterMessage displays a message and marks it as a gutter message func (i *InfoBuf) GutterMessage(msg ...interface{}) { i.Message(msg...) i.HasGutter = true } // ClearGutter clears the info bar and unmarks the message func (i *InfoBuf) ClearGutter() { i.HasGutter = false i.Message("") } // Error sends an error message to the user func (i *InfoBuf) Error(msg ...interface{}) { // only display a new message if there isn't an active prompt // this is to prevent overwriting an existing prompt to the user if !i.HasPrompt { // if there is no active prompt then style and display the message as normal i.Msg = fmt.Sprint(msg...) i.HasMessage, i.HasError = false, true } // TODO: add to log? } // Prompt starts a prompt for the user, it takes a prompt, a possibly partially filled in msg // and callbacks executed when the user executes an event and when the user finishes the prompt // The eventcb passes the current user response as the argument and donecb passes the user's message // and a boolean indicating if the prompt was canceled func (i *InfoBuf) Prompt(prompt string, msg string, ptype string, eventcb func(string), donecb func(string, bool)) { // If we get another prompt mid-prompt we cancel the one getting overwritten if i.HasPrompt { i.DonePrompt(true) } if _, ok := i.History[ptype]; !ok { i.History[ptype] = []string{""} } else { i.History[ptype] = append(i.History[ptype], "") } i.HistoryNum = len(i.History[ptype]) - 1 i.HistorySearch = false i.PromptType = ptype i.Msg = prompt i.HasPrompt = true i.HasMessage, i.HasError, i.HasYN = false, false, false i.HasGutter = false i.PromptCallback = donecb i.EventCallback = eventcb i.Buffer.Insert(i.Buffer.Start(), msg) } // YNPrompt creates a yes or no prompt, and the callback returns the yes/no result and whether // the prompt was canceled func (i *InfoBuf) YNPrompt(prompt string, donecb func(bool, bool)) { if i.HasPrompt { i.DonePrompt(true) } i.Msg = prompt i.HasPrompt = true i.HasYN = true i.HasMessage, i.HasError = false, false i.HasGutter = false i.YNCallback = donecb } // DonePrompt finishes the current prompt and indicates whether or not it was canceled func (i *InfoBuf) DonePrompt(canceled bool) { hadYN := i.HasYN i.HasPrompt = false i.HasYN = false i.HasGutter = false if !hadYN { if i.PromptCallback != nil { if canceled { i.Replace(i.Start(), i.End(), "") h := i.History[i.PromptType] i.History[i.PromptType] = h[:len(h)-1] i.PromptCallback("", true) } else { resp := string(i.LineBytes(0)) i.Replace(i.Start(), i.End(), "") h := i.History[i.PromptType] h[len(h)-1] = resp // avoid duplicates for j := len(h) - 2; j >= 0; j-- { if h[j] == h[len(h)-1] { i.History[i.PromptType] = append(h[:j], h[j+1:]...) break } } i.PromptCallback(resp, false) } // i.PromptCallback = nil } } if i.YNCallback != nil && hadYN { i.YNCallback(i.YNResp, canceled) } } // Reset resets the infobuffer's msg and info func (i *InfoBuf) Reset() { i.Msg = "" i.HasPrompt, i.HasMessage, i.HasError = false, false, false i.HasGutter = false } micro-2.0.14/internal/lua/0000775000175000017510000000000014663411671014646 5ustar nileshnileshmicro-2.0.14/internal/lua/lua.go0000664000175000017510000006055614663411671015772 0ustar nileshnileshpackage lua import ( "archive/zip" "bytes" "errors" "fmt" "io" "io/ioutil" "math" "math/rand" "net" "net/http" "os" "path" "path/filepath" "regexp" "runtime" "strings" "time" "unicode/utf8" humanize "github.com/dustin/go-humanize" lua "github.com/yuin/gopher-lua" luar "layeh.com/gopher-luar" ) var L *lua.LState // LoadFile loads a lua file func LoadFile(module string, file string, data []byte) error { pluginDef := []byte("module(\"" + module + "\", package.seeall)") if fn, err := L.Load(bytes.NewReader(append(pluginDef, data...)), file); err != nil { return err } else { L.Push(fn) return L.PCall(0, lua.MultRet, nil) } } // Import allows a lua plugin to import a package func Import(pkg string) *lua.LTable { switch pkg { case "fmt": return importFmt() case "io": return importIo() case "io/ioutil", "ioutil": return importIoUtil() case "net": return importNet() case "math": return importMath() case "math/rand": return importMathRand() case "os": return importOs() case "runtime": return importRuntime() case "path": return importPath() case "path/filepath", "filepath": return importFilePath() case "strings": return importStrings() case "regexp": return importRegexp() case "errors": return importErrors() case "time": return importTime() case "unicode/utf8", "utf8": return importUtf8() case "humanize": return importHumanize() case "net/http", "http": return importHTTP() case "archive/zip": return importArchiveZip() default: return nil } } func importFmt() *lua.LTable { pkg := L.NewTable() L.SetField(pkg, "Errorf", luar.New(L, fmt.Errorf)) L.SetField(pkg, "Fprint", luar.New(L, fmt.Fprint)) L.SetField(pkg, "Fprintf", luar.New(L, fmt.Fprintf)) L.SetField(pkg, "Fprintln", luar.New(L, fmt.Fprintln)) L.SetField(pkg, "Fscan", luar.New(L, fmt.Fscan)) L.SetField(pkg, "Fscanf", luar.New(L, fmt.Fscanf)) L.SetField(pkg, "Fscanln", luar.New(L, fmt.Fscanln)) L.SetField(pkg, "Print", luar.New(L, fmt.Print)) L.SetField(pkg, "Printf", luar.New(L, fmt.Printf)) L.SetField(pkg, "Println", luar.New(L, fmt.Println)) L.SetField(pkg, "Scan", luar.New(L, fmt.Scan)) L.SetField(pkg, "Scanf", luar.New(L, fmt.Scanf)) L.SetField(pkg, "Scanln", luar.New(L, fmt.Scanln)) L.SetField(pkg, "Sprint", luar.New(L, fmt.Sprint)) L.SetField(pkg, "Sprintf", luar.New(L, fmt.Sprintf)) L.SetField(pkg, "Sprintln", luar.New(L, fmt.Sprintln)) L.SetField(pkg, "Sscan", luar.New(L, fmt.Sscan)) L.SetField(pkg, "Sscanf", luar.New(L, fmt.Sscanf)) L.SetField(pkg, "Sscanln", luar.New(L, fmt.Sscanln)) return pkg } func importIo() *lua.LTable { pkg := L.NewTable() L.SetField(pkg, "Copy", luar.New(L, io.Copy)) L.SetField(pkg, "CopyN", luar.New(L, io.CopyN)) L.SetField(pkg, "EOF", luar.New(L, io.EOF)) L.SetField(pkg, "ErrClosedPipe", luar.New(L, io.ErrClosedPipe)) L.SetField(pkg, "ErrNoProgress", luar.New(L, io.ErrNoProgress)) L.SetField(pkg, "ErrShortBuffer", luar.New(L, io.ErrShortBuffer)) L.SetField(pkg, "ErrShortWrite", luar.New(L, io.ErrShortWrite)) L.SetField(pkg, "ErrUnexpectedEOF", luar.New(L, io.ErrUnexpectedEOF)) L.SetField(pkg, "LimitReader", luar.New(L, io.LimitReader)) L.SetField(pkg, "MultiReader", luar.New(L, io.MultiReader)) L.SetField(pkg, "MultiWriter", luar.New(L, io.MultiWriter)) L.SetField(pkg, "NewSectionReader", luar.New(L, io.NewSectionReader)) L.SetField(pkg, "Pipe", luar.New(L, io.Pipe)) L.SetField(pkg, "ReadAtLeast", luar.New(L, io.ReadAtLeast)) L.SetField(pkg, "ReadFull", luar.New(L, io.ReadFull)) L.SetField(pkg, "TeeReader", luar.New(L, io.TeeReader)) L.SetField(pkg, "WriteString", luar.New(L, io.WriteString)) return pkg } func importIoUtil() *lua.LTable { pkg := L.NewTable() L.SetField(pkg, "ReadAll", luar.New(L, ioutil.ReadAll)) L.SetField(pkg, "ReadDir", luar.New(L, ioutil.ReadDir)) L.SetField(pkg, "ReadFile", luar.New(L, ioutil.ReadFile)) L.SetField(pkg, "WriteFile", luar.New(L, ioutil.WriteFile)) return pkg } func importNet() *lua.LTable { pkg := L.NewTable() L.SetField(pkg, "CIDRMask", luar.New(L, net.CIDRMask)) L.SetField(pkg, "Dial", luar.New(L, net.Dial)) L.SetField(pkg, "DialIP", luar.New(L, net.DialIP)) L.SetField(pkg, "DialTCP", luar.New(L, net.DialTCP)) L.SetField(pkg, "DialTimeout", luar.New(L, net.DialTimeout)) L.SetField(pkg, "DialUDP", luar.New(L, net.DialUDP)) L.SetField(pkg, "DialUnix", luar.New(L, net.DialUnix)) L.SetField(pkg, "ErrWriteToConnected", luar.New(L, net.ErrWriteToConnected)) L.SetField(pkg, "FileConn", luar.New(L, net.FileConn)) L.SetField(pkg, "FileListener", luar.New(L, net.FileListener)) L.SetField(pkg, "FilePacketConn", luar.New(L, net.FilePacketConn)) L.SetField(pkg, "FlagBroadcast", luar.New(L, net.FlagBroadcast)) L.SetField(pkg, "FlagLoopback", luar.New(L, net.FlagLoopback)) L.SetField(pkg, "FlagMulticast", luar.New(L, net.FlagMulticast)) L.SetField(pkg, "FlagPointToPoint", luar.New(L, net.FlagPointToPoint)) L.SetField(pkg, "FlagUp", luar.New(L, net.FlagUp)) L.SetField(pkg, "IPv4", luar.New(L, net.IPv4)) L.SetField(pkg, "IPv4Mask", luar.New(L, net.IPv4Mask)) L.SetField(pkg, "IPv4allrouter", luar.New(L, net.IPv4allrouter)) L.SetField(pkg, "IPv4allsys", luar.New(L, net.IPv4allsys)) L.SetField(pkg, "IPv4bcast", luar.New(L, net.IPv4bcast)) L.SetField(pkg, "IPv4len", luar.New(L, net.IPv4len)) L.SetField(pkg, "IPv4zero", luar.New(L, net.IPv4zero)) L.SetField(pkg, "IPv6interfacelocalallnodes", luar.New(L, net.IPv6interfacelocalallnodes)) L.SetField(pkg, "IPv6len", luar.New(L, net.IPv6len)) L.SetField(pkg, "IPv6linklocalallnodes", luar.New(L, net.IPv6linklocalallnodes)) L.SetField(pkg, "IPv6linklocalallrouters", luar.New(L, net.IPv6linklocalallrouters)) L.SetField(pkg, "IPv6loopback", luar.New(L, net.IPv6loopback)) L.SetField(pkg, "IPv6unspecified", luar.New(L, net.IPv6unspecified)) L.SetField(pkg, "IPv6zero", luar.New(L, net.IPv6zero)) L.SetField(pkg, "InterfaceAddrs", luar.New(L, net.InterfaceAddrs)) L.SetField(pkg, "InterfaceByIndex", luar.New(L, net.InterfaceByIndex)) L.SetField(pkg, "InterfaceByName", luar.New(L, net.InterfaceByName)) L.SetField(pkg, "Interfaces", luar.New(L, net.Interfaces)) L.SetField(pkg, "JoinHostPort", luar.New(L, net.JoinHostPort)) L.SetField(pkg, "Listen", luar.New(L, net.Listen)) L.SetField(pkg, "ListenIP", luar.New(L, net.ListenIP)) L.SetField(pkg, "ListenMulticastUDP", luar.New(L, net.ListenMulticastUDP)) L.SetField(pkg, "ListenPacket", luar.New(L, net.ListenPacket)) L.SetField(pkg, "ListenTCP", luar.New(L, net.ListenTCP)) L.SetField(pkg, "ListenUDP", luar.New(L, net.ListenUDP)) L.SetField(pkg, "ListenUnix", luar.New(L, net.ListenUnix)) L.SetField(pkg, "ListenUnixgram", luar.New(L, net.ListenUnixgram)) L.SetField(pkg, "LookupAddr", luar.New(L, net.LookupAddr)) L.SetField(pkg, "LookupCNAME", luar.New(L, net.LookupCNAME)) L.SetField(pkg, "LookupHost", luar.New(L, net.LookupHost)) L.SetField(pkg, "LookupIP", luar.New(L, net.LookupIP)) L.SetField(pkg, "LookupMX", luar.New(L, net.LookupMX)) L.SetField(pkg, "LookupNS", luar.New(L, net.LookupNS)) L.SetField(pkg, "LookupPort", luar.New(L, net.LookupPort)) L.SetField(pkg, "LookupSRV", luar.New(L, net.LookupSRV)) L.SetField(pkg, "LookupTXT", luar.New(L, net.LookupTXT)) L.SetField(pkg, "ParseCIDR", luar.New(L, net.ParseCIDR)) L.SetField(pkg, "ParseIP", luar.New(L, net.ParseIP)) L.SetField(pkg, "ParseMAC", luar.New(L, net.ParseMAC)) L.SetField(pkg, "Pipe", luar.New(L, net.Pipe)) L.SetField(pkg, "ResolveIPAddr", luar.New(L, net.ResolveIPAddr)) L.SetField(pkg, "ResolveTCPAddr", luar.New(L, net.ResolveTCPAddr)) L.SetField(pkg, "ResolveUDPAddr", luar.New(L, net.ResolveUDPAddr)) L.SetField(pkg, "ResolveUnixAddr", luar.New(L, net.ResolveUnixAddr)) L.SetField(pkg, "SplitHostPort", luar.New(L, net.SplitHostPort)) return pkg } func importMath() *lua.LTable { pkg := L.NewTable() L.SetField(pkg, "Abs", luar.New(L, math.Abs)) L.SetField(pkg, "Acos", luar.New(L, math.Acos)) L.SetField(pkg, "Acosh", luar.New(L, math.Acosh)) L.SetField(pkg, "Asin", luar.New(L, math.Asin)) L.SetField(pkg, "Asinh", luar.New(L, math.Asinh)) L.SetField(pkg, "Atan", luar.New(L, math.Atan)) L.SetField(pkg, "Atan2", luar.New(L, math.Atan2)) L.SetField(pkg, "Atanh", luar.New(L, math.Atanh)) L.SetField(pkg, "Cbrt", luar.New(L, math.Cbrt)) L.SetField(pkg, "Ceil", luar.New(L, math.Ceil)) L.SetField(pkg, "Copysign", luar.New(L, math.Copysign)) L.SetField(pkg, "Cos", luar.New(L, math.Cos)) L.SetField(pkg, "Cosh", luar.New(L, math.Cosh)) L.SetField(pkg, "Dim", luar.New(L, math.Dim)) L.SetField(pkg, "Erf", luar.New(L, math.Erf)) L.SetField(pkg, "Erfc", luar.New(L, math.Erfc)) L.SetField(pkg, "Exp", luar.New(L, math.Exp)) L.SetField(pkg, "Exp2", luar.New(L, math.Exp2)) L.SetField(pkg, "Expm1", luar.New(L, math.Expm1)) L.SetField(pkg, "Float32bits", luar.New(L, math.Float32bits)) L.SetField(pkg, "Float32frombits", luar.New(L, math.Float32frombits)) L.SetField(pkg, "Float64bits", luar.New(L, math.Float64bits)) L.SetField(pkg, "Float64frombits", luar.New(L, math.Float64frombits)) L.SetField(pkg, "Floor", luar.New(L, math.Floor)) L.SetField(pkg, "Frexp", luar.New(L, math.Frexp)) L.SetField(pkg, "Gamma", luar.New(L, math.Gamma)) L.SetField(pkg, "Hypot", luar.New(L, math.Hypot)) L.SetField(pkg, "Ilogb", luar.New(L, math.Ilogb)) L.SetField(pkg, "Inf", luar.New(L, math.Inf)) L.SetField(pkg, "IsInf", luar.New(L, math.IsInf)) L.SetField(pkg, "IsNaN", luar.New(L, math.IsNaN)) L.SetField(pkg, "J0", luar.New(L, math.J0)) L.SetField(pkg, "J1", luar.New(L, math.J1)) L.SetField(pkg, "Jn", luar.New(L, math.Jn)) L.SetField(pkg, "Ldexp", luar.New(L, math.Ldexp)) L.SetField(pkg, "Lgamma", luar.New(L, math.Lgamma)) L.SetField(pkg, "Log", luar.New(L, math.Log)) L.SetField(pkg, "Log10", luar.New(L, math.Log10)) L.SetField(pkg, "Log1p", luar.New(L, math.Log1p)) L.SetField(pkg, "Log2", luar.New(L, math.Log2)) L.SetField(pkg, "Logb", luar.New(L, math.Logb)) L.SetField(pkg, "Max", luar.New(L, math.Max)) L.SetField(pkg, "Min", luar.New(L, math.Min)) L.SetField(pkg, "Mod", luar.New(L, math.Mod)) L.SetField(pkg, "Modf", luar.New(L, math.Modf)) L.SetField(pkg, "NaN", luar.New(L, math.NaN)) L.SetField(pkg, "Nextafter", luar.New(L, math.Nextafter)) L.SetField(pkg, "Pow", luar.New(L, math.Pow)) L.SetField(pkg, "Pow10", luar.New(L, math.Pow10)) L.SetField(pkg, "Remainder", luar.New(L, math.Remainder)) L.SetField(pkg, "Signbit", luar.New(L, math.Signbit)) L.SetField(pkg, "Sin", luar.New(L, math.Sin)) L.SetField(pkg, "Sincos", luar.New(L, math.Sincos)) L.SetField(pkg, "Sinh", luar.New(L, math.Sinh)) L.SetField(pkg, "Sqrt", luar.New(L, math.Sqrt)) L.SetField(pkg, "Tan", luar.New(L, math.Tan)) L.SetField(pkg, "Tanh", luar.New(L, math.Tanh)) L.SetField(pkg, "Trunc", luar.New(L, math.Trunc)) L.SetField(pkg, "Y0", luar.New(L, math.Y0)) L.SetField(pkg, "Y1", luar.New(L, math.Y1)) L.SetField(pkg, "Yn", luar.New(L, math.Yn)) return pkg } func importMathRand() *lua.LTable { pkg := L.NewTable() L.SetField(pkg, "ExpFloat64", luar.New(L, rand.ExpFloat64)) L.SetField(pkg, "Float32", luar.New(L, rand.Float32)) L.SetField(pkg, "Float64", luar.New(L, rand.Float64)) L.SetField(pkg, "Int", luar.New(L, rand.Int)) L.SetField(pkg, "Int31", luar.New(L, rand.Int31)) L.SetField(pkg, "Int31n", luar.New(L, rand.Int31n)) L.SetField(pkg, "Int63", luar.New(L, rand.Int63)) L.SetField(pkg, "Int63n", luar.New(L, rand.Int63n)) L.SetField(pkg, "Intn", luar.New(L, rand.Intn)) L.SetField(pkg, "NormFloat64", luar.New(L, rand.NormFloat64)) L.SetField(pkg, "Perm", luar.New(L, rand.Perm)) L.SetField(pkg, "Seed", luar.New(L, rand.Seed)) L.SetField(pkg, "Uint32", luar.New(L, rand.Uint32)) return pkg } func importOs() *lua.LTable { pkg := L.NewTable() L.SetField(pkg, "Args", luar.New(L, os.Args)) L.SetField(pkg, "Chdir", luar.New(L, os.Chdir)) L.SetField(pkg, "Chmod", luar.New(L, os.Chmod)) L.SetField(pkg, "Chown", luar.New(L, os.Chown)) L.SetField(pkg, "Chtimes", luar.New(L, os.Chtimes)) L.SetField(pkg, "Clearenv", luar.New(L, os.Clearenv)) L.SetField(pkg, "Create", luar.New(L, os.Create)) L.SetField(pkg, "DevNull", luar.New(L, os.DevNull)) L.SetField(pkg, "Environ", luar.New(L, os.Environ)) L.SetField(pkg, "ErrExist", luar.New(L, os.ErrExist)) L.SetField(pkg, "ErrInvalid", luar.New(L, os.ErrInvalid)) L.SetField(pkg, "ErrNotExist", luar.New(L, os.ErrNotExist)) L.SetField(pkg, "ErrPermission", luar.New(L, os.ErrPermission)) L.SetField(pkg, "Exit", luar.New(L, os.Exit)) L.SetField(pkg, "Expand", luar.New(L, os.Expand)) L.SetField(pkg, "ExpandEnv", luar.New(L, os.ExpandEnv)) L.SetField(pkg, "FindProcess", luar.New(L, os.FindProcess)) L.SetField(pkg, "Getegid", luar.New(L, os.Getegid)) L.SetField(pkg, "Getenv", luar.New(L, os.Getenv)) L.SetField(pkg, "Geteuid", luar.New(L, os.Geteuid)) L.SetField(pkg, "Getgid", luar.New(L, os.Getgid)) L.SetField(pkg, "Getgroups", luar.New(L, os.Getgroups)) L.SetField(pkg, "Getpagesize", luar.New(L, os.Getpagesize)) L.SetField(pkg, "Getpid", luar.New(L, os.Getpid)) L.SetField(pkg, "Getuid", luar.New(L, os.Getuid)) L.SetField(pkg, "Getwd", luar.New(L, os.Getwd)) L.SetField(pkg, "Hostname", luar.New(L, os.Hostname)) L.SetField(pkg, "Interrupt", luar.New(L, os.Interrupt)) L.SetField(pkg, "IsExist", luar.New(L, os.IsExist)) L.SetField(pkg, "IsNotExist", luar.New(L, os.IsNotExist)) L.SetField(pkg, "IsPathSeparator", luar.New(L, os.IsPathSeparator)) L.SetField(pkg, "IsPermission", luar.New(L, os.IsPermission)) L.SetField(pkg, "Kill", luar.New(L, os.Kill)) L.SetField(pkg, "Lchown", luar.New(L, os.Lchown)) L.SetField(pkg, "Link", luar.New(L, os.Link)) L.SetField(pkg, "Lstat", luar.New(L, os.Lstat)) L.SetField(pkg, "Mkdir", luar.New(L, os.Mkdir)) L.SetField(pkg, "MkdirAll", luar.New(L, os.MkdirAll)) L.SetField(pkg, "ModeAppend", luar.New(L, os.ModeAppend)) L.SetField(pkg, "ModeCharDevice", luar.New(L, os.ModeCharDevice)) L.SetField(pkg, "ModeDevice", luar.New(L, os.ModeDevice)) L.SetField(pkg, "ModeDir", luar.New(L, os.ModeDir)) L.SetField(pkg, "ModeExclusive", luar.New(L, os.ModeExclusive)) L.SetField(pkg, "ModeNamedPipe", luar.New(L, os.ModeNamedPipe)) L.SetField(pkg, "ModePerm", luar.New(L, os.ModePerm)) L.SetField(pkg, "ModeSetgid", luar.New(L, os.ModeSetgid)) L.SetField(pkg, "ModeSetuid", luar.New(L, os.ModeSetuid)) L.SetField(pkg, "ModeSocket", luar.New(L, os.ModeSocket)) L.SetField(pkg, "ModeSticky", luar.New(L, os.ModeSticky)) L.SetField(pkg, "ModeSymlink", luar.New(L, os.ModeSymlink)) L.SetField(pkg, "ModeTemporary", luar.New(L, os.ModeTemporary)) L.SetField(pkg, "ModeType", luar.New(L, os.ModeType)) L.SetField(pkg, "NewFile", luar.New(L, os.NewFile)) L.SetField(pkg, "NewSyscallError", luar.New(L, os.NewSyscallError)) L.SetField(pkg, "O_APPEND", luar.New(L, os.O_APPEND)) L.SetField(pkg, "O_CREATE", luar.New(L, os.O_CREATE)) L.SetField(pkg, "O_EXCL", luar.New(L, os.O_EXCL)) L.SetField(pkg, "O_RDONLY", luar.New(L, os.O_RDONLY)) L.SetField(pkg, "O_RDWR", luar.New(L, os.O_RDWR)) L.SetField(pkg, "O_SYNC", luar.New(L, os.O_SYNC)) L.SetField(pkg, "O_TRUNC", luar.New(L, os.O_TRUNC)) L.SetField(pkg, "O_WRONLY", luar.New(L, os.O_WRONLY)) L.SetField(pkg, "Open", luar.New(L, os.Open)) L.SetField(pkg, "OpenFile", luar.New(L, os.OpenFile)) L.SetField(pkg, "PathListSeparator", luar.New(L, os.PathListSeparator)) L.SetField(pkg, "PathSeparator", luar.New(L, os.PathSeparator)) L.SetField(pkg, "Pipe", luar.New(L, os.Pipe)) L.SetField(pkg, "Readlink", luar.New(L, os.Readlink)) L.SetField(pkg, "Remove", luar.New(L, os.Remove)) L.SetField(pkg, "RemoveAll", luar.New(L, os.RemoveAll)) L.SetField(pkg, "Rename", luar.New(L, os.Rename)) L.SetField(pkg, "SEEK_CUR", luar.New(L, io.SeekCurrent)) L.SetField(pkg, "SEEK_END", luar.New(L, io.SeekEnd)) L.SetField(pkg, "SEEK_SET", luar.New(L, io.SeekStart)) L.SetField(pkg, "SameFile", luar.New(L, os.SameFile)) L.SetField(pkg, "Setenv", luar.New(L, os.Setenv)) L.SetField(pkg, "StartProcess", luar.New(L, os.StartProcess)) L.SetField(pkg, "Stat", luar.New(L, os.Stat)) L.SetField(pkg, "Stderr", luar.New(L, os.Stderr)) L.SetField(pkg, "Stdin", luar.New(L, os.Stdin)) L.SetField(pkg, "Stdout", luar.New(L, os.Stdout)) L.SetField(pkg, "Symlink", luar.New(L, os.Symlink)) L.SetField(pkg, "TempDir", luar.New(L, os.TempDir)) L.SetField(pkg, "Truncate", luar.New(L, os.Truncate)) L.SetField(pkg, "UserHomeDir", luar.New(L, os.UserHomeDir)) return pkg } func importRuntime() *lua.LTable { pkg := L.NewTable() L.SetField(pkg, "GC", luar.New(L, runtime.GC)) L.SetField(pkg, "GOARCH", luar.New(L, runtime.GOARCH)) L.SetField(pkg, "GOMAXPROCS", luar.New(L, runtime.GOMAXPROCS)) L.SetField(pkg, "GOOS", luar.New(L, runtime.GOOS)) L.SetField(pkg, "GOROOT", luar.New(L, runtime.GOROOT)) return pkg } func importPath() *lua.LTable { pkg := L.NewTable() L.SetField(pkg, "Base", luar.New(L, path.Base)) L.SetField(pkg, "Clean", luar.New(L, path.Clean)) L.SetField(pkg, "Dir", luar.New(L, path.Dir)) L.SetField(pkg, "ErrBadPattern", luar.New(L, path.ErrBadPattern)) L.SetField(pkg, "Ext", luar.New(L, path.Ext)) L.SetField(pkg, "IsAbs", luar.New(L, path.IsAbs)) L.SetField(pkg, "Join", luar.New(L, path.Join)) L.SetField(pkg, "Match", luar.New(L, path.Match)) L.SetField(pkg, "Split", luar.New(L, path.Split)) return pkg } func importFilePath() *lua.LTable { pkg := L.NewTable() L.SetField(pkg, "Join", luar.New(L, filepath.Join)) L.SetField(pkg, "Abs", luar.New(L, filepath.Abs)) L.SetField(pkg, "Base", luar.New(L, filepath.Base)) L.SetField(pkg, "Clean", luar.New(L, filepath.Clean)) L.SetField(pkg, "Dir", luar.New(L, filepath.Dir)) L.SetField(pkg, "EvalSymlinks", luar.New(L, filepath.EvalSymlinks)) L.SetField(pkg, "Ext", luar.New(L, filepath.Ext)) L.SetField(pkg, "FromSlash", luar.New(L, filepath.FromSlash)) L.SetField(pkg, "Glob", luar.New(L, filepath.Glob)) L.SetField(pkg, "HasPrefix", luar.New(L, filepath.HasPrefix)) L.SetField(pkg, "IsAbs", luar.New(L, filepath.IsAbs)) L.SetField(pkg, "Join", luar.New(L, filepath.Join)) L.SetField(pkg, "Match", luar.New(L, filepath.Match)) L.SetField(pkg, "Rel", luar.New(L, filepath.Rel)) L.SetField(pkg, "Split", luar.New(L, filepath.Split)) L.SetField(pkg, "SplitList", luar.New(L, filepath.SplitList)) L.SetField(pkg, "ToSlash", luar.New(L, filepath.ToSlash)) L.SetField(pkg, "VolumeName", luar.New(L, filepath.VolumeName)) return pkg } func importStrings() *lua.LTable { pkg := L.NewTable() L.SetField(pkg, "Contains", luar.New(L, strings.Contains)) L.SetField(pkg, "ContainsAny", luar.New(L, strings.ContainsAny)) L.SetField(pkg, "ContainsRune", luar.New(L, strings.ContainsRune)) L.SetField(pkg, "Count", luar.New(L, strings.Count)) L.SetField(pkg, "EqualFold", luar.New(L, strings.EqualFold)) L.SetField(pkg, "Fields", luar.New(L, strings.Fields)) L.SetField(pkg, "FieldsFunc", luar.New(L, strings.FieldsFunc)) L.SetField(pkg, "HasPrefix", luar.New(L, strings.HasPrefix)) L.SetField(pkg, "HasSuffix", luar.New(L, strings.HasSuffix)) L.SetField(pkg, "Index", luar.New(L, strings.Index)) L.SetField(pkg, "IndexAny", luar.New(L, strings.IndexAny)) L.SetField(pkg, "IndexByte", luar.New(L, strings.IndexByte)) L.SetField(pkg, "IndexFunc", luar.New(L, strings.IndexFunc)) L.SetField(pkg, "IndexRune", luar.New(L, strings.IndexRune)) L.SetField(pkg, "Join", luar.New(L, strings.Join)) L.SetField(pkg, "LastIndex", luar.New(L, strings.LastIndex)) L.SetField(pkg, "LastIndexAny", luar.New(L, strings.LastIndexAny)) L.SetField(pkg, "LastIndexFunc", luar.New(L, strings.LastIndexFunc)) L.SetField(pkg, "Map", luar.New(L, strings.Map)) L.SetField(pkg, "NewReader", luar.New(L, strings.NewReader)) L.SetField(pkg, "NewReplacer", luar.New(L, strings.NewReplacer)) L.SetField(pkg, "Repeat", luar.New(L, strings.Repeat)) L.SetField(pkg, "Replace", luar.New(L, strings.Replace)) L.SetField(pkg, "Split", luar.New(L, strings.Split)) L.SetField(pkg, "SplitAfter", luar.New(L, strings.SplitAfter)) L.SetField(pkg, "SplitAfterN", luar.New(L, strings.SplitAfterN)) L.SetField(pkg, "SplitN", luar.New(L, strings.SplitN)) L.SetField(pkg, "Title", luar.New(L, strings.Title)) L.SetField(pkg, "ToLower", luar.New(L, strings.ToLower)) L.SetField(pkg, "ToLowerSpecial", luar.New(L, strings.ToLowerSpecial)) L.SetField(pkg, "ToTitle", luar.New(L, strings.ToTitle)) L.SetField(pkg, "ToTitleSpecial", luar.New(L, strings.ToTitleSpecial)) L.SetField(pkg, "ToUpper", luar.New(L, strings.ToUpper)) L.SetField(pkg, "ToUpperSpecial", luar.New(L, strings.ToUpperSpecial)) L.SetField(pkg, "Trim", luar.New(L, strings.Trim)) L.SetField(pkg, "TrimFunc", luar.New(L, strings.TrimFunc)) L.SetField(pkg, "TrimLeft", luar.New(L, strings.TrimLeft)) L.SetField(pkg, "TrimLeftFunc", luar.New(L, strings.TrimLeftFunc)) L.SetField(pkg, "TrimPrefix", luar.New(L, strings.TrimPrefix)) L.SetField(pkg, "TrimRight", luar.New(L, strings.TrimRight)) L.SetField(pkg, "TrimRightFunc", luar.New(L, strings.TrimRightFunc)) L.SetField(pkg, "TrimSpace", luar.New(L, strings.TrimSpace)) L.SetField(pkg, "TrimSuffix", luar.New(L, strings.TrimSuffix)) return pkg } func importRegexp() *lua.LTable { pkg := L.NewTable() L.SetField(pkg, "Match", luar.New(L, regexp.Match)) L.SetField(pkg, "MatchReader", luar.New(L, regexp.MatchReader)) L.SetField(pkg, "MatchString", luar.New(L, regexp.MatchString)) L.SetField(pkg, "QuoteMeta", luar.New(L, regexp.QuoteMeta)) L.SetField(pkg, "Compile", luar.New(L, regexp.Compile)) L.SetField(pkg, "CompilePOSIX", luar.New(L, regexp.CompilePOSIX)) L.SetField(pkg, "MustCompile", luar.New(L, regexp.MustCompile)) L.SetField(pkg, "MustCompilePOSIX", luar.New(L, regexp.MustCompilePOSIX)) return pkg } func importErrors() *lua.LTable { pkg := L.NewTable() L.SetField(pkg, "New", luar.New(L, errors.New)) return pkg } func importTime() *lua.LTable { pkg := L.NewTable() L.SetField(pkg, "Sleep", luar.New(L, time.Sleep)) L.SetField(pkg, "Since", luar.New(L, time.Since)) L.SetField(pkg, "FixedZone", luar.New(L, time.FixedZone)) L.SetField(pkg, "LoadLocation", luar.New(L, time.LoadLocation)) L.SetField(pkg, "Date", luar.New(L, time.Date)) L.SetField(pkg, "Now", luar.New(L, time.Now)) L.SetField(pkg, "Parse", luar.New(L, time.Parse)) L.SetField(pkg, "ParseDuration", luar.New(L, time.ParseDuration)) L.SetField(pkg, "ParseInLocation", luar.New(L, time.ParseInLocation)) L.SetField(pkg, "Unix", luar.New(L, time.Unix)) L.SetField(pkg, "Nanosecond", luar.New(L, time.Nanosecond)) L.SetField(pkg, "Microsecond", luar.New(L, time.Microsecond)) L.SetField(pkg, "Millisecond", luar.New(L, time.Millisecond)) L.SetField(pkg, "Second", luar.New(L, time.Second)) L.SetField(pkg, "Minute", luar.New(L, time.Minute)) L.SetField(pkg, "Hour", luar.New(L, time.Hour)) // TODO: these raw Go timer APIs don't provide any synchronization // with micro. Stop exposing them to lua once plugins switch to using // the safer micro.After() interface instead. See issue #3209 L.SetField(pkg, "After", luar.New(L, time.After)) L.SetField(pkg, "AfterFunc", luar.New(L, time.AfterFunc)) L.SetField(pkg, "NewTicker", luar.New(L, time.NewTicker)) L.SetField(pkg, "NewTimer", luar.New(L, time.NewTimer)) L.SetField(pkg, "Tick", luar.New(L, time.Tick)) return pkg } func importUtf8() *lua.LTable { pkg := L.NewTable() L.SetField(pkg, "DecodeLastRune", luar.New(L, utf8.DecodeLastRune)) L.SetField(pkg, "DecodeLastRuneInString", luar.New(L, utf8.DecodeLastRuneInString)) L.SetField(pkg, "DecodeRune", luar.New(L, utf8.DecodeRune)) L.SetField(pkg, "DecodeRuneInString", luar.New(L, utf8.DecodeRuneInString)) L.SetField(pkg, "EncodeRune", luar.New(L, utf8.EncodeRune)) L.SetField(pkg, "FullRune", luar.New(L, utf8.FullRune)) L.SetField(pkg, "FullRuneInString", luar.New(L, utf8.FullRuneInString)) L.SetField(pkg, "RuneCount", luar.New(L, utf8.RuneCount)) L.SetField(pkg, "RuneCountInString", luar.New(L, utf8.RuneCountInString)) L.SetField(pkg, "RuneLen", luar.New(L, utf8.RuneLen)) L.SetField(pkg, "RuneStart", luar.New(L, utf8.RuneStart)) L.SetField(pkg, "Valid", luar.New(L, utf8.Valid)) L.SetField(pkg, "ValidRune", luar.New(L, utf8.ValidRune)) L.SetField(pkg, "ValidString", luar.New(L, utf8.ValidString)) return pkg } func importHumanize() *lua.LTable { pkg := L.NewTable() L.SetField(pkg, "Bytes", luar.New(L, humanize.Bytes)) L.SetField(pkg, "Ordinal", luar.New(L, humanize.Ordinal)) return pkg } func importHTTP() *lua.LTable { pkg := L.NewTable() L.SetField(pkg, "Get", luar.New(L, http.Get)) L.SetField(pkg, "Post", luar.New(L, http.Post)) return pkg } func importArchiveZip() *lua.LTable { pkg := L.NewTable() L.SetField(pkg, "OpenReader", luar.New(L, zip.OpenReader)) L.SetField(pkg, "NewReader", luar.New(L, zip.NewReader)) L.SetField(pkg, "NewWriter", luar.New(L, zip.NewWriter)) return pkg } micro-2.0.14/internal/screen/0000775000175000017510000000000014663411671015344 5ustar nileshnileshmicro-2.0.14/internal/screen/message.go0000664000175000017510000000321314663411671017316 0ustar nileshnileshpackage screen import ( "bufio" "fmt" "os" "strconv" "strings" ) // TermMessage sends a message to the user in the terminal. This usually occurs before // micro has been fully initialized -- ie if there is an error in the syntax highlighting // regular expressions // The function must be called when the Screen is not initialized // This will write the message, and wait for the user // to press and key to continue func TermMessage(msg ...interface{}) { screenb := TempFini() fmt.Println(msg...) fmt.Print("\nPress enter to continue") reader := bufio.NewReader(os.Stdin) reader.ReadString('\n') TempStart(screenb) } // TermPrompt prints a prompt and requests the user for a response // The result is matched against a list of options and the index of // the match is returned // If wait is true, the prompt re-prompts until a valid option is // chosen, otherwise if wait is false, -1 is returned for no match func TermPrompt(prompt string, options []string, wait bool) int { screenb := TempFini() idx := -1 // same behavior as do { ... } while (wait && idx == -1) for ok := true; ok; ok = wait && idx == -1 { reader := bufio.NewReader(os.Stdin) fmt.Print(prompt) resp, _ := reader.ReadString('\n') resp = strings.TrimSpace(resp) for i, opt := range options { if resp == opt { idx = i } } if wait && idx == -1 { fmt.Println("\nInvalid choice.") } } TempStart(screenb) return idx } // TermError sends an error to the user in the terminal. Like TermMessage except formatted // as an error func TermError(filename string, lineNum int, err string) { TermMessage(filename + ", " + strconv.Itoa(lineNum) + ": " + err) } micro-2.0.14/internal/screen/screen.go0000664000175000017510000001226114663411671017154 0ustar nileshnileshpackage screen import ( "errors" "log" "os" "sync" "github.com/zyedidia/micro/v2/internal/config" "github.com/zyedidia/micro/v2/internal/util" "github.com/zyedidia/tcell/v2" ) // Screen is the tcell screen we use to draw to the terminal // Synchronization is used because we poll the screen on a separate // thread and sometimes the screen is shut down by the main thread // (for example on TermMessage) so we don't want to poll a nil/shutdown // screen. TODO: maybe we should worry about polling and drawing at the // same time too. var Screen tcell.Screen // Events is the channel of tcell events var Events chan (tcell.Event) // RestartCallback is called when the screen is restarted after it was // temporarily shut down var RestartCallback func() // The lock is necessary since the screen is polled on a separate thread var lock sync.Mutex // drawChan is a channel that will cause the screen to redraw when // written to even if no event user event has occurred var drawChan chan bool // Lock locks the screen lock func Lock() { lock.Lock() } // Unlock unlocks the screen lock func Unlock() { lock.Unlock() } // Redraw schedules a redraw with the draw channel func Redraw() { select { case drawChan <- true: default: // channel is full } } // DrawChan returns the draw channel func DrawChan() chan bool { return drawChan } type screenCell struct { x, y int r rune combc []rune style tcell.Style } var lastCursor screenCell // ShowFakeCursor displays a cursor at the given position by modifying the // style of the given column instead of actually using the terminal cursor // This can be useful in certain terminals such as the windows console where // modifying the cursor location is slow and frequent modifications cause flashing // This keeps track of the most recent fake cursor location and resets it when // a new fake cursor location is specified func ShowFakeCursor(x, y int) { r, combc, style, _ := Screen.GetContent(x, y) Screen.SetContent(lastCursor.x, lastCursor.y, lastCursor.r, lastCursor.combc, lastCursor.style) Screen.SetContent(x, y, r, combc, config.DefStyle.Reverse(true)) lastCursor.x, lastCursor.y = x, y lastCursor.r = r lastCursor.combc = combc lastCursor.style = style } func UseFake() bool { return util.FakeCursor || config.GetGlobalOption("fakecursor").(bool) } // ShowFakeCursorMulti is the same as ShowFakeCursor except it does not // reset previous locations of the cursor // Fake cursors are also necessary to display multiple cursors func ShowFakeCursorMulti(x, y int) { r, _, _, _ := Screen.GetContent(x, y) Screen.SetContent(x, y, r, nil, config.DefStyle.Reverse(true)) } // ShowCursor puts the cursor at the given location using a fake cursor // if enabled or using the terminal cursor otherwise // By default only the windows console will use a fake cursor func ShowCursor(x, y int) { if UseFake() { ShowFakeCursor(x, y) } else { Screen.ShowCursor(x, y) } } // SetContent sets a cell at a point on the screen and makes sure that it is // synced with the last cursor location func SetContent(x, y int, mainc rune, combc []rune, style tcell.Style) { if !Screen.CanDisplay(mainc, true) { mainc = '�' } Screen.SetContent(x, y, mainc, combc, style) if UseFake() && lastCursor.x == x && lastCursor.y == y { lastCursor.r = mainc lastCursor.style = style lastCursor.combc = combc } } // TempFini shuts the screen down temporarily func TempFini() bool { screenWasNil := Screen == nil if !screenWasNil { Screen.Fini() Lock() Screen = nil } return screenWasNil } // TempStart restarts the screen after it was temporarily disabled func TempStart(screenWasNil bool) { if !screenWasNil { Init() Unlock() if RestartCallback != nil { RestartCallback() } } } // Init creates and initializes the tcell screen func Init() error { drawChan = make(chan bool, 8) // Should we enable true color? truecolor := os.Getenv("MICRO_TRUECOLOR") == "1" if !truecolor { os.Setenv("TCELL_TRUECOLOR", "disable") } var oldTerm string modifiedTerm := false setXterm := func() { oldTerm = os.Getenv("TERM") os.Setenv("TERM", "xterm-256color") modifiedTerm = true } if config.GetGlobalOption("xterm").(bool) { setXterm() } // Initilize tcell var err error Screen, err = tcell.NewScreen() if err != nil { log.Println("Warning: during screen initialization:", err) log.Println("Falling back to TERM=xterm-256color") setXterm() Screen, err = tcell.NewScreen() if err != nil { return err } } if err = Screen.Init(); err != nil { return err } Screen.SetPaste(config.GetGlobalOption("paste").(bool)) // restore TERM if modifiedTerm { os.Setenv("TERM", oldTerm) } if config.GetGlobalOption("mouse").(bool) { Screen.EnableMouse() } return nil } // InitSimScreen initializes a simulation screen for testing purposes func InitSimScreen() (tcell.SimulationScreen, error) { drawChan = make(chan bool, 8) // Initilize tcell var err error s := tcell.NewSimulationScreen("") if s == nil { return nil, errors.New("Failed to get a simulation screen") } if err = s.Init(); err != nil { return nil, err } s.SetSize(80, 24) Screen = s if config.GetGlobalOption("mouse").(bool) { Screen.EnableMouse() } return s, nil } micro-2.0.14/internal/shell/0000775000175000017510000000000014663411671015174 5ustar nileshnileshmicro-2.0.14/internal/shell/job.go0000664000175000017510000000536214663411671016303 0ustar nileshnileshpackage shell import ( "bytes" "io" "os/exec" ) var Jobs chan JobFunction func init() { Jobs = make(chan JobFunction, 100) } // Jobs are the way plugins can run processes in the background // A job is simply a process that gets executed asynchronously // There are callbacks for when the job exits, when the job creates stdout // and when the job creates stderr // These jobs run in a separate goroutine but the lua callbacks need to be // executed in the main thread (where the Lua VM is running) so they are // put into the jobs channel which gets read by the main loop // JobFunction is a representation of a job (this data structure is what is loaded // into the jobs channel) type JobFunction struct { Function func(string, []interface{}) Output string Args []interface{} } // A CallbackFile is the data structure that makes it possible to catch stderr and stdout write events type CallbackFile struct { io.Writer callback func(string, []interface{}) args []interface{} } // Job stores the executing command for the job, and the stdin pipe type Job struct { *exec.Cmd Stdin io.WriteCloser } func (f *CallbackFile) Write(data []byte) (int, error) { // This is either stderr or stdout // In either case we create a new job function callback and put it in the jobs channel jobFunc := JobFunction{f.callback, string(data), f.args} Jobs <- jobFunc return f.Writer.Write(data) } // JobStart starts a shell command in the background with the given callbacks // It returns an *exec.Cmd as the job id func JobStart(cmd string, onStdout, onStderr, onExit func(string, []interface{}), userargs ...interface{}) *Job { return JobSpawn("sh", []string{"-c", cmd}, onStdout, onStderr, onExit, userargs...) } // JobSpawn starts a process with args in the background with the given callbacks // It returns an *exec.Cmd as the job id func JobSpawn(cmdName string, cmdArgs []string, onStdout, onStderr, onExit func(string, []interface{}), userargs ...interface{}) *Job { // Set up everything correctly if the functions have been provided proc := exec.Command(cmdName, cmdArgs...) var outbuf bytes.Buffer if onStdout != nil { proc.Stdout = &CallbackFile{&outbuf, onStdout, userargs} } else { proc.Stdout = &outbuf } if onStderr != nil { proc.Stderr = &CallbackFile{&outbuf, onStderr, userargs} } else { proc.Stderr = &outbuf } stdin, _ := proc.StdinPipe() go func() { // Run the process in the background and create the onExit callback proc.Run() jobFunc := JobFunction{onExit, outbuf.String(), userargs} Jobs <- jobFunc }() return &Job{proc, stdin} } // JobStop kills a job func JobStop(j *Job) { j.Process.Kill() } // JobSend sends the given data into the job's stdin stream func JobSend(j *Job, data string) { j.Stdin.Write([]byte(data)) } micro-2.0.14/internal/shell/shell.go0000664000175000017510000000635114663411671016637 0ustar nileshnileshpackage shell import ( "bytes" "errors" "fmt" "io" "os" "os/exec" "os/signal" shellquote "github.com/kballard/go-shellquote" "github.com/zyedidia/micro/v2/internal/screen" "github.com/zyedidia/micro/v2/internal/util" ) // ExecCommand executes a command using exec // It returns any output/errors func ExecCommand(name string, arg ...string) (string, error) { var err error cmd := exec.Command(name, arg...) outputBytes := &bytes.Buffer{} cmd.Stdout = outputBytes cmd.Stderr = outputBytes err = cmd.Start() if err != nil { return "", err } err = cmd.Wait() // wait for command to finish outstring := outputBytes.String() return outstring, err } // RunCommand executes a shell command and returns the output/error func RunCommand(input string) (string, error) { args, err := shellquote.Split(input) if err != nil { return "", err } if len(args) == 0 { return "", errors.New("No arguments") } inputCmd := args[0] return ExecCommand(inputCmd, args[1:]...) } // RunBackgroundShell runs a shell command in the background // It returns a function which will run the command and returns a string // message result func RunBackgroundShell(input string) (func() string, error) { args, err := shellquote.Split(input) if err != nil { return nil, err } if len(args) == 0 { return nil, errors.New("No arguments") } inputCmd := args[0] return func() string { output, err := RunCommand(input) str := output if err != nil { str = fmt.Sprint(inputCmd, " exited with error: ", err, ": ", output) } return str }, nil } // RunInteractiveShell runs a shellcommand interactively func RunInteractiveShell(input string, wait bool, getOutput bool) (string, error) { args, err := shellquote.Split(input) if err != nil { return "", err } if len(args) == 0 { return "", errors.New("No arguments") } inputCmd := args[0] // Shut down the screen because we're going to interact directly with the shell screenb := screen.TempFini() args = args[1:] // Set up everything for the command outputBytes := &bytes.Buffer{} cmd := exec.Command(inputCmd, args...) cmd.Stdin = os.Stdin if getOutput { cmd.Stdout = io.MultiWriter(os.Stdout, outputBytes) } else { cmd.Stdout = os.Stdout } cmd.Stderr = os.Stderr // This is a trap for Ctrl-C so that it doesn't kill micro // micro is killed if the signal is ignored only on Windows, so it is // received c := make(chan os.Signal, 1) signal.Reset(os.Interrupt) signal.Notify(c, os.Interrupt) err = cmd.Start() if err == nil { err = cmd.Wait() if wait { // This is just so we don't return right away and let the user press enter to return screen.TermMessage("") } } else { screen.TermMessage(err) } output := outputBytes.String() // Start the screen back up screen.TempStart(screenb) signal.Notify(util.Sigterm, os.Interrupt) signal.Stop(c) return output, err } // UserCommand runs the shell command // The openTerm argument specifies whether a terminal should be opened (for viewing output // or interacting with stdin) // func UserCommand(input string, openTerm bool, waitToFinish bool) string { // if !openTerm { // RunBackgroundShell(input) // return "" // } else { // output, _ := RunInteractiveShell(input, waitToFinish, false) // return output // } // } micro-2.0.14/internal/shell/terminal.go0000664000175000017510000000576514663411671017353 0ustar nileshnileshpackage shell import ( "bytes" "os/exec" "strconv" "github.com/zyedidia/micro/v2/internal/buffer" "github.com/zyedidia/micro/v2/internal/screen" "github.com/zyedidia/terminal" ) type TermType int type CallbackFunc func(string) const ( TTClose = iota // Should be closed TTRunning // Currently running a command TTDone // Finished running a command ) var CloseTerms chan bool func init() { CloseTerms = make(chan bool) } // A Terminal holds information for the terminal emulator type Terminal struct { State terminal.State Term *terminal.VT title string Status TermType Selection [2]buffer.Loc wait bool getOutput bool output *bytes.Buffer callback CallbackFunc } // HasSelection returns whether this terminal has a valid selection func (t *Terminal) HasSelection() bool { return t.Selection[0] != t.Selection[1] } func (t *Terminal) Name() string { return t.title } // GetSelection returns the selected text func (t *Terminal) GetSelection(width int) string { start := t.Selection[0] end := t.Selection[1] if start.GreaterThan(end) { start, end = end, start } var ret string var l buffer.Loc for y := start.Y; y <= end.Y; y++ { for x := 0; x < width; x++ { l.X, l.Y = x, y if l.GreaterEqual(start) && l.LessThan(end) { c, _, _ := t.State.Cell(x, y) ret += string(c) } } } return ret } // Start begins a new command in this terminal with a given view func (t *Terminal) Start(execCmd []string, getOutput bool, wait bool, callback func(out string, userargs []interface{}), userargs []interface{}) error { if len(execCmd) <= 0 { return nil } cmd := exec.Command(execCmd[0], execCmd[1:]...) t.output = nil if getOutput { t.output = bytes.NewBuffer([]byte{}) cmd.Stdout = t.output } Term, _, err := terminal.Start(&t.State, cmd) if err != nil { return err } t.Term = Term t.getOutput = getOutput t.Status = TTRunning t.title = execCmd[0] + ":" + strconv.Itoa(cmd.Process.Pid) t.wait = wait t.callback = func(out string) { callback(out, userargs) } go func() { for { err := Term.Parse() if err != nil { Term.Write([]byte("Press enter to close")) screen.Redraw() break } screen.Redraw() } t.Stop() }() return nil } // Stop stops execution of the terminal and sets the Status // to TTDone func (t *Terminal) Stop() { t.Term.File().Close() t.Term.Close() if t.wait { t.Status = TTDone } else { t.Close() CloseTerms <- true } } // Close sets the Status to TTClose indicating that the terminal // is done and should be closed func (t *Terminal) Close() { t.Status = TTClose // call the lua function that the user has given as a callback if t.getOutput { if t.callback != nil { Jobs <- JobFunction{ Function: func(out string, args []interface{}) { t.callback(out) }, Output: t.output.String(), Args: nil, } } } } // WriteString writes a given string to this terminal's pty func (t *Terminal) WriteString(str string) { t.Term.File().WriteString(str) } micro-2.0.14/internal/util/0000775000175000017510000000000014663411671015042 5ustar nileshnileshmicro-2.0.14/internal/util/lua.go0000664000175000017510000000151314663411671016152 0ustar nileshnileshpackage util // LuaRuneAt is a helper function for lua plugins to return the rune // at an index within a string func LuaRuneAt(str string, runeidx int) string { i := 0 for len(str) > 0 { r, _, size := DecodeCharacterInString(str) str = str[size:] if i == runeidx { return string(r) } i++ } return "" } // LuaGetLeadingWhitespace returns the leading whitespace of a string (used by lua plugins) func LuaGetLeadingWhitespace(s string) string { ws := []byte{} for len(s) > 0 { r, _, size := DecodeCharacterInString(s) if r == ' ' || r == '\t' { ws = append(ws, byte(r)) } else { break } s = s[size:] } return string(ws) } // LuaIsWordChar returns true if the first rune in a string is a word character func LuaIsWordChar(s string) bool { r, _, _ := DecodeCharacterInString(s) return IsWordChar(r) } micro-2.0.14/internal/util/profile.go0000664000175000017510000000120114663411671017023 0ustar nileshnileshpackage util import ( "fmt" "log" "runtime" "time" humanize "github.com/dustin/go-humanize" ) // GetMemStats returns a string describing the memory usage and gc time used so far func GetMemStats() string { var memstats runtime.MemStats runtime.ReadMemStats(&memstats) return fmt.Sprintf("Alloc: %s, Sys: %s, GC: %d, PauseTotalNs: %dns", humanize.Bytes(memstats.Alloc), humanize.Bytes(memstats.Sys), memstats.NumGC, memstats.PauseTotalNs) } func Tic(s string) time.Time { log.Println("START:", s) return time.Now() } func Toc(start time.Time) { end := time.Now() log.Println("END: ElapsedTime in seconds:", end.Sub(start)) } micro-2.0.14/internal/util/unicode.go0000664000175000017510000000442414663411671017023 0ustar nileshnileshpackage util import ( "unicode" "unicode/utf8" ) // Unicode is annoying. A "code point" (rune in Go-speak) may need up to // 4 bytes to represent it. In general, a code point will represent a // complete character, but this is not always the case. A character with // accents may be made up of multiple code points (the code point for the // original character, and additional code points for each accent/marking). // The functions below are meant to help deal with these additional "combining" // code points. In underlying operations (search, replace, etc...), micro will // treat a character with combining code points as just the original code point. // For rendering, micro will display the combining characters. It's not perfect // but it's pretty good. var minMark = rune(unicode.Mark.R16[0].Lo) func isMark(r rune) bool { // Fast path if r < minMark { return false } return unicode.In(r, unicode.Mark) } // DecodeCharacter returns the next character from an array of bytes // A character is a rune along with any accompanying combining runes func DecodeCharacter(b []byte) (rune, []rune, int) { r, size := utf8.DecodeRune(b) b = b[size:] c, s := utf8.DecodeRune(b) var combc []rune for isMark(c) { combc = append(combc, c) size += s b = b[s:] c, s = utf8.DecodeRune(b) } return r, combc, size } // DecodeCharacterInString returns the next character from a string // A character is a rune along with any accompanying combining runes func DecodeCharacterInString(str string) (rune, []rune, int) { r, size := utf8.DecodeRuneInString(str) str = str[size:] c, s := utf8.DecodeRuneInString(str) var combc []rune for isMark(c) { combc = append(combc, c) size += s str = str[s:] c, s = utf8.DecodeRuneInString(str) } return r, combc, size } // CharacterCount returns the number of characters in a byte array // Similar to utf8.RuneCount but for unicode characters func CharacterCount(b []byte) int { s := 0 for len(b) > 0 { r, size := utf8.DecodeRune(b) if !isMark(r) { s++ } b = b[size:] } return s } // CharacterCount returns the number of characters in a string // Similar to utf8.RuneCountInString but for unicode characters func CharacterCountInString(str string) int { s := 0 for _, r := range str { if !isMark(r) { s++ } } return s } micro-2.0.14/internal/util/util.go0000664000175000017510000003343514663411671016356 0ustar nileshnileshpackage util import ( "archive/zip" "bytes" "errors" "fmt" "io" "net/http" "os" "os/user" "path/filepath" "regexp" "runtime" "strconv" "strings" "time" "unicode" "unicode/utf8" "github.com/blang/semver" runewidth "github.com/mattn/go-runewidth" ) var ( // These variables should be set by the linker when compiling // Version is the version number or commit hash Version = "0.0.0-unknown" // SemVersion is the Semantic version SemVersion semver.Version // CommitHash is the commit this version was built on CommitHash = "Unknown" // CompileDate is the date this binary was compiled on CompileDate = "Unknown" // Debug logging Debug = "OFF" // FakeCursor is used to disable the terminal cursor and have micro // draw its own (enabled for windows consoles where the cursor is slow) FakeCursor = false // Stdout is a buffer that is written to stdout when micro closes Stdout *bytes.Buffer // Sigterm is a channel where micro exits when written Sigterm chan os.Signal ) func init() { var err error SemVersion, err = semver.Make(Version) if err != nil { fmt.Println("Invalid version: ", Version, err) } _, wt := os.LookupEnv("WT_SESSION") if runtime.GOOS == "windows" && !wt { FakeCursor = true } Stdout = new(bytes.Buffer) } // SliceEnd returns a byte slice where the index is a rune index // Slices off the start of the slice func SliceEnd(slc []byte, index int) []byte { len := len(slc) i := 0 totalSize := 0 for totalSize < len { if i >= index { return slc[totalSize:] } _, _, size := DecodeCharacter(slc[totalSize:]) totalSize += size i++ } return slc[totalSize:] } // SliceEndStr is the same as SliceEnd but for strings func SliceEndStr(str string, index int) string { len := len(str) i := 0 totalSize := 0 for totalSize < len { if i >= index { return str[totalSize:] } _, _, size := DecodeCharacterInString(str[totalSize:]) totalSize += size i++ } return str[totalSize:] } // SliceStart returns a byte slice where the index is a rune index // Slices off the end of the slice func SliceStart(slc []byte, index int) []byte { len := len(slc) i := 0 totalSize := 0 for totalSize < len { if i >= index { return slc[:totalSize] } _, _, size := DecodeCharacter(slc[totalSize:]) totalSize += size i++ } return slc[:totalSize] } // SliceStartStr is the same as SliceStart but for strings func SliceStartStr(str string, index int) string { len := len(str) i := 0 totalSize := 0 for totalSize < len { if i >= index { return str[:totalSize] } _, _, size := DecodeCharacterInString(str[totalSize:]) totalSize += size i++ } return str[:totalSize] } // SliceVisualEnd will take a byte slice and slice off the start // up to a given visual index. If the index is in the middle of a // rune the number of visual columns into the rune will be returned // It will also return the char pos of the first character of the slice func SliceVisualEnd(b []byte, n, tabsize int) ([]byte, int, int) { width := 0 i := 0 for len(b) > 0 { r, _, size := DecodeCharacter(b) w := 0 switch r { case '\t': ts := tabsize - (width % tabsize) w = ts default: w = runewidth.RuneWidth(r) } if width+w > n { return b, n - width, i } width += w b = b[size:] i++ } return b, n - width, i } // Abs is a simple absolute value function for ints func Abs(n int) int { if n < 0 { return -n } return n } // StringWidth returns the visual width of a byte array indexed from 0 to n (rune index) // with a given tabsize func StringWidth(b []byte, n, tabsize int) int { if n <= 0 { return 0 } i := 0 width := 0 for len(b) > 0 { r, _, size := DecodeCharacter(b) b = b[size:] switch r { case '\t': ts := tabsize - (width % tabsize) width += ts default: width += runewidth.RuneWidth(r) } i++ if i == n { return width } } return width } // Min takes the min of two ints func Min(a, b int) int { if a > b { return b } return a } // Max takes the max of two ints func Max(a, b int) int { if a > b { return a } return b } // FSize gets the size of a file func FSize(f *os.File) int64 { fi, _ := f.Stat() return fi.Size() } // IsWordChar returns whether or not a rune is a 'word character' // Word characters are defined as numbers, letters or sub-word delimiters func IsWordChar(r rune) bool { return IsAlphanumeric(r) || IsSubwordDelimiter(r) } // IsNonWordChar returns whether or not a rune is not a 'word character' // Non word characters are defined as all characters not being numbers, letters or sub-word delimiters // See IsWordChar() func IsNonWordChar(r rune) bool { return !IsWordChar(r) } // IsUpperWordChar returns whether or not a rune is an 'upper word character' // Upper word characters are defined as numbers, upper-case letters or sub-word delimiters func IsUpperWordChar(r rune) bool { return IsUpperAlphanumeric(r) || IsSubwordDelimiter(r) } // IsLowerWordChar returns whether or not a rune is a 'lower word character' // Lower word characters are defined as numbers, lower-case letters or sub-word delimiters func IsLowerWordChar(r rune) bool { return IsLowerAlphanumeric(r) || IsSubwordDelimiter(r) } // IsSubwordDelimiter returns whether or not a rune is a 'sub-word delimiter character' // i.e. is considered a part of the word and is used as a delimiter between sub-words of the word. // For now the only sub-word delimiter character is '_'. func IsSubwordDelimiter(r rune) bool { return r == '_' } // IsAlphanumeric returns whether or not a rune is an 'alphanumeric character' // Alphanumeric characters are defined as numbers or letters func IsAlphanumeric(r rune) bool { return unicode.IsLetter(r) || unicode.IsNumber(r) } // IsUpperAlphanumeric returns whether or not a rune is an 'upper alphanumeric character' // Upper alphanumeric characters are defined as numbers or upper-case letters func IsUpperAlphanumeric(r rune) bool { return IsUpperLetter(r) || unicode.IsNumber(r) } // IsLowerAlphanumeric returns whether or not a rune is a 'lower alphanumeric character' // Lower alphanumeric characters are defined as numbers or lower-case letters func IsLowerAlphanumeric(r rune) bool { return IsLowerLetter(r) || unicode.IsNumber(r) } // IsUpperLetter returns whether or not a rune is an 'upper letter character' // Upper letter characters are defined as upper-case letters func IsUpperLetter(r rune) bool { // unicode.IsUpper() returns true for letters only return unicode.IsUpper(r) } // IsLowerLetter returns whether or not a rune is a 'lower letter character' // Lower letter characters are defined as lower-case letters func IsLowerLetter(r rune) bool { // unicode.IsLower() returns true for letters only return unicode.IsLower(r) } // Spaces returns a string with n spaces func Spaces(n int) string { return strings.Repeat(" ", n) } // IsSpaces checks if a given string is only spaces func IsSpaces(str []byte) bool { for _, c := range str { if c != ' ' { return false } } return true } // IsSpacesOrTabs checks if a given string contains only spaces and tabs func IsSpacesOrTabs(str []byte) bool { for _, c := range str { if c != ' ' && c != '\t' { return false } } return true } // IsWhitespace returns true if the given rune is a space, tab, or newline func IsWhitespace(c rune) bool { return unicode.IsSpace(c) } // IsBytesWhitespace returns true if the given bytes are all whitespace func IsBytesWhitespace(b []byte) bool { for _, c := range b { if !IsWhitespace(rune(c)) { return false } } return true } // RunePos returns the rune index of a given byte index // Make sure the byte index is not between code points func RunePos(b []byte, i int) int { return CharacterCount(b[:i]) } // MakeRelative will attempt to make a relative path between path and base func MakeRelative(path, base string) (string, error) { if len(path) > 0 { rel, err := filepath.Rel(base, path) if err != nil { return path, err } return rel, nil } return path, nil } // ReplaceHome takes a path as input and replaces ~ at the start of the path with the user's // home directory. Does nothing if the path does not start with '~'. func ReplaceHome(path string) (string, error) { if !strings.HasPrefix(path, "~") { return path, nil } var userData *user.User var err error homeString := strings.Split(path, "/")[0] if homeString == "~" { userData, err = user.Current() if err != nil { return "", errors.New("Could not find user: " + err.Error()) } } else { userData, err = user.Lookup(homeString[1:]) if err != nil { return "", errors.New("Could not find user: " + err.Error()) } } home := userData.HomeDir return strings.Replace(path, homeString, home, 1), nil } // GetPathAndCursorPosition returns a filename without everything following a `:` // This is used for opening files like util.go:10:5 to specify a line and column // Special cases like Windows Absolute path (C:\myfile.txt:10:5) are handled correctly. func GetPathAndCursorPosition(path string) (string, []string) { re := regexp.MustCompile(`([\s\S]+?)(?::(\d+))(?::(\d+))?$`) match := re.FindStringSubmatch(path) // no lines/columns were specified in the path, return just the path with no cursor location if len(match) == 0 { return path, nil } else if match[len(match)-1] != "" { // if the last capture group match isn't empty then both line and column were provided return match[1], match[2:] } // if it was empty, then only a line was provided, so default to column 0 return match[1], []string{match[2], "0"} } // GetModTime returns the last modification time for a given file func GetModTime(path string) (time.Time, error) { info, err := os.Stat(path) if err != nil { return time.Now(), err } return info.ModTime(), nil } // EscapePath replaces every path separator in a given path with a % func EscapePath(path string) string { path = filepath.ToSlash(path) if runtime.GOOS == "windows" { // ':' is not valid in a path name on Windows but is ok on Unix path = strings.ReplaceAll(path, ":", "%") } return strings.ReplaceAll(path, "/", "%") } // GetLeadingWhitespace returns the leading whitespace of the given byte array func GetLeadingWhitespace(b []byte) []byte { ws := []byte{} for len(b) > 0 { r, _, size := DecodeCharacter(b) if r == ' ' || r == '\t' { ws = append(ws, byte(r)) } else { break } b = b[size:] } return ws } // GetTrailingWhitespace returns the trailing whitespace of the given byte array func GetTrailingWhitespace(b []byte) []byte { ws := []byte{} for len(b) > 0 { r, size := utf8.DecodeLastRune(b) if IsWhitespace(r) { ws = append([]byte(string(r)), ws...) } else { break } b = b[:len(b)-size] } return ws } // HasTrailingWhitespace returns true if the given byte array ends with a whitespace func HasTrailingWhitespace(b []byte) bool { r, _ := utf8.DecodeLastRune(b) return IsWhitespace(r) } // IntOpt turns a float64 setting to an int func IntOpt(opt interface{}) int { return int(opt.(float64)) } // GetCharPosInLine gets the char position of a visual x y // coordinate (this is necessary because tabs are 1 char but // 4 visual spaces) func GetCharPosInLine(b []byte, visualPos int, tabsize int) int { // Scan rune by rune until we exceed the visual width that we are // looking for. Then we can return the character position we have found i := 0 // char pos width := 0 // string visual width for len(b) > 0 { r, _, size := DecodeCharacter(b) b = b[size:] switch r { case '\t': ts := tabsize - (width % tabsize) width += ts default: width += runewidth.RuneWidth(r) } if width >= visualPos { if width == visualPos { i++ } break } i++ } return i } // ParseBool is almost exactly like strconv.ParseBool, except it also accepts 'on' and 'off' // as 'true' and 'false' respectively func ParseBool(str string) (bool, error) { if str == "on" { return true, nil } if str == "off" { return false, nil } return strconv.ParseBool(str) } // Clamp clamps a value between min and max func Clamp(val, min, max int) int { if val < min { val = min } else if val > max { val = max } return val } // IsAutocomplete returns whether a character should begin an autocompletion. func IsAutocomplete(c rune) bool { return c == '.' || IsWordChar(c) } // ParseSpecial replaces escaped ts with '\t'. func ParseSpecial(s string) string { return strings.ReplaceAll(s, "\\t", "\t") } // String converts a byte array to a string (for lua plugins) func String(s []byte) string { return string(s) } // Unzip unzips a file to given folder func Unzip(src, dest string) error { r, err := zip.OpenReader(src) if err != nil { return err } defer r.Close() os.MkdirAll(dest, 0755) // Closure to address file descriptors issue with all the deferred .Close() methods extractAndWriteFile := func(f *zip.File) error { rc, err := f.Open() if err != nil { return err } defer rc.Close() path := filepath.Join(dest, f.Name) // Check for ZipSlip (Directory traversal) if !strings.HasPrefix(path, filepath.Clean(dest)+string(os.PathSeparator)) { return fmt.Errorf("illegal file path: %s", path) } if f.FileInfo().IsDir() { os.MkdirAll(path, f.Mode()) } else { os.MkdirAll(filepath.Dir(path), f.Mode()) f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) if err != nil { return err } defer f.Close() _, err = io.Copy(f, rc) if err != nil { return err } } return nil } for _, f := range r.File { err := extractAndWriteFile(f) if err != nil { return err } } return nil } // HttpRequest returns a new http.Client for making custom requests (for lua plugins) func HttpRequest(method string, url string, headers []string) (resp *http.Response, err error) { client := http.Client{} req, err := http.NewRequest(method, url, nil) if err != nil { return nil, err } for i := 0; i < len(headers); i += 2 { req.Header.Add(headers[i], headers[i+1]) } return client.Do(req) } micro-2.0.14/internal/util/util_test.go0000664000175000017510000000131414663411671017404 0ustar nileshnileshpackage util import ( "testing" "github.com/stretchr/testify/assert" ) func TestStringWidth(t *testing.T) { bytes := []byte("\tPot să \tmănânc sticlă È™i ea nu mă răneÈ™te.") n := StringWidth(bytes, 23, 4) assert.Equal(t, 26, n) } func TestSliceVisualEnd(t *testing.T) { s := []byte("\thello") slc, n, _ := SliceVisualEnd(s, 2, 4) assert.Equal(t, []byte("\thello"), slc) assert.Equal(t, 2, n) slc, n, _ = SliceVisualEnd(s, 1, 4) assert.Equal(t, []byte("\thello"), slc) assert.Equal(t, 1, n) slc, n, _ = SliceVisualEnd(s, 4, 4) assert.Equal(t, []byte("hello"), slc) assert.Equal(t, 0, n) slc, n, _ = SliceVisualEnd(s, 5, 4) assert.Equal(t, []byte("ello"), slc) assert.Equal(t, 0, n) } micro-2.0.14/internal/views/0000775000175000017510000000000014663411671015222 5ustar nileshnileshmicro-2.0.14/internal/views/splits.go0000664000175000017510000002470614663411671017100 0ustar nileshnileshpackage views import ( "fmt" "strings" ) type SplitType uint8 const ( STVert = 0 STHoriz = 1 STUndef = 2 ) var idcounter uint64 // NewID returns a new unique id func NewID() uint64 { idcounter++ return idcounter } // A View is a size and location of a split type View struct { X, Y int W, H int } // A Node describes a split in the tree // If a node is a leaf node then it corresponds to a buffer that is being // displayed otherwise it has a number of children of the opposite type // (vertical splits have horizontal children and vice versa) type Node struct { View Kind SplitType parent *Node children []*Node // Nodes can be marked as non resizable if they shouldn't be rescaled // when the terminal window is resized or when a new split is added // Only the splits on the edges of the screen can be marked as non resizable canResize bool // A node may also be marked with proportional scaling. This means that when // the window is resized the split maintains its proportions propScale bool // Defines the proportion of the screen this node should take up if propScale is // on propW, propH float64 // The id is unique for each leaf node and provides a way to keep track of a split // The id cannot be 0 id uint64 } // NewNode returns a new node with the given specifications func NewNode(Kind SplitType, x, y, w, h int, parent *Node, id uint64) *Node { n := new(Node) n.Kind = Kind n.canResize = true n.propScale = true n.X, n.Y, n.W, n.H = x, y, w, h n.children = make([]*Node, 0) n.parent = parent n.id = id if parent != nil { n.propW, n.propH = float64(w)/float64(parent.W), float64(h)/float64(parent.H) } else { n.propW, n.propH = 1, 1 } return n } // NewRoot returns an empty Node with a size and location // The type of the node will be determined by the first action on the node // In other words, a lone split is neither horizontal nor vertical, it only // becomes one or the other after a vsplit or hsplit is made func NewRoot(x, y, w, h int) *Node { n1 := NewNode(STUndef, x, y, w, h, nil, NewID()) return n1 } // IsLeaf returns if this node is a leaf node func (n *Node) IsLeaf() bool { return len(n.children) == 0 } // ID returns this node's id or 0 if it is not viewable func (n *Node) ID() uint64 { if n.IsLeaf() { return n.id } return 0 } // CanResize returns if this node can be resized func (n *Node) CanResize() bool { return n.canResize } // PropScale returns if this node is proportionally scaled func (n *Node) PropScale() bool { return n.propScale } // SetResize sets the resize flag func (n *Node) SetResize(b bool) { n.canResize = b } // SetPropScale sets the propScale flag func (n *Node) SetPropScale(b bool) { n.propScale = b } // Children returns this node's children func (n *Node) Children() []*Node { return n.children } // GetNode returns the node with the given id in the tree of children // that this node has access to or nil if the node with that id cannot be found func (n *Node) GetNode(id uint64) *Node { if n.id == id && n.IsLeaf() { return n } for _, c := range n.children { if c.id == id && c.IsLeaf() { return c } gc := c.GetNode(id) if gc != nil { return gc } } return nil } func (n *Node) vResizeSplit(i int, size int) bool { if i < 0 || i >= len(n.children) { return false } var c1, c2 *Node if i == len(n.children)-1 { c1, c2 = n.children[i-1], n.children[i] } else { c1, c2 = n.children[i], n.children[i+1] } toth := c1.H + c2.H if size >= toth { return false } c2.Y = c1.Y + size c1.Resize(c1.W, size) c2.Resize(c2.W, toth-size) n.markSizes() n.alignSizes(n.W, n.H) return true } func (n *Node) hResizeSplit(i int, size int) bool { if i < 0 || i >= len(n.children) { return false } var c1, c2 *Node if i == len(n.children)-1 { c1, c2 = n.children[i-1], n.children[i] } else { c1, c2 = n.children[i], n.children[i+1] } totw := c1.W + c2.W if size >= totw { return false } c2.X = c1.X + size c1.Resize(size, c1.H) c2.Resize(totw-size, c2.H) n.markSizes() n.alignSizes(n.W, n.H) return true } // ResizeSplit resizes a certain split to a given size func (n *Node) ResizeSplit(size int) bool { // TODO: `size < 0` does not work for some reason if size <= 0 { return false } if len(n.parent.children) <= 1 { // cannot resize a lone node return false } ind := 0 for i, c := range n.parent.children { if c.id == n.id { ind = i } } if n.parent.Kind == STVert { return n.parent.vResizeSplit(ind, size) } return n.parent.hResizeSplit(ind, size) } // Resize sets this node's size and resizes all children accordingly func (n *Node) Resize(w, h int) { n.W, n.H = w, h if n.IsLeaf() { return } x, y := n.X, n.Y totw, toth := 0, 0 for _, c := range n.children { cW := int(float64(w) * c.propW) cH := int(float64(h) * c.propH) c.X, c.Y = x, y c.Resize(cW, cH) if n.Kind == STHoriz { x += cW totw += cW } else { y += cH toth += cH } } n.alignSizes(totw, toth) } func (n *Node) alignSizes(totw, toth int) { // Make sure that there are no off-by-one problems with the rounding // of the sizes by making the final split fill the screen if n.Kind == STVert && toth != n.H { last := n.children[len(n.children)-1] last.Resize(last.W, last.H+n.H-toth) } else if n.Kind == STHoriz && totw != n.W { last := n.children[len(n.children)-1] last.Resize(last.W+n.W-totw, last.H) } } // Resets all proportions for children func (n *Node) markSizes() { for _, c := range n.children { c.propW = float64(c.W) / float64(n.W) c.propH = float64(c.H) / float64(n.H) c.markSizes() } } func (n *Node) markResize() { n.markSizes() n.Resize(n.W, n.H) } // vsplits a vertical split and returns the id of the new split func (n *Node) vVSplit(right bool) uint64 { ind := 0 for i, c := range n.parent.children { if c.id == n.id { ind = i } } return n.parent.hVSplit(ind, right) } // hsplits a horizontal split func (n *Node) hHSplit(bottom bool) uint64 { ind := 0 for i, c := range n.parent.children { if c.id == n.id { ind = i } } return n.parent.vHSplit(ind, bottom) } // Returns the size of the non-resizable area and the number of resizable // splits func (n *Node) getResizeInfo(h bool) (int, int) { numr := 0 numnr := 0 nonr := 0 for _, c := range n.children { if !c.CanResize() { if h { nonr += c.H } else { nonr += c.W } numnr++ } else { numr++ } } // if there are no resizable splits make them all resizable if numr == 0 { numr = numnr } return nonr, numr } func (n *Node) applyNewSize(size int, h bool) { a := n.X if h { a = n.Y } for _, c := range n.children { if h { c.Y = a } else { c.X = a } if c.CanResize() { if h { c.Resize(c.W, size) } else { c.Resize(size, c.H) } } if h { a += c.H } else { a += c.H } } n.markResize() } // hsplits a vertical split func (n *Node) vHSplit(i int, right bool) uint64 { if n.IsLeaf() { newid := NewID() hn1 := NewNode(STHoriz, n.X, n.Y, n.W, n.H/2, n, n.id) hn2 := NewNode(STHoriz, n.X, n.Y+hn1.H, n.W, n.H/2, n, newid) if !right { hn1.id, hn2.id = hn2.id, hn1.id } n.children = append(n.children, hn1, hn2) n.markResize() return newid } else { nonrh, numr := n.getResizeInfo(true) // size of resizable area height := (n.H - nonrh) / (numr + 1) newid := NewID() hn := NewNode(STHoriz, n.X, 0, n.W, height, n, newid) // insert the node into the correct slot n.children = append(n.children, nil) inspos := i if right { inspos++ } copy(n.children[inspos+1:], n.children[inspos:]) n.children[inspos] = hn n.applyNewSize(height, true) return newid } } // vsplits a horizontal split func (n *Node) hVSplit(i int, right bool) uint64 { if n.IsLeaf() { newid := NewID() vn1 := NewNode(STVert, n.X, n.Y, n.W/2, n.H, n, n.id) vn2 := NewNode(STVert, n.X+vn1.W, n.Y, n.W/2, n.H, n, newid) if !right { vn1.id, vn2.id = vn2.id, vn1.id } n.children = append(n.children, vn1, vn2) n.markResize() return newid } else { nonrw, numr := n.getResizeInfo(false) width := (n.W - nonrw) / (numr + 1) newid := NewID() vn := NewNode(STVert, 0, n.Y, width, n.H, n, newid) // Inser the node into the correct slot n.children = append(n.children, nil) inspos := i if right { inspos++ } copy(n.children[inspos+1:], n.children[inspos:]) n.children[inspos] = vn n.applyNewSize(width, false) return newid } } // HSplit creates a horizontal split and returns the id of the new split // bottom specifies if the new split should be created on the top or bottom // of the current split func (n *Node) HSplit(bottom bool) uint64 { if !n.IsLeaf() { return 0 } if n.Kind == STUndef { n.Kind = STVert } if n.Kind == STVert { return n.vHSplit(0, bottom) } return n.hHSplit(bottom) } // VSplit creates a vertical split and returns the id of the new split // right specifies if the new split should be created on the right or left // of the current split func (n *Node) VSplit(right bool) uint64 { if !n.IsLeaf() { return 0 } if n.Kind == STUndef { n.Kind = STHoriz } if n.Kind == STVert { return n.vVSplit(right) } return n.hVSplit(0, right) } // unsplits the child of a split func (n *Node) unsplit(i int, h bool) { copy(n.children[i:], n.children[i+1:]) n.children[len(n.children)-1] = nil n.children = n.children[:len(n.children)-1] nonrs, numr := n.getResizeInfo(h) if numr == 0 { // This means that this was the last child // The parent will get cleaned up in the next iteration and // will resolve all sizing issues with its parent return } size := (n.W - nonrs) / numr if h { size = (n.H - nonrs) / numr } n.applyNewSize(size, h) } // Unsplit deletes this split and resizes everything // else accordingly func (n *Node) Unsplit() bool { if !n.IsLeaf() || n.parent == nil { return false } ind := 0 for i, c := range n.parent.children { if c.id == n.id { ind = i } } if n.parent.Kind == STVert { n.parent.unsplit(ind, true) } else { n.parent.unsplit(ind, false) } if n.parent.IsLeaf() { return n.parent.Unsplit() } return true } // String returns the string form of the node and all children (used for debugging) func (n *Node) String() string { var strf func(n *Node, ident int) string strf = func(n *Node, ident int) string { marker := "|" if n.Kind == STHoriz { marker = "-" } str := fmt.Sprint(strings.Repeat("\t", ident), marker, n.View, n.id) if n.IsLeaf() { str += "ðŸ" } str += "\n" for _, c := range n.children { str += strf(c, ident+1) } return str } return strf(n, 0) } micro-2.0.14/internal/views/splits_test.go0000664000175000017510000000040014663411671020120 0ustar nileshnileshpackage views import ( "fmt" "testing" ) func TestHSplit(t *testing.T) { root := NewRoot(0, 0, 80, 80) n1 := root.VSplit(true) root.GetNode(n1).VSplit(true) root.GetNode(root.id).ResizeSplit(7) root.Resize(120, 120) fmt.Println(root.String()) } micro-2.0.14/pkg/0000775000175000017510000000000014663411671013032 5ustar nileshnileshmicro-2.0.14/pkg/highlight/0000775000175000017510000000000014663411671015001 5ustar nileshnileshmicro-2.0.14/pkg/highlight/highlighter.go0000664000175000017510000002405014663411671017627 0ustar nileshnileshpackage highlight import ( "regexp" "strings" ) func sliceStart(slc []byte, index int) []byte { len := len(slc) i := 0 totalSize := 0 for totalSize < len { if i >= index { return slc[totalSize:] } _, _, size := DecodeCharacter(slc[totalSize:]) totalSize += size i++ } return slc[totalSize:] } func sliceEnd(slc []byte, index int) []byte { len := len(slc) i := 0 totalSize := 0 for totalSize < len { if i >= index { return slc[:totalSize] } _, _, size := DecodeCharacter(slc[totalSize:]) totalSize += size i++ } return slc[:totalSize] } // RunePos returns the rune index of a given byte index // This could cause problems if the byte index is between code points func runePos(p int, str []byte) int { if p < 0 { return 0 } if p >= len(str) { return CharacterCount(str) } return CharacterCount(str[:p]) } func combineLineMatch(src, dst LineMatch) LineMatch { for k, v := range src { if g, ok := dst[k]; ok { if g == 0 { dst[k] = v } } else { dst[k] = v } } return dst } // A State represents the region at the end of a line type State *region // LineStates is an interface for a buffer-like object which can also store the states and matches for every line type LineStates interface { LineBytes(n int) []byte LinesNum() int State(lineN int) State SetState(lineN int, s State) SetMatch(lineN int, m LineMatch) Lock() Unlock() } // A Highlighter contains the information needed to highlight a string type Highlighter struct { lastRegion *region Def *Def } // NewHighlighter returns a new highlighter from the given syntax definition func NewHighlighter(def *Def) *Highlighter { h := new(Highlighter) h.Def = def return h } // LineMatch represents the syntax highlighting matches for one line. Each index where the coloring is changed is marked with that // color's group (represented as one byte) type LineMatch map[int]Group func findIndex(regex *regexp.Regexp, skip *regexp.Regexp, str []byte) []int { var strbytes []byte if skip != nil { strbytes = skip.ReplaceAllFunc(str, func(match []byte) []byte { res := make([]byte, CharacterCount(match)) return res }) } else { strbytes = str } match := regex.FindIndex(strbytes) if match == nil { return nil } // return []int{match.Index, match.Index + match.Length} return []int{runePos(match[0], str), runePos(match[1], str)} } func findAllIndex(regex *regexp.Regexp, str []byte) [][]int { matches := regex.FindAllIndex(str, -1) for i, m := range matches { matches[i][0] = runePos(m[0], str) matches[i][1] = runePos(m[1], str) } return matches } func (h *Highlighter) highlightRegion(highlights LineMatch, start int, canMatchEnd bool, lineNum int, line []byte, curRegion *region, statesOnly bool) LineMatch { lineLen := CharacterCount(line) if start == 0 { if !statesOnly { if _, ok := highlights[0]; !ok { highlights[0] = curRegion.group } } } var firstRegion *region firstLoc := []int{lineLen, 0} searchNesting := true endLoc := findIndex(curRegion.end, curRegion.skip, line) if endLoc != nil { if start == endLoc[0] { searchNesting = false } else { firstLoc = endLoc } } if searchNesting { for _, r := range curRegion.rules.regions { loc := findIndex(r.start, r.skip, line) if loc != nil { if loc[0] < firstLoc[0] { firstLoc = loc firstRegion = r } } } } if firstRegion != nil && firstLoc[0] != lineLen { if !statesOnly { highlights[start+firstLoc[0]] = firstRegion.limitGroup } h.highlightEmptyRegion(highlights, start+firstLoc[1], canMatchEnd, lineNum, sliceStart(line, firstLoc[1]), statesOnly) h.highlightRegion(highlights, start+firstLoc[1], canMatchEnd, lineNum, sliceStart(line, firstLoc[1]), firstRegion, statesOnly) return highlights } if !statesOnly { fullHighlights := make([]Group, lineLen) for i := 0; i < len(fullHighlights); i++ { fullHighlights[i] = curRegion.group } if searchNesting { for _, p := range curRegion.rules.patterns { if curRegion.group == curRegion.limitGroup || p.group == curRegion.limitGroup { matches := findAllIndex(p.regex, line) for _, m := range matches { if ((endLoc == nil) || (m[0] < endLoc[0])) { for i := m[0]; i < m[1]; i++ { fullHighlights[i] = p.group } } } } } } for i, h := range fullHighlights { if i == 0 || h != fullHighlights[i-1] { highlights[start+i] = h } } } loc := endLoc if loc != nil { if !statesOnly { highlights[start+loc[0]] = curRegion.limitGroup } if curRegion.parent == nil { if !statesOnly { highlights[start+loc[1]] = 0 } h.highlightEmptyRegion(highlights, start+loc[1], canMatchEnd, lineNum, sliceStart(line, loc[1]), statesOnly) return highlights } if !statesOnly { highlights[start+loc[1]] = curRegion.parent.group } h.highlightRegion(highlights, start+loc[1], canMatchEnd, lineNum, sliceStart(line, loc[1]), curRegion.parent, statesOnly) return highlights } if canMatchEnd { h.lastRegion = curRegion } return highlights } func (h *Highlighter) highlightEmptyRegion(highlights LineMatch, start int, canMatchEnd bool, lineNum int, line []byte, statesOnly bool) LineMatch { lineLen := CharacterCount(line) if lineLen == 0 { if canMatchEnd { h.lastRegion = nil } return highlights } var firstRegion *region firstLoc := []int{lineLen, 0} for _, r := range h.Def.rules.regions { loc := findIndex(r.start, r.skip, line) if loc != nil { if loc[0] < firstLoc[0] { firstLoc = loc firstRegion = r } } } if firstRegion != nil && firstLoc[0] != lineLen { if !statesOnly { highlights[start+firstLoc[0]] = firstRegion.limitGroup } h.highlightEmptyRegion(highlights, start, false, lineNum, sliceEnd(line, firstLoc[0]), statesOnly) h.highlightRegion(highlights, start+firstLoc[1], canMatchEnd, lineNum, sliceStart(line, firstLoc[1]), firstRegion, statesOnly) return highlights } if statesOnly { if canMatchEnd { h.lastRegion = nil } return highlights } fullHighlights := make([]Group, len(line)) for _, p := range h.Def.rules.patterns { matches := findAllIndex(p.regex, line) for _, m := range matches { for i := m[0]; i < m[1]; i++ { fullHighlights[i] = p.group } } } for i, h := range fullHighlights { if i == 0 || h != fullHighlights[i-1] { // if _, ok := highlights[start+i]; !ok { highlights[start+i] = h // } } } if canMatchEnd { h.lastRegion = nil } return highlights } // HighlightString syntax highlights a string // Use this function for simple syntax highlighting and use the other functions for // more advanced syntax highlighting. They are optimized for quick rehighlighting of the same // text with minor changes made func (h *Highlighter) HighlightString(input string) []LineMatch { lines := strings.Split(input, "\n") var lineMatches []LineMatch for i := 0; i < len(lines); i++ { line := []byte(lines[i]) highlights := make(LineMatch) if i == 0 || h.lastRegion == nil { lineMatches = append(lineMatches, h.highlightEmptyRegion(highlights, 0, true, i, line, false)) } else { lineMatches = append(lineMatches, h.highlightRegion(highlights, 0, true, i, line, h.lastRegion, false)) } } return lineMatches } // HighlightStates correctly sets all states for the buffer func (h *Highlighter) HighlightStates(input LineStates) { for i := 0; ; i++ { input.Lock() if i >= input.LinesNum() { input.Unlock() break } line := input.LineBytes(i) // highlights := make(LineMatch) if i == 0 || h.lastRegion == nil { h.highlightEmptyRegion(nil, 0, true, i, line, true) } else { h.highlightRegion(nil, 0, true, i, line, h.lastRegion, true) } curState := h.lastRegion input.SetState(i, curState) input.Unlock() } } // HighlightMatches sets the matches for each line from startline to endline // It sets all other matches in the buffer to nil to conserve memory // This assumes that all the states are set correctly func (h *Highlighter) HighlightMatches(input LineStates, startline, endline int) { for i := startline; i <= endline; i++ { input.Lock() if i >= input.LinesNum() { input.Unlock() break } line := input.LineBytes(i) highlights := make(LineMatch) var match LineMatch if i == 0 || input.State(i-1) == nil { match = h.highlightEmptyRegion(highlights, 0, true, i, line, false) } else { match = h.highlightRegion(highlights, 0, true, i, line, input.State(i-1), false) } input.SetMatch(i, match) input.Unlock() } } // ReHighlightStates will scan down from `startline` and set the appropriate end of line state // for each line until it comes across a line whose state does not change // returns the number of the final line func (h *Highlighter) ReHighlightStates(input LineStates, startline int) int { // lines := input.LineData() h.lastRegion = nil if startline > 0 { input.Lock() if startline-1 < input.LinesNum() { h.lastRegion = input.State(startline - 1) } input.Unlock() } for i := startline; ; i++ { input.Lock() if i >= input.LinesNum() { input.Unlock() break } line := input.LineBytes(i) // highlights := make(LineMatch) // var match LineMatch if i == 0 || h.lastRegion == nil { h.highlightEmptyRegion(nil, 0, true, i, line, true) } else { h.highlightRegion(nil, 0, true, i, line, h.lastRegion, true) } curState := h.lastRegion lastState := input.State(i) input.SetState(i, curState) input.Unlock() if curState == lastState { return i } } return input.LinesNum() - 1 } // ReHighlightLine will rehighlight the state and match for a single line func (h *Highlighter) ReHighlightLine(input LineStates, lineN int) { input.Lock() defer input.Unlock() line := input.LineBytes(lineN) highlights := make(LineMatch) h.lastRegion = nil if lineN > 0 { h.lastRegion = input.State(lineN - 1) } var match LineMatch if lineN == 0 || h.lastRegion == nil { match = h.highlightEmptyRegion(highlights, 0, true, lineN, line, false) } else { match = h.highlightRegion(highlights, 0, true, lineN, line, h.lastRegion, false) } curState := h.lastRegion input.SetMatch(lineN, match) input.SetState(lineN, curState) } micro-2.0.14/pkg/highlight/parser.go0000664000175000017510000002606414663411671016634 0ustar nileshnileshpackage highlight import ( "bytes" "errors" "fmt" "regexp" "gopkg.in/yaml.v2" ) // A Group represents a syntax group type Group uint8 // Groups contains all of the groups that are defined // You can access them in the map via their string name var Groups map[string]Group var numGroups Group // String returns the group name attached to the specific group func (g Group) String() string { for k, v := range Groups { if v == g { return k } } return "" } // A Def is a full syntax definition for a language // It has a filetype, information about how to detect the filetype based // on filename or header (the first line of the file) // Then it has the rules which define how to highlight the file type Def struct { *Header rules *rules } type Header struct { FileType string FileNameRegex *regexp.Regexp HeaderRegex *regexp.Regexp SignatureRegex *regexp.Regexp } type HeaderYaml struct { FileType string `yaml:"filetype"` Detect struct { FNameRegexStr string `yaml:"filename"` HeaderRegexStr string `yaml:"header"` SignatureRegexStr string `yaml:"signature"` } `yaml:"detect"` } type File struct { FileType string yamlSrc map[interface{}]interface{} } // A Pattern is one simple syntax rule // It has a group that the rule belongs to, as well as // the regular expression to match the pattern type pattern struct { group Group regex *regexp.Regexp } // rules defines which patterns and regions can be used to highlight // a filetype type rules struct { regions []*region patterns []*pattern includes []string } // A region is a highlighted region (such as a multiline comment, or a string) // It belongs to a group, and has start and end regular expressions // A region also has rules of its own that only apply when matching inside the // region and also rules from the above region do not match inside this region // Note that a region may contain more regions type region struct { group Group limitGroup Group parent *region start *regexp.Regexp end *regexp.Regexp skip *regexp.Regexp rules *rules } func init() { Groups = make(map[string]Group) } // MakeHeader takes a header (.hdr file) file and parses the header // Header files make parsing more efficient when you only want to compute // on the headers of syntax files // A yaml file might take ~400us to parse while a header file only takes ~20us func MakeHeader(data []byte) (*Header, error) { lines := bytes.Split(data, []byte{'\n'}) if len(lines) < 4 { return nil, errors.New("Header file has incorrect format") } header := new(Header) var err error header.FileType = string(lines[0]) fnameRegexStr := string(lines[1]) headerRegexStr := string(lines[2]) signatureRegexStr := string(lines[3]) if fnameRegexStr != "" { header.FileNameRegex, err = regexp.Compile(fnameRegexStr) } if err == nil && headerRegexStr != "" { header.HeaderRegex, err = regexp.Compile(headerRegexStr) } if err == nil && signatureRegexStr != "" { header.SignatureRegex, err = regexp.Compile(signatureRegexStr) } if err != nil { return nil, err } return header, nil } // MakeHeaderYaml takes a yaml spec for a syntax file and parses the // header func MakeHeaderYaml(data []byte) (*Header, error) { var hdrYaml HeaderYaml err := yaml.Unmarshal(data, &hdrYaml) if err != nil { return nil, err } header := new(Header) header.FileType = hdrYaml.FileType if hdrYaml.Detect.FNameRegexStr != "" { header.FileNameRegex, err = regexp.Compile(hdrYaml.Detect.FNameRegexStr) } if err == nil && hdrYaml.Detect.HeaderRegexStr != "" { header.HeaderRegex, err = regexp.Compile(hdrYaml.Detect.HeaderRegexStr) } if err == nil && hdrYaml.Detect.SignatureRegexStr != "" { header.SignatureRegex, err = regexp.Compile(hdrYaml.Detect.SignatureRegexStr) } if err != nil { return nil, err } return header, nil } // MatchFileName will check the given file name with the stored regex func (header *Header) MatchFileName(filename string) bool { if header.FileNameRegex != nil { return header.FileNameRegex.MatchString(filename) } return false } func (header *Header) MatchFileHeader(firstLine []byte) bool { if header.HeaderRegex != nil { return header.HeaderRegex.Match(firstLine) } return false } // HasFileSignature checks the presence of a stored signature func (header *Header) HasFileSignature() bool { return header.SignatureRegex != nil } // MatchFileSignature will check the given line with the stored regex func (header *Header) MatchFileSignature(line []byte) bool { if header.SignatureRegex != nil { return header.SignatureRegex.Match(line) } return false } func ParseFile(input []byte) (f *File, err error) { // This is just so if we have an error, we can exit cleanly and return the parse error to the user defer func() { if r := recover(); r != nil { var ok bool err, ok = r.(error) if !ok { err = fmt.Errorf("pkg: %v", r) } } }() var rules map[interface{}]interface{} if err = yaml.Unmarshal(input, &rules); err != nil { return nil, err } f = new(File) f.yamlSrc = rules for k, v := range rules { if k == "filetype" { filetype := v.(string) if filetype == "" { return nil, errors.New("empty filetype") } f.FileType = filetype break } } if f.FileType == "" { return nil, errors.New("missing filetype") } return f, err } // ParseDef parses an input syntax file into a highlight Def func ParseDef(f *File, header *Header) (s *Def, err error) { // This is just so if we have an error, we can exit cleanly and return the parse error to the user defer func() { if r := recover(); r != nil { var ok bool err, ok = r.(error) if !ok { err = fmt.Errorf("pkg: %v", r) } } }() src := f.yamlSrc s = new(Def) s.Header = header for k, v := range src { if k == "rules" { inputRules := v.([]interface{}) rules, err := parseRules(inputRules, nil) if err != nil { return nil, err } s.rules = rules } } if s.rules == nil { // allow empty rules s.rules = new(rules) } return s, err } // HasIncludes returns whether this syntax def has any include statements func HasIncludes(d *Def) bool { hasIncludes := len(d.rules.includes) > 0 for _, r := range d.rules.regions { hasIncludes = hasIncludes || hasIncludesInRegion(r) } return hasIncludes } func hasIncludesInRegion(region *region) bool { hasIncludes := len(region.rules.includes) > 0 for _, r := range region.rules.regions { hasIncludes = hasIncludes || hasIncludesInRegion(r) } return hasIncludes } // GetIncludes returns a list of filetypes that are included by this syntax def func GetIncludes(d *Def) []string { includes := d.rules.includes for _, r := range d.rules.regions { includes = append(includes, getIncludesInRegion(r)...) } return includes } func getIncludesInRegion(region *region) []string { includes := region.rules.includes for _, r := range region.rules.regions { includes = append(includes, getIncludesInRegion(r)...) } return includes } // ResolveIncludes will sort out the rules for including other filetypes // You should call this after parsing all the Defs func ResolveIncludes(def *Def, files []*File) { resolveIncludesInDef(files, def) } func resolveIncludesInDef(files []*File, d *Def) { for _, lang := range d.rules.includes { for _, searchFile := range files { if lang == searchFile.FileType { searchDef, _ := ParseDef(searchFile, nil) d.rules.patterns = append(d.rules.patterns, searchDef.rules.patterns...) d.rules.regions = append(d.rules.regions, searchDef.rules.regions...) } } } for _, r := range d.rules.regions { resolveIncludesInRegion(files, r) r.parent = nil } } func resolveIncludesInRegion(files []*File, region *region) { for _, lang := range region.rules.includes { for _, searchFile := range files { if lang == searchFile.FileType { searchDef, _ := ParseDef(searchFile, nil) region.rules.patterns = append(region.rules.patterns, searchDef.rules.patterns...) region.rules.regions = append(region.rules.regions, searchDef.rules.regions...) } } } for _, r := range region.rules.regions { resolveIncludesInRegion(files, r) r.parent = region } } func parseRules(input []interface{}, curRegion *region) (ru *rules, err error) { defer func() { if r := recover(); r != nil { var ok bool err, ok = r.(error) if !ok { err = fmt.Errorf("pkg: %v", r) } } }() ru = new(rules) for _, v := range input { rule := v.(map[interface{}]interface{}) for k, val := range rule { group := k switch object := val.(type) { case string: if object == "" { return nil, fmt.Errorf("Empty rule %s", k) } if k == "include" { ru.includes = append(ru.includes, object) } else { // Pattern r, err := regexp.Compile(object) if err != nil { return nil, err } groupStr := group.(string) if _, ok := Groups[groupStr]; !ok { numGroups++ Groups[groupStr] = numGroups } groupNum := Groups[groupStr] ru.patterns = append(ru.patterns, &pattern{groupNum, r}) } case map[interface{}]interface{}: // region region, err := parseRegion(group.(string), object, curRegion) if err != nil { return nil, err } ru.regions = append(ru.regions, region) default: return nil, fmt.Errorf("Bad type %T", object) } } } return ru, nil } func parseRegion(group string, regionInfo map[interface{}]interface{}, prevRegion *region) (r *region, err error) { defer func() { if r := recover(); r != nil { var ok bool err, ok = r.(error) if !ok { err = fmt.Errorf("pkg: %v", r) } } }() r = new(region) if _, ok := Groups[group]; !ok { numGroups++ Groups[group] = numGroups } groupNum := Groups[group] r.group = groupNum r.parent = prevRegion // start is mandatory if start, ok := regionInfo["start"]; ok { start := start.(string) if start == "" { return nil, fmt.Errorf("Empty start in %s", group) } r.start, err = regexp.Compile(start) if err != nil { return nil, err } } else { return nil, fmt.Errorf("Missing start in %s", group) } // end is mandatory if end, ok := regionInfo["end"]; ok { end := end.(string) if end == "" { return nil, fmt.Errorf("Empty end in %s", group) } r.end, err = regexp.Compile(end) if err != nil { return nil, err } } else { return nil, fmt.Errorf("Missing end in %s", group) } // skip is optional if skip, ok := regionInfo["skip"]; ok { skip := skip.(string) if skip == "" { return nil, fmt.Errorf("Empty skip in %s", group) } r.skip, err = regexp.Compile(skip) if err != nil { return nil, err } } // limit-color is optional if groupStr, ok := regionInfo["limit-group"]; ok { groupStr := groupStr.(string) if groupStr == "" { return nil, fmt.Errorf("Empty limit-group in %s", group) } if _, ok := Groups[groupStr]; !ok { numGroups++ Groups[groupStr] = numGroups } groupNum := Groups[groupStr] r.limitGroup = groupNum if err != nil { return nil, err } } else { r.limitGroup = r.group } r.rules, err = parseRules(regionInfo["rules"].([]interface{}), r) if err != nil { return nil, err } return r, nil } micro-2.0.14/pkg/highlight/unicode.go0000664000175000017510000000312114663411671016753 0ustar nileshnileshpackage highlight import ( "unicode" "unicode/utf8" ) var minMark = rune(unicode.Mark.R16[0].Lo) func isMark(r rune) bool { // Fast path if r < minMark { return false } return unicode.In(r, unicode.Mark) } // DecodeCharacter returns the next character from an array of bytes // A character is a rune along with any accompanying combining runes func DecodeCharacter(b []byte) (rune, []rune, int) { r, size := utf8.DecodeRune(b) b = b[size:] c, s := utf8.DecodeRune(b) var combc []rune for isMark(c) { combc = append(combc, c) size += s b = b[s:] c, s = utf8.DecodeRune(b) } return r, combc, size } // DecodeCharacterInString returns the next character from a string // A character is a rune along with any accompanying combining runes func DecodeCharacterInString(str string) (rune, []rune, int) { r, size := utf8.DecodeRuneInString(str) str = str[size:] c, s := utf8.DecodeRuneInString(str) var combc []rune for isMark(c) { combc = append(combc, c) size += s str = str[s:] c, s = utf8.DecodeRuneInString(str) } return r, combc, size } // CharacterCount returns the number of characters in a byte array // Similar to utf8.RuneCount but for unicode characters func CharacterCount(b []byte) int { s := 0 for len(b) > 0 { r, size := utf8.DecodeRune(b) if !isMark(r) { s++ } b = b[size:] } return s } // CharacterCount returns the number of characters in a string // Similar to utf8.RuneCountInString but for unicode characters func CharacterCountInString(str string) int { s := 0 for _, r := range str { if !isMark(r) { s++ } } return s } micro-2.0.14/runtime/0000775000175000017510000000000014663411671013734 5ustar nileshnileshmicro-2.0.14/runtime/README.md0000664000175000017510000000046314663411671015216 0ustar nileshnilesh# Runtime files for Micro This directory will be embedded in the Go binary for portability, but it may just as well be put in `~/.config/micro`. If you would like to make your own colorschemes and syntax files, you can put them in `~/.config/micro/colorschemes` and `~/.config/micro/syntax` respectively. micro-2.0.14/runtime/colorschemes/0000775000175000017510000000000014663411671016422 5ustar nileshnileshmicro-2.0.14/runtime/colorschemes/atom-dark.micro0000664000175000017510000000232514663411671021336 0ustar nileshnileshcolor-link default "#C5C8C6,#1D1F21" color-link comment "#7C7C7C,#1D1F21" color-link identifier "#F9EE98,#1D1F21" color-link constant "#FF73FD,#1D1F21" color-link constant.string "#A8FF60,#1D1F21" color-link statement "#96CBFE,#1D1F21" color-link symbol "#96CBFE,#1D1F21" color-link preproc "#62B1FE,#1D1F21" color-link type "#C6C5FE,#1D1F21" color-link special "#A6E22E,#1D1F21" color-link underlined "#D33682,#1D1F21" color-link error "bold #FF4444,#1D1F21" color-link todo "bold #FF8844,#1D1F21" color-link hlsearch "#000000,#B4EC85" color-link statusline "#1D1F21,#C5C8C6" color-link tabbar "#1D1F21,#C5C8C6" color-link indent-char "#505050,#1D1F21" color-link line-number "#656866,#232526" color-link current-line-number "#656866,#1D1F21" color-link diff-added "#00AF00" color-link diff-modified "#FFAF00" color-link diff-deleted "#D70000" color-link gutter-error "#FF4444,#1D1F21" color-link gutter-warning "#EEEE77,#1D1F21" color-link cursor-line "#2D2F31" color-link color-column "#2D2F31" #color-link symbol.brackets "#96CBFE,#1D1F21" #No extended types (bool in C, etc.) #color-link type.extended "default" #Plain brackets color-link match-brace "#1D1F21,#62B1FE" color-link tab-error "#D75F5F" color-link trailingws "#D75F5F" micro-2.0.14/runtime/colorschemes/bubblegum.micro0000664000175000017510000000172714663411671021430 0ustar nileshnileshcolor-link default "241,231" color-link comment "246,231" color-link constant "130,231" color-link constant.string "136,231" color-link constant.number "131,231" color-link identifier "133,231" color-link statement "32,231" color-link symbol "32,231" color-link preproc "28,231" color-link type "61,231" color-link special "167,231" color-link error "231, 160" color-link underlined "underline 241,231" color-link todo "246,231" color-link hlsearch "231,136" color-link statusline "241,254" color-link tabbar "241,254" color-link diff-added "34" color-link diff-modified "214" color-link diff-deleted "160" color-link gutter-error "197,231" color-link gutter-warning "134,231" color-link line-number "246,254" color-link cursor-line "254" color-link color-column "254" #No extended types (bool in C, &c.) and plain brackets color-link type.extended "241,231" color-link symbol.brackets "241,231" color-link match-brace "231,28" color-link tab-error "210" color-link trailingws "210" micro-2.0.14/runtime/colorschemes/cmc-16.micro0000664000175000017510000000322014663411671020440 0ustar nileshnilesh#CaptainMcClellan's personal color scheme. #16 colour version. color-link comment "bold black" color-link constant "cyan" color-link constant.bool "bold cyan" color-link constant.bool.true "bold green" color-link constant.bool.false "bold red" color-link constant.string "yellow" color-link constant.string.url "underline blue, white" #color-link constant.number "constant" color-link constant.specialChar "bold magenta" color-link identifier "bold red" color-link identifier.macro "bold red" color-link identifier.var "bold blue" #color-link identifier.class "bold green" color-link identifier.class "bold white" color-link statement "bold yellow" color-link symbol "red" color-link symbol.brackets "blue" color-link symbol.tag "bold blue" color-link symbol.tag.extended "bold green" color-link preproc "bold cyan" color-link type "green" color-link type.keyword "bold green" color-link special "magenta" color-link ignore "default" color-link error "bold ,brightred" color-link todo "underline black,brightyellow" color-link hlsearch "white,darkgreen" color-link indent-char ",brightgreen" color-link line-number "green" color-link line-number.scrollbar "green" color-link statusline "white,blue" color-link tabbar "white,blue" color-link current-line-number "red" color-link current-line-number.scroller "red" color-link diff-added "green" color-link diff-modified "yellow" color-link diff-deleted "red" color-link gutter-error ",red" color-link gutter-warning "red" color-link color-column "cyan" color-link underlined.url "underline blue, white" color-link divider "blue" color-link match-brace "black,cyan" color-link tab-error "brightred" color-link trailingws "brightred" micro-2.0.14/runtime/colorschemes/cmc-tc.micro0000664000175000017510000000307514663411671020630 0ustar nileshnilesh#CaptainMcClellan's personal colour scheme. #Full colour edition. color-link default "#aaaaaa,#1e2124" color-link comment "bold #555555" color-link constant "#008888" #color-link constant.string "#888800" color-link constant.string "#a85700" color-link constant.specialChar "bold #ccccff" color-link identifier "bold #e34234" color-link identifier.macro "bold #e34234" color-link identifier.var "bold #5757ff" color-link identifier.class "bold #ffffff" color-link statement "bold #ffff55" color-link symbol "#722f37" color-link symbol.brackets "#4169e1" color-link symbol.tag "#5757ff" color-link preproc "bold #55ffff" color-link type "#3eb489" color-link type.keyword "bold #bdecb6" color-link special "#b57edc" color-link ignore "default" color-link error "bold ,#e34234" color-link todo "bold underline #888888,#f26522" color-link hlsearch "#b7b7b7,#32593d" color-link indent-char ",#bdecb6" color-link line-number "#bdecb6,#36393e" color-link line-number.scrollbar "#3eb489" color-link statusline "#aaaaaa,#8a496b" color-link tabbar "#aaaaaa,#8a496b" color-link current-line-number "bold #e34234,#424549" color-link current-line-number.scroller "red" color-link diff-added "#00AF00" color-link diff-modified "#FFAF00" color-link diff-deleted "#D70000" color-link gutter-error ",#e34234" color-link gutter-warning "#e34234" color-link color-column "#f26522" color-link constant.bool "bold #55ffff" color-link constant.bool.true "bold #85ff85" color-link constant.bool.false "bold #ff8585" color-link match-brace "#1e2124,#55ffff" color-link tab-error "#d75f5f" color-link trailingws "#d75f5f" micro-2.0.14/runtime/colorschemes/darcula.micro0000664000175000017510000000242514663411671021073 0ustar nileshnileshcolor-link default "#CCCCCC,#242424" color-link comment "#707070,#242424" color-link identifier "#FFC66D,#242424" color-link constant "#7A9EC2,#242424" color-link constant.string "#6A8759,#242424" color-link constant.string.char "#6A8759,#242424" color-link statement "#CC8242,#242424" color-link symbol "#CCCCCC,#242424" color-link preproc "#CC8242,#242424" color-link type "#CC8242,#242424" color-link special "#CC8242,#242424" color-link underlined "#D33682,#242424" color-link error "bold #CB4B16,#242424" color-link todo "bold #D33682,#242424" color-link hlsearch "#CCCCCC,#32593D" color-link statusline "#242424,#CCCCCC" color-link tabbar "#242424,#CCCCCC" color-link indent-char "#4F4F4F,#242424" color-link line-number "#666666,#2C2C2C" color-link current-line-number "#666666,#242424" color-link diff-added "#00AF00" color-link diff-modified "#FFAF00" color-link diff-deleted "#D70000" color-link gutter-error "#CB4B16,#242424" color-link gutter-warning "#E6DB74,#242424" color-link cursor-line "#2C2C2C" color-link color-column "#2C2C2C" #No extended types; Plain brackets. color-link type.extended "default" #color-link symbol.brackets "default" color-link symbol.tag "#AE81FF,#242424" color-link match-brace "#242424,#7A9EC2" color-link tab-error "#D75F5F" color-link trailingws "#D75F5F" micro-2.0.14/runtime/colorschemes/default.micro0000664000175000017510000000002214663411671021073 0ustar nileshnileshinclude "monokai" micro-2.0.14/runtime/colorschemes/dracula-tc.micro0000664000175000017510000000233314663411671021475 0ustar nileshnileshcolor-link default "#F8F8F2,#282A36" color-link comment "#6272A4" color-link identifier "#50FA7B" color-link identifier.class "#8BE9FD" color-link identifier.var "#F8F8F2" color-link constant "#BD93F9" color-link constant.number "#F8F8F2" color-link constant.string "#F1FA8C" color-link symbol "#FF79C6" color-link symbol.brackets "#F8F8F2" color-link symbol.tag "#AE81FF" color-link type "italic #8BE9FD" color-link type.keyword "#FF79C6" color-link special "#FF79C6" color-link statement "#FF79C6" color-link preproc "#FF79C6" color-link underlined "#FF79C6" color-link error "bold #FF5555" color-link todo "bold #FF79C6" color-link hlsearch "#282A36,#50FA7B" color-link diff-added "#50FA7B" color-link diff-modified "#FFB86C" color-link diff-deleted "#FF5555" color-link gutter-error "#FF5555" color-link gutter-warning "#E6DB74" color-link statusline "#282A36,#F8F8F2" color-link tabbar "#282A36,#F8F8F2" color-link indent-char "#6272A4" color-link line-number "#6272A4" color-link current-line-number "#F8F8F2" color-link cursor-line "#44475A,#F8F8F2" color-link color-column "#44475A" color-link type.extended "default" color-link match-brace "#282A36,#FF79C6" color-link tab-error "#D75F5F" color-link trailingws "#D75F5F" micro-2.0.14/runtime/colorschemes/dukedark-tc.micro0000664000175000017510000000300514663411671021651 0ustar nileshnileshcolor-link color-column "#001e28" color-link comment "#608b4e,#001e28" color-link constant.bool "#fd971f,#001e28" color-link constant "#fd971f,#001e28" color-link constant.string "#a0f000,#001e28" color-link constant.string.char "#a0f000,#001e28" color-link constant.string.url "#a0f000,#001e28" color-link current-line-number "bold #fd971f,#001e28" color-link cursor-line "#001923" color-link default "#ffffff,#001e28" color-link diff-added "#00c8a0,#001e28" color-link diff-modified "#fd971f,#001e28" color-link diff-deleted "#cb4b16,#001e28" color-link divider "#001e28,#d0d0d0" color-link error "#cb4b16,#001e28" color-link gutter-error "#cb4b16,#001e28" color-link gutter-warning "#fce94f,#001e28" color-link hlsearch "#ffffff,#005028" color-link identifier "#00c8a0,#001e28" color-link identifier.class "#00c8a0,#001e28" color-link indent-char "#a0a0a0,#001e28" color-link line-number "#a0a0a0,#001923" color-link preproc "bold #5aaae6,#001e28" color-link special "#a6e22e,#001e28" color-link statement "bold #5aaae6,#001e28" color-link statusline "#ffffff,#0078c8" color-link symbol "#00c8a0,#001e28" color-link symbol.brackets "#ffffff,#001e28" color-link symbol.tag "bold #5aaae6,#001e28" color-link tabbar "#001e28,#ffffff" color-link todo "#fce94f,#001e28" color-link type "bold #3cc83c,#001e28" color-link type.keyword "bold #5aaae6,#001e28" color-link type.extended "#ffffff,#001e28" color-link underlined "#608b4e,#001e28" color-link match-brace "#001e28,#5aaae6" color-link tab-error "#d75f5f" color-link trailingws "#d75f5f" micro-2.0.14/runtime/colorschemes/dukelight-tc.micro0000664000175000017510000000303114663411671022036 0ustar nileshnileshcolor-link color-column "#f0f0f0" color-link comment "#3f7f5f,#f0f0f0" color-link constant.bool "#641e00,#f0f0f0" color-link constant "#641e00,#f0f0f0" color-link constant.string "#0000ff,#f0f0f0" color-link constant.string.char "#0000ff,#f0f0f0" color-link constant.string.url "#0000ff,#f0f0f0" color-link current-line-number "bold #004080,#f0f0f0" color-link cursor-line "#e6e6e6" color-link default "#000000,#f0f0f0" color-link diff-added "#008040,#f0f0f0" color-link diff-modified "#641e00,#f0f0f0" color-link diff-deleted "#500000,#f0f0f0" color-link divider "#f0f0f0,#004080" color-link error "#500000,#f0f0f0" color-link gutter-error "#500000,#f0f0f0" color-link gutter-warning "#dcc800,#f0f0f0" color-link hlsearch "#000000,#b8d8e8" color-link identifier "bold #0078a0,#f0f0f0" color-link identifier.class "bold #0078a0,#f0f0f0" color-link indent-char "#404040,#f0f0f0" color-link line-number "#404040,#e6e6e6" color-link preproc "bold #780050,#f0f0f0" color-link special "bold #0078a0,#f0f0f0" color-link statement "bold #780050,#f0f0f0" color-link statusline "#ffffff,#0078c8" color-link symbol "bold #0078a0,#f0f0f0" color-link symbol.brackets "#000000,#f0f0f0" color-link symbol.tag "bold #780050,#f0f0f0" color-link tabbar "#f0f0f0,#004080" color-link todo "#dcc800,#f0f0f0" color-link type "bold #004080,#f0f0f0" color-link type.keyword "bold #780050,#f0f0f0" color-link type.extended "#000000,#f0f0f0" color-link underlined "#3f7f5f,#f0f0f0" color-link match-brace "#f0f0f0,#780050" color-link tab-error "#ff8787" color-link trailingws "#ff8787" micro-2.0.14/runtime/colorschemes/dukeubuntu-tc.micro0000664000175000017510000000300514663411671022252 0ustar nileshnileshcolor-link color-column "#2d0023" color-link comment "#886484,#2d0023" color-link constant.bool "#fd971f,#2d0023" color-link constant "#fd971f,#2d0023" color-link constant.string "#a0f000,#2d0023" color-link constant.string.char "#a0f000,#2d0023" color-link constant.string.url "#a0f000,#2d0023" color-link current-line-number "bold #fd971f,#2d0023" color-link cursor-line "#230019" color-link default "#ffffff,#2d0023" color-link diff-added "#00c8a0,#2d0023" color-link diff-modified "#fd971f,#2d0023" color-link diff-deleted "#cb4b16,#2d0023" color-link divider "#2d0023,#d0d0d0" color-link error "#cb4b16,#2d0023" color-link gutter-error "#cb4b16,#2d0023" color-link gutter-warning "#fce94f,#2d0023" color-link hlsearch "#ffffff,#005028" color-link identifier "#00c8a0,#2d0023" color-link identifier.class "#00c8a0,#2d0023" color-link indent-char "#a0a0a0,#2d0023" color-link line-number "#a0a0a0,#230019" color-link preproc "bold #5aaae6,#2d0023" color-link special "#a6e22e,#2d0023" color-link statement "bold #5aaae6,#2d0023" color-link statusline "#ffffff,#0078c8" color-link symbol "#00c8a0,#2d0023" color-link symbol.brackets "#ffffff,#2d0023" color-link symbol.tag "bold #5aaae6,#2d0023" color-link tabbar "#2d0023,#ffffff" color-link todo "#fce94f,#2d0023" color-link type "bold #3cc83c,#2d0023" color-link type.keyword "bold #5aaae6,#2d0023" color-link type.extended "#ffffff,#2d0023" color-link underlined "#886484,#2d0023" color-link match-brace "#2d0023,#5aaae6" color-link tab-error "#d75f5f" color-link trailingws "#d75f5f" micro-2.0.14/runtime/colorschemes/geany.micro0000664000175000017510000000156614663411671020570 0ustar nileshnilesh#Geany color-link comment "red" color-link constant "default" color-link constant.string "bold yellow" color-link identifier "default" color-link preproc "cyan" color-link special "blue" color-link statement "blue" color-link symbol "default" color-link symbol.tag "bold blue" color-link type "blue" color-link type.extended "default" color-link error "red" color-link todo "bold cyan" color-link hlsearch "black,brightcyan" color-link indent-char "bold black" color-link line-number "" color-link current-line-number "" color-link statusline "black,white" color-link tabbar "black,white" color-link color-column "bold geren" color-link diff-added "green" color-link diff-modified "yellow" color-link diff-deleted "red" color-link gutter-error ",red" color-link gutter-warning "red" color-link match-brace "black,cyan" color-link tab-error "brightred" color-link trailingws "brightred" micro-2.0.14/runtime/colorschemes/gotham.micro0000664000175000017510000000213414663411671020734 0ustar nileshnileshcolor-link default "#99D1CE,#0C1014" color-link comment "#245361,#0C1014" color-link identifier "#599CAB,#0C1014" color-link constant "#D26937,#0C1014" color-link constant.string "#2AA889,#0C1014" color-link constant.string.char "#D3EBE9,#0C1014" color-link statement "#599CAB,#0C1014" color-link preproc "#C23127,#0C1014" color-link type "#D26937,#0C1014" color-link special "#D26937,#0C1014" color-link underlined "#EDB443,#0C1014" color-link error "bold #C23127,#0C1014" color-link todo "bold #888CA6,#0C1014" color-link hlsearch "#091F2E,#EDB443" color-link statusline "#091F2E,#599CAB" color-link indent-char "#505050,#0C1014" color-link line-number "#245361,#11151C" color-link current-line-number "#599CAB,#11151C" color-link diff-added "#00AF00" color-link diff-modified "#FFAF00" color-link diff-deleted "#D70000" color-link gutter-error "#C23127,#11151C" color-link gutter-warning "#EDB443,#11151C" color-link cursor-line "#091F2E" color-link color-column "#11151C" color-link symbol "#99D1CE,#0C1014" color-link match-brace "#0C1014,#D26937" color-link tab-error "#D75F5F" color-link trailingws "#D75F5F" micro-2.0.14/runtime/colorschemes/gruvbox-tc.micro0000664000175000017510000000213014663411671021551 0ustar nileshnileshcolor-link default "#ebdbb2,#282828" color-link comment "#928374,#282828" color-link symbol "#d79921,#282828" color-link constant "#d3869b,#282828" color-link constant.string "#b8bb26,#282828" color-link constant.string.char "#b8bb26,#282828" color-link identifier "#8ec07c,#282828" color-link statement "#fb4934,#282828" color-link preproc "#fb4934,235" color-link type "#fb4934,#282828" color-link special "#d79921,#282828" color-link underlined "underline #458588,#282828" color-link error "#9d0006,#282828" color-link todo "bold #ebdbb2,#282828" color-link hlsearch "#282828,#fabd2f" color-link diff-added "#00AF00" color-link diff-modified "#FFAF00" color-link diff-deleted "#D70000" color-link gutter-error "#fb4934,#282828" color-link gutter-warning "#d79921,#282828" color-link line-number "#665c54,#3c3836" color-link current-line-number "#d79921,#282828" color-link cursor-line "#3c3836" color-link color-column "#79740e" color-link statusline "#ebdbb2,#665c54" color-link tabbar "#ebdbb2,#665c54" color-link match-brace "#282828,#d3869b" color-link tab-error "#d75f5f" color-link trailingws "#d75f5f" micro-2.0.14/runtime/colorschemes/gruvbox.micro0000664000175000017510000000143514663411671021154 0ustar nileshnileshcolor-link default "223,235" color-link comment "243,235" color-link constant "175,235" color-link constant.string "142,235" color-link identifier "109,235" color-link statement "124,235" color-link symbol "124,235" color-link preproc "72,235" color-link type "214,235" color-link special "172,235" color-link underlined "underline 109,235" color-link error "235,124" color-link todo "bold 223,235" color-link hlsearch "235,214" color-link diff-added "34" color-link diff-modified "214" color-link diff-deleted "160" color-link line-number "243,237" color-link current-line-number "172,235" color-link cursor-line "237" color-link color-column "237" color-link statusline "223,237" color-link tabbar "223,237" color-link match-brace "235,72" color-link tab-error "167" color-link trailingws "167" micro-2.0.14/runtime/colorschemes/material-tc.micro0000664000175000017510000000261314663411671021661 0ustar nileshnileshcolor-link color-column "#263238" color-link comment "#4F6875,#263238" color-link constant "#F07178,#263238" color-link constant.number "#F78C6A,#263238" color-link constant.specialChar "#89DDF3,#263238" color-link constant.string "#C3E88D,#263238" color-link current-line-number "#80DEEA,#263238" color-link cursor-line "#283942" color-link default "#EEFFFF,#263238" color-link diff-added "#00AF00" color-link diff-modified "#FFAF00" color-link diff-deleted "#D70000" color-link divider "#263238,#80DEEA" color-link error "bold #263238,#F07178" color-link gutter-error "#EEFFFF,#F07178" color-link gutter-warning "#EEFFFF,#FFF176" color-link hlsearch "#FFFFFF,#546E7A" color-link identifier "#82AAFF,#263238" color-link identifier.macro "#FFCB6B,#263238" color-link indent-char "#505050,#263238" color-link line-number "#656866,#283942" color-link preproc "#C792EA,#263238" color-link scrollbar "#80DEEA,#283942" color-link special "#C792EA,#263238" color-link statement "#C792EA,#263238" color-link statusline "#80DEEA,#3b4d56" color-link symbol "#96CBFE,#263238" color-link symbol.brackets "#89DDF3,#263238" color-link symbol.operator "#C792EA,#263238" color-link tabbar "#80DEEA,#3b4d56" color-link todo "bold #C792EA,#263238" color-link type "#FFCB6B,#263238" color-link underlined "underline #EEFFFF,#263238" color-link match-brace "#263238,#C792EA" color-link tab-error "#D75F5F" color-link trailingws "#D75F5F" micro-2.0.14/runtime/colorschemes/monokai-dark.micro0000664000175000017510000000171014663411671022030 0ustar nileshnileshcolor-link default "#D5D8D6,#1D0000" color-link comment "#75715E" color-link identifier "#66D9EF" color-link constant "#AE81FF" color-link constant.string "#E6DB74" color-link constant.string.char "#BDE6AD" color-link statement "#F92672" color-link preproc "#CB4B16" color-link type "#66D9EF" color-link special "#A6E22E" color-link underlined "#D33682" color-link error "bold #CB4B16" color-link todo "bold #D33682" color-link hlsearch "#1D0000,#E6DB74" color-link statusline "#282828,#F8F8F2" color-link indent-char "#505050,#282828" color-link line-number "#AAAAAA,#282828" color-link current-line-number "#AAAAAA,#1D0000" color-link diff-added "#00AF00" color-link diff-modified "#FFAF00" color-link diff-deleted "#D70000" color-link gutter-error "#CB4B16" color-link gutter-warning "#E6DB74" color-link cursor-line "#323232" color-link color-column "#323232" color-link match-brace "#1D0000,#AE81FF" color-link tab-error "#D75F5F" color-link trailingws "#D75F5F" micro-2.0.14/runtime/colorschemes/monokai.micro0000664000175000017510000000243614663411671021117 0ustar nileshnileshcolor-link default "#F8F8F2,#282828" color-link comment "#75715E,#282828" color-link identifier "#66D9EF,#282828" color-link constant "#AE81FF,#282828" color-link constant.string "#E6DB74,#282828" color-link constant.string.char "#BDE6AD,#282828" color-link statement "#F92672,#282828" color-link symbol.operator "#F92672,#282828" color-link preproc "#CB4B16,#282828" color-link type "#66D9EF,#282828" color-link special "#A6E22E,#282828" color-link underlined "#D33682,#282828" color-link error "bold #CB4B16,#282828" color-link todo "bold #D33682,#282828" color-link hlsearch "#282828,#E6DB74" color-link statusline "#282828,#F8F8F2" color-link tabbar "#282828,#F8F8F2" color-link indent-char "#505050,#282828" color-link line-number "#AAAAAA,#323232" color-link current-line-number "#AAAAAA,#282828" color-link diff-added "#00AF00" color-link diff-modified "#FFAF00" color-link diff-deleted "#D70000" color-link gutter-error "#CB4B16,#282828" color-link gutter-warning "#E6DB74,#282828" color-link cursor-line "#323232" color-link color-column "#323232" #No extended types; Plain brackets. color-link type.extended "default" #color-link symbol.brackets "default" color-link symbol.tag "#AE81FF,#282828" color-link match-brace "#282828,#AE81FF" color-link tab-error "#D75F5F" color-link trailingws "#D75F5F" micro-2.0.14/runtime/colorschemes/one-dark.micro0000664000175000017510000000247014663411671021160 0ustar nileshnileshcolor-link default "#ABB2BF,#21252C" color-link color-column "#282C34" color-link comment "#5C6370" color-link constant "#C678DD" color-link constant.number "#E5C07B" color-link constant.string "#98C379" color-link constant.string.char "#BDE6AD" color-link constant.specialChar "#DDF2A4" color-link current-line-number "#C6C6C6,#21252C" color-link cursor-line "#282C34" color-link divider "#ABB2BF" color-link error "#D2A8A1" color-link diff-added "#00AF00" color-link diff-modified "#FFAF00" color-link diff-deleted "#D70000" color-link gutter-error "#9B859D" color-link gutter-warning "#9B859D" color-link hlsearch "#21252C,#E5C07B" color-link identifier "#61AFEF" color-link identifier.class "#C678DD" color-link identifier.var "#C678DD" color-link indent-char "#515151" color-link line-number "#636D83,#282C34" color-link preproc "#E0C589" color-link special "#E0C589" color-link statement "#C678DD" color-link statusline "#282828,#ABB2BF" color-link symbol "#AC885B" color-link symbol.brackets "#ABB2BF" color-link symbol.operator "#C678DD" color-link symbol.tag "#AC885B" color-link tabbar "#F2F0EC,#2D2D2D" color-link todo "#8B98AB" color-link type "#66D9EF" color-link type.keyword "#C678DD" color-link underlined "#8996A8" color-link match-brace "#21252C,#C678DD" color-link tab-error "#D75F5F" color-link trailingws "#D75F5F" micro-2.0.14/runtime/colorschemes/railscast.micro0000664000175000017510000000261014663411671021441 0ustar nileshnileshcolor-link default "#e6e1dc,#2b2b2b" color-link comment "#bc9458,#2b2b2b" color-link statement "#cc7833,#2b2b2b" color-link constant "#a5c261,#2b2b2b" color-link constant.bool "#6d9cbe,#2b2b2b" color-link constant.specialChar "#459231,#2b2b2b" color-link type "#6d9cbe,#2b2b2b" color-link preproc "#cc7833,#2b2b2b" color-link special "#cc7833,#2b2b2b" color-link underlined "#cc7833,#2b2b2b" color-link todo "bold #cc7833,#2b2b2b" color-link error "bold #cc7833,#2b2b2b" color-link gutter-error "#cc7833,#11151C" color-link hlsearch "#e6e1dc,#474d5c" color-link indent-char "#414141,#2b2b2b" color-link line-number "#a1a1a1,#232323" color-link current-line-number "#e6e1dc,#2b2b2b" color-link diff-added "#00AF00" color-link diff-modified "#FFAF00" color-link diff-deleted "#D70000" color-link gutter-warning "#a5c261,#11151C" color-link symbol "#edb753,#2b2b2b" color-link symbol.operator "#cc7833,#2b2b2b" color-link symbol.brackets "#cc7833,#2b2b2b" color-link identifier "#edb753,#2b2b2b" color-link statusline "#b1b1b1,#232323" color-link tabbar "bold #b1b1b1,#232323" color-link cursor-line "#353535" color-link color-column "#353535" color-link space "underline #e6e1dc,#2b2b2b" color-link tab-error "#d75f5f" color-link trailingws "#d75f5f" #the Python syntax definition are wrong. This is not how you should do decorators! color-link brightgreen "#edb753,#2b2b2b" color-link match-brace "#2b2b2b,#a5c261" micro-2.0.14/runtime/colorschemes/simple.micro0000664000175000017510000000205414663411671020747 0ustar nileshnileshcolor-link comment "blue" color-link constant "red" color-link identifier "cyan" color-link statement "yellow" color-link symbol "yellow" color-link preproc "magenta" color-link type "green" color-link special "magenta" color-link ignore "default" color-link error ",brightred" color-link todo ",brightyellow" color-link hlsearch "black,yellow" color-link statusline "black,white" color-link indent-char "black" color-link line-number "yellow" color-link current-line-number "red" color-link diff-added "green" color-link diff-modified "yellow" color-link diff-deleted "red" color-link gutter-error ",red" color-link gutter-warning "red" #Cursor line causes readability issues. Disabled for now. #color-link cursor-line "white,black" color-link color-column "white" #No extended types. (bool in C) color-link type.extended "default" #No bracket highlighting. color-link symbol.brackets "default" #Color shebangs the comment color color-link preproc.shebang "comment" color-link match-brace ",magenta" color-link tab-error "brightred" color-link trailingws "brightred" micro-2.0.14/runtime/colorschemes/solarized-tc.micro0000664000175000017510000000225314663411671022057 0ustar nileshnileshcolor-link default "#839496,#002833" color-link comment "#586E75,#002833" color-link identifier "#268BD2,#002833" color-link constant "#2AA198,#002833" color-link constant.specialChar "#DC322F,#002833" color-link statement "#859900,#002833" color-link symbol "#859900,#002833" color-link preproc "#CB4B16,#002833" color-link type "#B58900,#002833" color-link special "#268BD2,#002833" color-link underlined "#D33682,#002833" color-link error "bold #CB4B16,#002833" color-link todo "bold #D33682,#002833" color-link hlsearch "#002833,#B58900" color-link statusline "#003541,#839496" color-link tabbar "#003541,#839496" color-link indent-char "#003541,#002833" color-link line-number "#586E75,#003541" color-link current-line-number "#586E75,#002833" color-link diff-added "#00AF00" color-link diff-modified "#FFAF00" color-link diff-deleted "#D70000" color-link gutter-error "#003541,#CB4B16" color-link gutter-warning "#CB4B16,#002833" color-link cursor-line "#003541" color-link color-column "#003541" color-link type.extended "#839496,#002833" color-link symbol.brackets "#839496,#002833" color-link match-brace "#002833,#268BD2" color-link tab-error "#D75F5F" color-link trailingws "#D75F5F" micro-2.0.14/runtime/colorschemes/solarized.micro0000664000175000017510000000200714663411671021450 0ustar nileshnileshcolor-link comment "bold brightgreen" color-link constant "cyan" color-link constant.specialChar "red" color-link identifier "blue" color-link statement "green" color-link symbol "green" color-link preproc "brightred" color-link type "yellow" color-link special "blue" color-link underlined "magenta" color-link error "bold brightred" color-link todo "bold magenta" color-link hlsearch "black,yellow" color-link statusline "black,brightblue" color-link tabbar "black,brightblue" color-link indent-char "black" color-link line-number "bold brightgreen,black" color-link current-line-number "bold brightgreen,default" color-link diff-added "green" color-link diff-modified "yellow" color-link diff-deleted "red" color-link gutter-error "black,brightred" color-link gutter-warning "brightred,default" color-link cursor-line "black" color-link color-column "black" color-link type.extended "default" color-link symbol.brackets "default" color-link match-brace ",blue" color-link tab-error "brightred" color-link trailingws "brightred" micro-2.0.14/runtime/colorschemes/sunny-day.micro0000664000175000017510000000145214663411671021406 0ustar nileshnileshcolor-link default "0,230" color-link comment "244" color-link constant.string "17" color-link constant "88" color-link identifier "22" color-link statement "0,230" color-link symbol "89" color-link preproc "22" color-link type "88" color-link special "22" color-link underlined "61,230" color-link error "88" color-link todo "210" color-link hlsearch "0,253" color-link statusline "233,229" color-link tabbar "233,229" color-link indent-char "229" color-link line-number "244" color-link diff-added "34" color-link diff-modified "214" color-link diff-deleted "160" color-link gutter-error "88" color-link gutter-warning "88" color-link cursor-line "229" #color-link color-column "196" color-link current-line-number "246" color-line match-brace "230,22" color-link tab-error "210" color-link trailingws "210" micro-2.0.14/runtime/colorschemes/twilight.micro0000664000175000017510000000253014663411671021310 0ustar nileshnilesh# Twilight color scheme color-link default "#F8F8F8,#141414" color-link color-column "#1B1B1B" color-link comment "#5F5A60" color-link constant "#CF6A4C" #color-link constant.number "#CF6A4C" color-link constant.specialChar "#DDF2A4" color-link constant.string "#8F9D6A" color-link current-line-number "#868686,#1B1B1B" color-link cursor-line "#1B1B1B" color-link divider "#1E1E1E" color-link error "#D2A8A1" color-link diff-added "#00AF00" color-link diff-modified "#FFAF00" color-link diff-deleted "#D70000" color-link gutter-error "#9B859D" color-link gutter-warning "#9B859D" color-link hlsearch "#141414,#C0C000" color-link identifier "#9B703F" color-link identifier.class "#DAD085" color-link identifier.var "#7587A6" color-link indent-char "#515151" color-link line-number "#868686,#1B1B1B" color-link current-line-number "#868686,#141414" color-link preproc "#E0C589" color-link special "#E0C589" color-link statement "#CDA869" color-link statusline "#515151,#1E1E1E" color-link symbol "#AC885B" color-link symbol.brackets "#F8F8F8" color-link symbol.operator "#CDA869" color-link symbol.tag "#AC885B" color-link tabbar "#F2F0EC,#2D2D2D" color-link todo "#8B98AB" color-link type "#F9EE98" color-link type.keyword "#CDA869" color-link underlined "#8996A8" color-link match-brace "#141414,#E0C589" color-link tab-error "#D75F5F" color-link trailingws "#D75F5F" micro-2.0.14/runtime/colorschemes/zenburn.micro0000664000175000017510000000164014663411671021141 0ustar nileshnileshcolor-link default "188,237" color-link comment "108,237" color-link constant.string "174,237" color-link constant.number "116,237" color-link constant "181,237" color-link identifier "223,237" color-link statement "223,237" color-link symbol "223,237" color-link preproc "223,237" color-link type "187,237" color-link special "181,237" color-link underlined "188,237" color-link error "115,236" color-link todo "bold 254,237" color-link hlsearch "230,22" color-link statusline "186,236" color-link tabbar "186,236" color-link indent-char "238,237" color-link line-number "248,238" color-link diff-added "34" color-link diff-modified "214" color-link diff-deleted "160" color-link gutter-error "237,174" color-link gutter-warning "174,237" color-link cursor-line "238" color-link color-column "238" color-link current-line-number "188,237" color-link match-brace "237,223" color-link tab-error "167" color-link trailingws "167" micro-2.0.14/runtime/help/0000775000175000017510000000000014663411671014664 5ustar nileshnileshmicro-2.0.14/runtime/help/colors.md0000664000175000017510000003373214663411671016517 0ustar nileshnilesh# Colors This help page aims to cover two aspects of micro's syntax highlighting engine: * How to create colorschemes and use them. * How to create syntax files to add to the list of languages micro can highlight. ## Colorschemes To change your colorscheme, press `Ctrl-e` in micro to bring up the command prompt, and type: ``` set colorscheme twilight ``` (or whichever colorscheme you choose). Micro comes with a number of colorschemes by default. The colorschemes that you can display will depend on what kind of color support your terminal has. Omit color-link default "[fg color],[bg color]" will make the background color match the terminal's, and transparency if set. Modern terminals tend to have a palette of 16 user-configurable colors (these colors can often be configured in the terminal preferences), and additional color support comes in three flavors. * 16-color: A colorscheme that uses the 16 default colors will always work but will only look good if the 16 default colors have been configured to the user's liking. Using a colorscheme that only uses the 16 colors from the terminal palette will also preserve the terminal's theme from other applications since the terminal will often use those same colors for other applications. Default colorschemes of this type include `simple` and `solarized`. * 256-color: Almost all terminals support displaying an additional 240 colors on top of the 16 user-configurable colors (creating 256 colors total). Colorschemes which use 256-color are portable because they will look the same regardless of the configured 16-color palette. However, the color range is fairly limited due to the small number of colors available. Default 256-color colorschemes include `monokai`, `twilight`, `zenburn`, `darcula` and more. * true-color: Some terminals support displaying "true color" with 16 million colors using standard RGB values. This mode will be able to support displaying any colorscheme, but it should be noted that the user-configured 16-color palette is ignored when using true-color mode (this means the colors while using the terminal emulator will be slightly off). Not all terminals support true color but at this point most do. True color support in micro is off by default but can be enabled by setting the environment variable `MICRO_TRUECOLOR` to 1. In addition your terminal must support it (usually indicated by setting `$COLORTERM` to `truecolor`). True-color colorschemes in micro typically end with `-tc`, such as `solarized-tc`, `atom-dark`, `material-tc`, etc... If true color is not enabled but a true color colorscheme is used, micro will do its best to approximate the colors to the available 256 colors. Here is the list of colorschemes: ### 256 color These should work and look nice in most terminals. I recommend these themes the most. * `monokai` (also the `default` colorscheme) * `zenburn` * `gruvbox` * `darcula` * `twilight` * `railscast` * `bubblegum` (light theme) ### 16 color These may vary widely based on the 16 colors selected for your terminal. * `simple` * `solarized` (must have the solarized color palette in your terminal to use this colorscheme properly) * `cmc-16` * `cmc-paper` * `geany` ### True color True color requires your terminal to support it. This means that the environment variable `COLORTERM` should have the value `truecolor`, `24bit`, or `24-bit`. In addition, to enable true color in micro, the environment variable `MICRO_TRUECOLOR` must be set to 1. Note that you have to create and set this variable yourself. * `solarized-tc`: this is the solarized colorscheme for true color. * `atom-dark`: this colorscheme is based off of Atom's "dark" colorscheme. * `cmc-tc`: A true colour variant of the cmc theme. It requires true color to look its best. Use cmc-16 if your terminal doesn't support true color. * `gruvbox-tc`: The true color version of the gruvbox colorscheme * `material-tc`: Colorscheme based off of Google's Material Design palette ## Creating a Colorscheme Micro's colorschemes are also extremely simple to create. The default ones can be found [here](https://github.com/zyedidia/micro/tree/master/runtime/colorschemes). Custom colorschemes should be placed in the `~/.config/micro/colorschemes` directory. A number of custom directives are placed in a `.micro` file. Colorschemes are typically only 18-30 lines in total. To create the colorscheme you need to link highlight groups with actual colors. This is done using the `color-link` command. For example, to highlight all comments in green, you would use the command: ``` color-link comment "green" ``` Background colors can also be specified with a comma: ``` color-link comment "green,blue" ``` This will give the comments a blue background. If you would like no foreground you can just use a comma with nothing in front: ``` color-link comment ",blue" ``` You can also put bold, italic, or underline in front of the color: ``` color-link comment "bold red" ``` --- There are three different ways to specify the color. Color terminals usually have 16 colors that are preset by the user. This means that you cannot depend on those colors always being the same. You can use those colors with the names `black, red, green, yellow, blue, magenta, cyan, white` and the bright variants of each one (brightblack, brightred...). Then you can use the terminals 256 colors by using their numbers 1-256 (numbers 1-16 will refer to the named colors). If the user's terminal supports true color, then you can also specify colors exactly using their hex codes. If the terminal is not true color but micro is told to use a true color colorscheme it will attempt to map the colors to the available 256 colors. Generally colorschemes which require true color terminals to look good are marked with a `-tc` suffix and colorschemes which supply a white background are marked with a `-paper` suffix. --- Here is a list of the colorscheme groups that you can use: * default (color of the background and foreground for unhighlighted text) * comment * identifier * constant * statement * symbol * preproc * type * special * underlined * error * todo * selection (Color of the text selection) * statusline (Color of the statusline) * tabbar (Color of the tabbar that lists open files) * indent-char (Color of the character which indicates tabs if the option is enabled) * line-number * gutter-error * gutter-warning * diff-added * diff-modified * diff-deleted * cursor-line * current-line-number * color-column * ignore * scrollbar * divider (Color of the divider between vertical splits) * message (Color of messages in the bottom line of the screen) * error-message (Color of error messages in the bottom line of the screen) * match-brace (Color of matching brackets when `matchbracestyle` is set to `highlight`) * hlsearch (Color of highlighted search results when `hlsearch` is enabled) * tab-error (Color of tab vs space errors when `hltaberrors` is enabled) * trailingws (Color of trailing whitespaces when `hltrailingws` is enabled) Colorschemes must be placed in the `~/.config/micro/colorschemes` directory to be used. --- In addition to the main colorscheme groups, there are subgroups that you can specify by adding `.subgroup` to the group. If you're creating your own custom syntax files, you can make use of your own subgroups. If micro can't match the subgroup, it'll default to the root group, so it's safe and recommended to use subgroups in your custom syntax files. For example if `constant.string` is found in your colorscheme, micro will us that for highlighting strings. If it's not found, it will use constant instead. Micro tries to match the largest set of groups it can find in the colorscheme definitions, so if, for example `constant.bool.true` is found then micro will use that. If `constant.bool.true` is not found but `constant.bool` is found micro will use `constant.bool`. If not, it uses `constant`. Here's a list of subgroups used in micro's built-in syntax files. * comment.bright (Some filetypes have distinctions between types of comments) * constant.bool * constant.bool.true * constant.bool.false * constant.number * constant.specialChar * constant.string * constant.string.url * identifier.class (Also used for functions) * identifier.macro * identifier.var * preproc.shebang (The #! at the beginning of a file that tells the os what script interpreter to use) * symbol.brackets (`{}()[]` and sometimes `<>`) * symbol.operator (Color operator symbols differently) * symbol.tag (For html tags, among other things) * type.keyword (If you want a special highlight for keywords like `private`) In the future, plugins may also be able to use color groups for styling. --- Last but not least it's even possible to use `include` followed by the colorscheme name as string to include a different colorscheme within a new one. Additionally the groups can then be extended or overwritten. The `default.micro` theme can be seen as an example, which links to the chosen default colorscheme. ## Syntax files The syntax files are written in yaml-format and specify how to highlight languages. Micro's builtin syntax highlighting tries very hard to be sane, sensible and provide ample coverage of the meaningful elements of a language. Micro has syntax files built in for over 100 languages now! However, there may be situations where you find Micro's highlighting to be insufficient or not to your liking. The good news is that you can create your own syntax files, and place them in `~/.config/micro/syntax` and Micro will use those instead. ### Filetype definition You must start the syntax file by declaring the filetype: ``` filetype: go ``` ### Detect definition Then you must provide information about how to detect the filetype: ``` detect: filename: "\\.go$" ``` Micro will match this regex against a given filename to detect the filetype. In addition to the `filename` regex (or even instead of it) you can provide a `header` regex that will check the first line of the file. For example: ``` detect: filename: "\\.ya?ml$" header: "%YAML" ``` This is useful in cases when the given file name is not sufficient to determine the filetype, e.g. with the above example, if a YAML file has no `.yaml` extension but may contain a `%YAML` directive in its first line. `filename` takes precedence over `header`, i.e. if there is a syntax file that matches the file with a filetype by the `filename` and another syntax file that matches the same file with another filetype by the `header`, the first filetype will be used. Finally, in addition to `filename` and/or `header` (but not instead of them) you may also provide an optional `signature` regex which is useful for resolving ambiguities when there are multiple syntax files matching the same file with different filetypes. If a `signature` regex is given, micro will match a certain amount of first lines in the file (this amount is determined by the `detectlimit` option) against this regex, and if any of the lines match, this syntax file's filetype will be preferred over other matching filetypes. For example, to distinguish C++ header files from C and Objective-C header files that have the same `.h` extension: ``` detect: filename: "\\.c(c|pp|xx)$|\\.h(h|pp|xx)?$" signature: "namespace|template|public|protected|private" ``` ### Syntax rules Next you must provide the syntax highlighting rules. There are two types of rules: patterns and regions. A pattern is matched on a single line and usually a single word as well. A region highlights between two patterns over multiple lines and may have rules of its own inside the region. Here are some example patterns in Go: ``` rules: - special: "\\b(break|case|continue|default|go|goto|range|return)\\b" - statement: "\\b(else|for|if|switch)\\b" - preproc: "\\b(package|import|const|var|type|struct|func|go|defer|iota)\\b" ``` The order of patterns does matter as patterns lower in the file will overwrite the ones defined above them. And here are some example regions for Go: ``` - constant.string: start: "\"" end: "\"" rules: - constant.specialChar: "%." - constant.specialChar: "\\\\[abfnrtv'\\\"\\\\]" - constant.specialChar: "\\\\([0-7]{3}|x[A-Fa-f0-9]{2}|u[A-Fa-f0-9]{4}|U[A-Fa-f0-9]{8})" - comment: start: "//" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" - comment: start: "/\\*" end: "\\*/" rules: - todo: "(TODO|XXX|FIXME):?" ``` Notice how the regions may contain rules inside of them. Any inner rules that are matched are then skipped when searching for the end of the region. For example, when highlighting `"foo \" bar"`, since `\"` is matched by an inner rule in the region, it is skipped. Likewise for `"foo \\" bar`, since `\\` is matched by an inner rule, it is skipped, and then the `"` is found and the string ends at the correct place. You may also explicitly mark skip regexes if you don't want them to be highlighted. For example: ``` - constant.string: start: "\"" end: "\"" skip: "\\." rules: [] ``` #### Includes You may also include rules from other syntax files as embedded languages. For example, the following is possible for html: ``` - default: start: "" end: "" rules: - include: "javascript" - default: start: "" end: "" rules: - include: "css" ``` Note that nested include (i.e. including syntax files that include other syntax files) is not supported yet. ### Default syntax highlighting If micro cannot detect the filetype of the file, it falls back to using the default syntax highlighting for it, which highlights just the bare minimum: email addresses, URLs etc. Just like in other cases, you can override the default highlighting by adding your own custom `default.yaml` file to `~/.config/micro/syntax`. For example, if you work with various config files that use the `#` sign to mark the beginning of a comment, you can use the following custom `default.yaml` to highlight those comments by default: ``` filetype: unknown detect: filename: "" rules: - comment: "(^|\\s)#.*$" ``` micro-2.0.14/runtime/help/commands.md0000664000175000017510000001401714663411671017012 0ustar nileshnilesh# Command bar The command bar is opened by pressing `Ctrl-e`. It is a single-line buffer, meaning that all keybindings from a normal buffer are supported (as well as mouse and selection). When running a command, you can use extra syntax that micro will expand before running the command. To use an argument with a space in it, put it in quotes. The command bar parser uses the same rules for parsing arguments that `/bin/sh` would use (single quotes, double quotes, escaping). The command bar does not look up environment variables. # Commands Micro provides the following commands that can be executed at the command-bar by pressing `Ctrl-e` and entering the command. Arguments are placed in single quotes here but these are not necessary when entering the command in micro. * `bind 'key' 'action'`: creates a keybinding from key to action. See the `keybindings` documentation for more information about binding keys. This command will modify `bindings.json` and overwrite any bindings to `key` that already exist. * `help ['topic']`: opens the corresponding help topic. If no topic is provided opens the default help screen. Help topics are stored as `.md` files in the `runtime/help` directory of the source tree, which is embedded in the final binary. * `save ['filename']`: saves the current buffer. If the file is provided it will 'save as' the filename. * `quit`: quits micro. * `goto 'line[:col]'`: goes to the given absolute line (and optional column) number. A negative number can be passed to go inward from the end of the file. Example: -5 goes to the 5th-last line in the file. * `jump 'line[:col]'`: goes to the given relative number from the current line (and optional absolute column) number. Example: -5 jumps 5 lines up in the file, while (+)3 jumps 3 lines down. * `replace 'search' 'value' ['flags']`: This will replace `search` with `value`. The `flags` are optional. Possible flags are: * `-a`: Replace all occurrences at once * `-l`: Do a literal search instead of a regex search Note that `search` must be a valid regex (unless `-l` is passed). If one of the arguments does not have any spaces in it, you may omit the quotes. In case the search is done non-literal (without `-l`), the 'value' is interpreted as a template: * `$3` or `${3}` substitutes the submatch of the 3rd (capturing group) * `$foo` or `${foo}` substitutes the submatch of the (?Pnamed group) * You have to write `$$` to substitute a literal dollar. * `replaceall 'search' 'value'`: this will replace all occurrences of `search` with `value` without user confirmation. See `replace` command for more information. * `set 'option' 'value'`: sets the option to value. See the `options` help topic for a list of options you can set. This will modify your `settings.json` with the new value. * `setlocal 'option' 'value'`: sets the option to value locally (only in the current buffer). This will *not* modify `settings.json`. * `show 'option'`: shows the current value of the given option. * `run 'sh-command'`: runs the given shell command in the background. The command's output will be displayed in one line when it finishes running. * `vsplit ['filename']`: opens a vertical split with `filename`. If no filename is provided, a vertical split is opened with an empty buffer. * `hsplit ['filename']`: same as `vsplit` but opens a horizontal split instead of a vertical split. * `tab ['filename']`: opens the given file in a new tab. * `tabmove '[-+]n'`: Moves the active tab to another slot. `n` is an integer. If `n` is prefixed with `-` or `+`, then it represents a relative position (e.g. `tabmove +2` moves the tab to the right by `2`). If `n` has no prefix, it represents an absolute position (e.g. `tabmove 2` moves the tab to slot `2`). * `tabswitch 'tab'`: This command will switch to the specified tab. The `tab` can either be a tab number, or a name of a tab. * `textfilter 'sh-command'`: filters the current selection through a shell command as standard input and replaces the selection with the stdout of the shell command. For example, to sort a list of numbers, first select them, and then execute `> textfilter sort -n`. * `log`: opens a log of all messages and debug statements. * `plugin list`: lists all installed plugins. * `plugin install 'pl'`: install a plugin. * `plugin remove 'pl'`: remove a plugin. * `plugin update ['pl']`: update a plugin (if no arguments are provided updates all plugins). * `plugin search 'pl'`: search available plugins for a keyword. * `plugin available`: show available plugins that can be installed. * `reload`: reloads all runtime files (settings, keybindings, syntax files, colorschemes, plugins). All plugins will be unloaded by running their `deinit()` function (if it exists), and then loaded again by calling the `preinit()`, `init()` and `postinit()` functions (if they exist). * `cd 'path'`: Change the working directory to the given `path`. * `pwd`: Print the current working directory. * `open 'filename'`: Open a file in the current buffer. * `reopen`: Reopens the current file from disk. * `reset 'option'`: resets the given option to its default value * `retab`: Replaces all leading tabs with spaces or leading spaces with tabs depending on the value of `tabstospaces`. * `raw`: micro will open a new tab and show the escape sequence for every event it receives from the terminal. This shows you what micro actually sees from the terminal and helps you see which bindings aren't possible and why. This is most useful for debugging keybindings. * `showkey 'key'`: Show the action(s) bound to a given key. For example running `> showkey Ctrl-c` will display `Copy`. * `term ['exec']`: Open a terminal emulator running the given executable. If no executable is given, this will open the default shell in the terminal emulator. --- The following commands are provided by the default plugins: * `lint`: Lint the current file for errors. * `comment`: automatically comment or uncomment current selection or line. micro-2.0.14/runtime/help/copypaste.md0000664000175000017510000001523514663411671017223 0ustar nileshnileshCopy and paste are essential features in micro but can be confusing to get right especially when running micro over SSH because there are multiple methods. This help document will explain the various methods for copying and pasting, how they work, and the best methods for doing so over SSH. # OSC 52 (terminal clipboard) If possible, setting the `clipboard` option to `terminal` will give best results because it will work over SSH and locally. However, there is limited support among terminal emulators for the terminal clipboard (which uses the OSC 52 protocol to communicate clipboard contents). Here is a list of terminal emulators and their status: * `Kitty`: supported, but only writing is enabled by default. To enable reading, add `read-primary` and `read-clipboard` to the `clipboard_control` option. * `iTerm2`: only copying (writing to clipboard) is supported. Must be enabled in `Preferences->General-> Selection->Applications in terminal may access clipboard`. You can use `Command-v` to paste. * `st`: supported. * `rxvt-unicode`: not natively supported, but there is a Perl extension [here](http://anti.teamidiot.de/static/nei/*/Code/urxvt/). * `xterm`: supported, but disabled by default. It can be enabled by putting the following in `.Xresources` or `.Xdefaults`: `XTerm*disallowedWindowOps: 20,21,SetXprop`. * `gnome-terminal`: does not support OSC 52. * `alacritty`: supported. Since 0.13.0, reading has been disabled by default. To reenable it, set the `terminal.osc52` option to `CopyPaste`. * `foot`: supported. * `wezterm`: only copying (writing to clipboard) is supported. **Summary:** If you want copy and paste to work over SSH, then you should set `clipboard` to `terminal`, and make sure your terminal supports OSC 52. # Pasting ## Recommendations (TL;DR) The recommended method of pasting is the following: * If you are not working over SSH, use the micro keybinding (`Ctrl-v` by default) to perform pastes. If on Linux, install `xclip` or `xsel` beforehand. * If you are working over SSH, use the terminal keybinding (`Ctrl-Shift-v` or `Command-v`) to perform pastes. If your terminal does not support bracketed paste, when performing a paste first enable the `paste` option, and when finished disable the option. ## Micro paste events Micro is an application that runs within the terminal. This means that the terminal sends micro events, such as key events, mouse events, resize events, and paste events. Micro's default keybinding for paste is `Ctrl-v`. This means that when micro receives the key event saying `Ctrl-v` has been pressed from the terminal, it will attempt to access the system clipboard and effect a paste. The system clipboard will be accessed through `pbpaste` on MacOS (installed by default), `xclip` or `xsel` on Linux (these applications must be installed by the user) or a system call on Windows. ## Terminal paste events For certain keypresses, the terminal will not send an event to micro and will instead do something itself. In this document, such keypresses will be called "terminal keybindings." Often there will be a terminal keybinding for pasting and copying. On MacOS these are Command-v and Command-c and on Linux `Ctrl-Shift-v` and `Ctrl-Shift-c`. When the terminal keybinding for paste is executed, your terminal will access the system clipboard, and send micro either a paste event or a list of key events (one key for each character in the paste), depending on whether or not your terminal supports sending paste events (called bracketed paste). If your terminal supports bracketed paste, then it will send a paste event and everything will work well. However, if your terminal sends a list of key events, this can cause issues because micro will think you manually entered each character and may add closing brackets or automatic indentation, which will mess up the pasted text. To avoid this, you can temporarily enable the `paste` option while you perform the paste. When paste option is on, micro will aggregate lists of multiple key events into larger paste events. It is a good idea to disable the `paste` option during normal use as occasionally if you are typing quickly, the terminal will send the key events as lists of characters that were in fact manually entered. ## Pasting over SSH When working over SSH, micro is running on the remote machine and your terminal is running on your local machine. Therefore if you would like to paste, using `Ctrl-v` (micro's keybinding) will not work because when micro attempts to access the system clipboard, it will access the remote machine's clipboard rather than the local machine's clipboard. On the other hand, the terminal keybinding for paste will access your local clipboard and send the text over the network as a paste event, which is what you want. # Copying # Recommendations (TL;DR) The recommended method of copying is the following: * If you are not working over SSH, use the micro keybinding (`Ctrl-c` by default) to perform copies. If on Linux, install `xclip` or `xsel` beforehand. * If you are working over SSH, use the terminal keybinding (`Ctrl-Shift-c` or `Command-c`) to perform copies. You must first disable the `mouse` option to perform a terminal selection, and you may wish to disable line numbers and diff indicators (`ruler` and `diffgutter` options) and close other splits. This method will only be able to copy characters that are displayed on the screen (you will not be able to copy more than one page's worth of characters). Copying follows a similar discussion to the one above about pasting. The primary difference is before performing a copy, the application doing the copy must be told what text needs to be copied. Micro has a keybinding (`Ctrl-c`) for copying and will access the system clipboard to perform the copy. The text that micro will copy into is the text that is currently selected in micro (usually such text is displayed with a white background). When the `mouse` option is enabled, the mouse can be used to select text, as well as other keybindings, such as ShiftLeft, etc... The terminal also has a keybinding (`Ctrl-Shift-c` or `Command-c`) to perform a copy, and the text that it copies is the text selected by the terminal's selection (*not* micro's selection). To select text with the terminal selection, micro's mouse support must first be disabled by turning the `mouse` option off. The terminal, unlike micro, has no sense of different buffers/splits and what the different characters being displayed are. This means that for copying multiple lines using the terminal selection, you should first disable line numbers and diff indicators (turn off the `ruler` and `diffgutter` options), otherwise they might be part of your selection and copied. micro-2.0.14/runtime/help/defaultkeys.md0000664000175000017510000002153114663411671017530 0ustar nileshnilesh# Default Keys Below are simple charts of the default hotkeys and their functions. For more information about binding custom hotkeys or changing default bindings, please run `> help keybindings` Please remember that *all* keys here are rebindable! If you don't like it, you can change it! ### Power user | Key | Description of function | |---------- |-------------------------------------------------------------------------------------------------- | | Ctrl-e | Open a command prompt for running commands (see `> help commands` for a list of valid commands). | | Tab | In command prompt, it will autocomplete if possible. | | Ctrl-b | Run a shell command (this will close micro while your command executes). | ### Navigation | Key | Description of function | |---------------------------- |------------------------------------------------------------------------------------------ | | Arrows | Move the cursor around | | Shift-arrows | Move and select text | | Alt(Ctrl on Mac)-LeftArrow | Move to the beginning of the current line | | Alt(Ctrl on Mac)-RightArrow | Move to the end of the current line | | Home | Move to the beginning of text on the current line | | End | Move to the end of the current line | | Ctrl(Alt on Mac)-LeftArrow | Move cursor one word left | | Ctrl(Alt on Mac)-RightArrow | Move cursor one word right | | Alt-{ | Move cursor to previous empty line, or beginning of document | | Alt-} | Move cursor to next empty line, or end of document | | PageUp | Move cursor up one page | | PageDown | Move cursor down one page | | Ctrl-Home or Ctrl-UpArrow | Move cursor to start of document | | Ctrl-End or Ctrl-DownArrow | Move cursor to end of document | | Ctrl-l | Jump to a line in the file (prompts with #) | | Ctrl-w | Cycle between splits in the current tab (use `> vsplit` or `> hsplit` to create a split) | ### Tabs | Key | Description of function | |-------- |-------------------------- | | Ctrl-t | Open a new tab | | Alt-, | Previous tab | | Alt-. | Next tab | ### Find Operations | Key | Description of function | |---------- |------------------------------------------ | | Ctrl-f | Find (opens prompt) | | Ctrl-n | Find next instance of current search | | Ctrl-p | Find previous instance of current search | Note: `Ctrl-n` and `Ctrl-p` should be used from the main buffer, not from inside the search prompt. After `Ctrl-f`, press enter to complete the search and then you can use `Ctrl-n` and `Ctrl-p` to cycle through matches. ### File Operations | Key | Description of function | |---------- |------------------------------------------------------------------ | | Ctrl-q | Close current file (quits micro if this is the last file open) | | Ctrl-o | Open a file (prompts for filename) | | Ctrl-s | Save current file | ### Text operations | Key | Description of function | |------------------------------------ |------------------------------------------ | | Ctrl(Alt on Mac)-Shift-RightArrow | Select word right | | Ctrl(Alt on Mac)-Shift-LeftArrow | Select word left | | Alt(Ctrl on Mac)-Shift-LeftArrow | Select to start of current line | | Alt(Ctrl on Mac)-Shift-RightArrow | Select to end of current line | | Shift-Home | Select to start of current line | | Shift-End | Select to end of current line | | Ctrl-Shift-UpArrow | Select to start of file | | Ctrl-Shift-DownArrow | Select to end of file | | Ctrl-x | Cut selected text | | Ctrl-c | Copy selected text | | Ctrl-v | Paste | | Ctrl-k | Cut current line | | Ctrl-d | Duplicate current line | | Ctrl-z | Undo | | Ctrl-y | Redo | | Alt-UpArrow | Move current line or selected lines up | | Alt-DownArrow | Move current line or selected lines down | | Alt-Backspace or Alt-Ctrl-h | Delete word left | | Ctrl-a | Select all | | Tab | Indent selected text | | Shift-Tab | Unindent selected text | ### Macros | Key | Description of function | |---------- |---------------------------------------------------------------------------------- | | Ctrl-u | Toggle macro recording (press Ctrl-u to start recording and press again to stop) | | Ctrl-j | Run latest recorded macro | ### Multiple cursors | Key | Description of function | |------------------ |---------------------------------------------------------------------------------------------- | | Alt-n | Create new multiple cursor from selection (will select current word if no current selection) | | Alt-Shift-Up | Spawn a new cursor on the line above the current one | | Alt-Shift-Down | Spawn a new cursor on the line below the current one | | Alt-p | Remove latest multiple cursor | | Alt-c | Remove all multiple cursors (cancel) | | Alt-x | Skip multiple cursor selection | | Alt-m | Spawn a new cursor at the beginning of every line in the current selection | | Ctrl-MouseLeft | Place a multiple cursor at any location | ### Other | Key | Description of function | |---------- |-------------------------------------------------------------------------------------- | | Ctrl-g | Open help file | | Ctrl-h | Backspace (old terminals do not support the backspace key and use Ctrl+H instead) | | Ctrl-r | Toggle the line number ruler | ### Emacs style actions | Key | Description of function | |---------- |-------------------------- | | Alt-f | Next word | | Alt-b | Previous word | | Alt-a | Move to start of line | | Alt-e | Move to end of line | ### Function keys. Warning! The function keys may not work in all terminals! | Key | Description of function | |------ |-------------------------- | | F1 | Open help | | F2 | Save | | F3 | Find | | F4 | Quit | | F7 | Find | | F10 | Quit | micro-2.0.14/runtime/help/help.md0000664000175000017510000000506014663411671016137 0ustar nileshnilesh# Micro help text Micro is an easy to use, intuitive, text editor that takes advantage of the full capabilities of modern terminals. Micro can be controlled by commands entered on the command bar, or with keybindings. To open the command bar, press `Ctrl-e`: the `>` prompt will display. From now on, when the documentation shows a command to run (such as `> help`), press `Ctrl-e` and type the command followed by enter. For a list of the default keybindings, run `> help defaultkeys`. For more information on keybindings, see `> help keybindings`. To toggle a short list of important keybindings, press Alt-g. ## Quick-start To quit, press `Ctrl-q`. Save by pressing `Ctrl-s`. Press `Ctrl-e`, as previously mentioned, to start typing commands. To see which commands are available, at the prompt, press tab, or view the help topic with `> help commands`. Move the cursor around with the mouse or with the arrow keys. Enter text simply by pressing character keys. If the colorscheme doesn't look good, you can change it with `> set colorscheme ...`. You can press tab to see the available colorschemes, or see more information about colorschemes and syntax highlighting with `> help colors`. Press `Ctrl-w` to move between splits, and type `> vsplit filename` or `> hsplit filename` to open a new split. ## Accessing more help Micro has a built-in help system which can be accessed with the `> help` command. To view help for the various available topics, press `Ctrl-e` to access command mode and type in `> help` followed by a topic. Typing just `> help` will open this page. Here are the available help topics: * `tutorial`: A brief tutorial which gives an overview of all the other help topics * `keybindings`: Gives a full list of the default keybindings as well as how to rebind them * `defaultkeys`: Gives a more straight-forward list of the hotkey commands and what they do * `commands`: Gives a list of all the commands and what they do * `options`: Gives a list of all the options you can customize * `plugins`: Explains how micro's plugin system works and how to create your own plugins * `colors`: Explains micro's colorscheme and syntax highlighting engine and how to create your own colorschemes or add new languages to the engine For example, to open the help page on plugins you would run `> help plugins`. I recommend looking at the `tutorial` help file because it is short for each section and gives concrete examples of how to use the various configuration options in micro. However, it does not give the in-depth documentation that the other topics provide. micro-2.0.14/runtime/help/keybindings.md0000664000175000017510000004131114663411671017514 0ustar nileshnilesh# Keybindings Micro has a plethora of hotkeys that make it easy and powerful to use and all hotkeys are fully customizable to your liking. Custom keybindings are stored internally in micro if changed with the `> bind` command or can also be added in the file `~/.config/micro/bindings.json` as discussed below. For a list of the default keybindings in the json format used by micro, please see the end of this file. For a more user-friendly list with explanations of what the default hotkeys are and what they do, please see `> help defaultkeys` (a json formatted list of default keys is included at the end of this document). If `~/.config/micro/bindings.json` does not exist, you can simply create it. Micro will know what to do with it. You can use Ctrl + arrows to move word by word (Alt + arrows for Mac). Alt + left and right move the cursor to the start and end of the line (Ctrl + left/right for Mac), and Ctrl + up and down move the cursor to the start and end of the buffer. You can hold shift with all of these movement actions to select while moving. ## Rebinding keys The bindings may be rebound using the `~/.config/micro/bindings.json` file. Each key is bound to an action. For example, to bind `Ctrl-y` to undo and `Ctrl-z` to redo, you could put the following in the `bindings.json` file. ```json { "Ctrl-y": "Undo", "Ctrl-z": "Redo" } ``` **Note:** The syntax `` is equivalent to `-`. In addition, `Ctrl-Shift` bindings are not supported by terminals, and are the same as simply `Ctrl` bindings. This means that `CtrlG`, `Ctrl-G`, and `Ctrl-g` all mean the same thing. However, for `Alt` this is not the case: `AltG` and `Alt-G` mean `Alt-Shift-g`, while `Alt-g` does not require the Shift modifier. In addition to editing your `~/.config/micro/bindings.json`, you can run `>bind ` For a list of bindable actions, see below. You can also chain commands when rebinding. For example, if you want `Alt-s` to save and quit you can bind it like so: ```json { "Alt-s": "Save,Quit" } ``` Each action will return a success flag. Actions can be chained such that the chain only continues when there are successes, or failures, or either. The `,` separator will always chain to the next action. The `|` separator will abort the chain if the action preceding it succeeds, and the `&` will abort the chain if the action preceding it fails. For example, in the default bindings, tab is bound as ``` "Tab": "Autocomplete|IndentSelection|InsertTab" ``` This means that if the `Autocomplete` action is successful, the chain will abort. Otherwise, it will try `IndentSelection`, and if that fails too, it will execute `InsertTab`. ## Binding commands You can also bind a key to execute a command in command mode (see `help commands`). Simply prepend the binding with `command:`. For example: ```json { "Alt-p": "command:pwd" } ``` **Note for macOS**: By default, macOS terminals do not forward alt events and instead insert unicode characters. To fix this, do the following: * iTerm2: select `Esc+` for `Left Option Key` in `Preferences->Profiles->Keys`. * Terminal.app: Enable `Use Option key as Meta key` in `Preferences->Profiles->Keyboard`. Now when you press `Alt-p` the `pwd` command will be executed which will show your working directory in the infobar. You can also bind an "editable" command with `command-edit:`. This means that micro won't immediately execute the command when you press the binding, but instead just place the string in the infobar in command mode. For example, you could rebind `Ctrl-g` to `> help`: ```json { "Ctrl-g": "command-edit:help " } ``` Now when you press `Ctrl-g`, `help` will appear in the command bar and your cursor will be placed after it (note the space in the json that controls the cursor placement). ## Binding raw escape sequences Only read this section if you are interested in binding keys that aren't on the list of supported keys for binding. One of the drawbacks of using a terminal-based editor is that the editor must get all of its information about key events through the terminal. The terminal sends these events in the form of escape sequences often (but not always) starting with `0x1b`. For example, if micro reads `\x1b[1;5D`, on most terminals this will mean the user pressed CtrlLeft. For many key chords though, the terminal won't send any escape code or will send an escape code already in use. For example for `CtrlBackspace`, my terminal sends `\u007f` (note this doesn't start with `0x1b`), which it also sends for `Backspace` meaning micro can't bind `CtrlBackspace`. However, some terminals do allow you to bind keys to send specific escape sequences you define. Then from micro you can directly bind those escape sequences to actions. For example, to bind `CtrlBackspace` you can instruct your terminal to send `\x1bctrlback` and then bind it in `bindings.json`: ```json { "\u001bctrlback": "DeleteWordLeft" } ``` Here are some instructions for sending raw escapes in different terminals ### iTerm2 In iTerm2, you can do this in `Preferences->Profiles->Keys` then click the `+`, input your keybinding, and for the `Action` select `Send Escape Sequence`. For the above example your would type `ctrlback` into the box (the `\x1b`) is automatically sent by iTerm2. ### Linux using loadkeys You can do this in linux using the loadkeys program. Coming soon! ## Unbinding keys It is also possible to disable any of the default key bindings by use of the `None` action in the user's `bindings.json` file. ## Bindable actions and bindable keys The list of default keybindings contains most of the possible actions and keys which you can use, but not all of them. Here is a full list of both. Full list of possible actions: ``` CursorUp CursorDown CursorPageUp CursorPageDown CursorLeft CursorRight CursorStart CursorEnd SelectToStart SelectToEnd SelectUp SelectDown SelectLeft SelectRight SelectToStartOfText SelectToStartOfTextToggle WordRight WordLeft SubWordRight SubWordLeft SelectWordRight SelectWordLeft SelectSubWordRight SelectSubWordLeft MoveLinesUp MoveLinesDown DeleteWordRight DeleteWordLeft DeleteSubWordRight DeleteSubWordLeft SelectLine SelectToStartOfLine SelectToEndOfLine InsertNewline InsertSpace Backspace Delete Center InsertTab Save SaveAll SaveAs Find FindLiteral FindNext FindPrevious DiffPrevious DiffNext Undo Redo Copy CopyLine Cut CutLine DuplicateLine DeleteLine IndentSelection OutdentSelection OutdentLine IndentLine Paste SelectAll OpenFile Start End PageUp PageDown SelectPageUp SelectPageDown HalfPageUp HalfPageDown StartOfLine EndOfLine StartOfText StartOfTextToggle ParagraphPrevious ParagraphNext SelectToParagraphPrevious SelectToParagraphNext ToggleHelp ToggleDiffGutter ToggleRuler JumpLine ResetSearch ClearInfo ClearStatus ShellMode CommandMode Quit QuitAll AddTab PreviousTab NextTab NextSplit Unsplit VSplit HSplit PreviousSplit ToggleMacro PlayMacro Suspend (Unix only) ScrollUp ScrollDown SpawnMultiCursor SpawnMultiCursorUp SpawnMultiCursorDown SpawnMultiCursorSelect RemoveMultiCursor RemoveAllMultiCursors SkipMultiCursor None JumpToMatchingBrace Autocomplete ``` The `StartOfTextToggle` and `SelectToStartOfTextToggle` actions toggle between jumping to the start of the text (first) and start of the line. You can also bind some mouse actions (these must be bound to mouse buttons) ``` MousePress MouseMultiCursor ``` Here is the list of all possible keys you can bind: ``` Up Down Right Left UpLeft UpRight DownLeft DownRight Center PageUp PageDown Home End Insert Delete Help Exit Clear Cancel Print Pause Backtab F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 F32 F33 F34 F35 F36 F37 F38 F39 F40 F41 F42 F43 F44 F45 F46 F47 F48 F49 F50 F51 F52 F53 F54 F55 F56 F57 F58 F59 F60 F61 F62 F63 F64 CtrlSpace Ctrl-a Ctrl-b Ctrl-c Ctrl-d Ctrl-e Ctrl-f Ctrl-g Ctrl-h Ctrl-i Ctrl-j Ctrl-k Ctrl-l Ctrl-m Ctrl-n Ctrl-o Ctrl-p Ctrl-q Ctrl-r Ctrl-s Ctrl-t Ctrl-u Ctrl-v Ctrl-w Ctrl-x Ctrl-y Ctrl-z CtrlLeftSq CtrlBackslash CtrlRightSq CtrlCarat CtrlUnderscore Backspace OldBackspace Tab Esc Escape Enter ``` You can also bind some mouse buttons (they may be bound to normal actions or mouse actions) ``` MouseLeft MouseLeftDrag MouseLeftRelease MouseMiddle MouseMiddleDrag MouseMiddleRelease MouseRight MouseRightDrag MouseRightRelease MouseWheelUp MouseWheelDown MouseWheelLeft MouseWheelRight ``` ## Key sequences Key sequences can be bound by specifying valid keys one after another in brackets, such as ``. # Default keybinding configuration. A select few keybindings are different on MacOS compared to other operating systems. This is because different OSes have different conventions for text editing defaults. ```json { "Up": "CursorUp", "Down": "CursorDown", "Right": "CursorRight", "Left": "CursorLeft", "ShiftUp": "SelectUp", "ShiftDown": "SelectDown", "ShiftLeft": "SelectLeft", "ShiftRight": "SelectRight", "AltLeft": "WordLeft", (Mac) "AltRight": "WordRight", (Mac) "AltUp": "MoveLinesUp", "AltDown": "MoveLinesDown", "CtrlShiftRight": "SelectWordRight", "CtrlShiftLeft": "SelectWordLeft", "AltLeft": "StartOfTextToggle", "AltRight": "EndOfLine", "AltShiftRight": "SelectWordRight", (Mac) "AltShiftLeft": "SelectWordLeft", (Mac) "CtrlLeft": "StartOfText", (Mac) "CtrlRight": "EndOfLine", (Mac) "AltShiftLeft": "SelectToStartOfTextToggle", "CtrlShiftLeft": "SelectToStartOfTextToggle", (Mac) "ShiftHome": "SelectToStartOfTextToggle", "AltShiftRight": "SelectToEndOfLine", "CtrlShiftRight": "SelectToEndOfLine", (Mac) "ShiftEnd": "SelectToEndOfLine", "CtrlUp": "CursorStart", "CtrlDown": "CursorEnd", "CtrlShiftUp": "SelectToStart", "CtrlShiftDown": "SelectToEnd", "Alt-{": "ParagraphPrevious", "Alt-}": "ParagraphNext", "Enter": "InsertNewline", "Ctrl-h": "Backspace", "Backspace": "Backspace", "Alt-CtrlH": "DeleteWordLeft", "Alt-Backspace": "DeleteWordLeft", "Tab": "Autocomplete|IndentSelection|InsertTab", "Backtab": "OutdentSelection|OutdentLine", "Ctrl-o": "OpenFile", "Ctrl-s": "Save", "Ctrl-f": "Find", "Alt-F": "FindLiteral", "Ctrl-n": "FindNext", "Ctrl-p": "FindPrevious", "Alt-[": "DiffPrevious|CursorStart", "Alt-]": "DiffNext|CursorEnd", "Ctrl-z": "Undo", "Ctrl-y": "Redo", "Ctrl-c": "CopyLine|Copy", "Ctrl-x": "Cut", "Ctrl-k": "CutLine", "Ctrl-d": "DuplicateLine", "Ctrl-v": "Paste", "Ctrl-a": "SelectAll", "Ctrl-t": "AddTab", "Alt-,": "PreviousTab", "Alt-.": "NextTab", "Home": "StartOfText", "End": "EndOfLine", "CtrlHome": "CursorStart", "CtrlEnd": "CursorEnd", "PageUp": "CursorPageUp", "PageDown": "CursorPageDown", "CtrlPageUp": "PreviousTab", "CtrlPageDown": "NextTab", "ShiftPageUp": "SelectPageUp", "ShiftPageDown": "SelectPageDown", "Ctrl-g": "ToggleHelp", "Alt-g": "ToggleKeyMenu", "Ctrl-r": "ToggleRuler", "Ctrl-l": "command-edit:goto ", "Delete": "Delete", "Ctrl-b": "ShellMode", "Ctrl-q": "Quit", "Ctrl-e": "CommandMode", "Ctrl-w": "NextSplit", "Ctrl-u": "ToggleMacro", "Ctrl-j": "PlayMacro", "Insert": "ToggleOverwriteMode", // Emacs-style keybindings "Alt-f": "WordRight", "Alt-b": "WordLeft", "Alt-a": "StartOfLine", "Alt-e": "EndOfLine", // Integration with file managers "F2": "Save", "F3": "Find", "F4": "Quit", "F7": "Find", "F10": "Quit", "Esc": "Escape", // Mouse bindings "MouseWheelUp": "ScrollUp", "MouseWheelDown": "ScrollDown", "MouseLeft": "MousePress", "MouseLeftDrag": "MouseDrag", "MouseLeftRelease": "MouseRelease", "MouseMiddle": "PastePrimary", "Ctrl-MouseLeft": "MouseMultiCursor", // Multi-cursor bindings "Alt-n": "SpawnMultiCursor", "AltShiftUp": "SpawnMultiCursorUp", "AltShiftDown": "SpawnMultiCursorDown", "Alt-m": "SpawnMultiCursorSelect", "Alt-p": "RemoveMultiCursor", "Alt-c": "RemoveAllMultiCursors", "Alt-x": "SkipMultiCursor", } ``` ## Pane type bindings Keybindings can be specified for different pane types as well. For example, to make a binding that only affects the command bar, use the `command` subgroup: ``` { "command": { "Ctrl-w": "WordLeft" } } ``` The possible pane types are `buffer` (normal buffer), `command` (command bar), and `terminal` (terminal pane). The defaults for the command and terminal panes are given below: ``` { "terminal": { "": "Exit", "": "CommandMode", "": "NextSplit" }, "command": { "Up": "HistoryUp", "Down": "HistoryDown", "Right": "CursorRight", "Left": "CursorLeft", "ShiftUp": "SelectUp", "ShiftDown": "SelectDown", "ShiftLeft": "SelectLeft", "ShiftRight": "SelectRight", "AltLeft": "StartOfTextToggle", "AltRight": "EndOfLine", "AltUp": "CursorStart", "AltDown": "CursorEnd", "AltShiftRight": "SelectWordRight", "AltShiftLeft": "SelectWordLeft", "CtrlLeft": "WordLeft", "CtrlRight": "WordRight", "CtrlShiftLeft": "SelectToStartOfTextToggle", "ShiftHome": "SelectToStartOfTextToggle", "CtrlShiftRight": "SelectToEndOfLine", "ShiftEnd": "SelectToEndOfLine", "CtrlUp": "CursorStart", "CtrlDown": "CursorEnd", "CtrlShiftUp": "SelectToStart", "CtrlShiftDown": "SelectToEnd", "Enter": "ExecuteCommand", "CtrlH": "Backspace", "Backspace": "Backspace", "OldBackspace": "Backspace", "Alt-CtrlH": "DeleteWordLeft", "Alt-Backspace": "DeleteWordLeft", "Tab": "CommandComplete", "Backtab": "CycleAutocompleteBack", "Ctrl-z": "Undo", "Ctrl-y": "Redo", "Ctrl-c": "CopyLine|Copy", "Ctrl-x": "Cut", "Ctrl-k": "CutLine", "Ctrl-v": "Paste", "Home": "StartOfTextToggle", "End": "EndOfLine", "CtrlHome": "CursorStart", "CtrlEnd": "CursorEnd", "Delete": "Delete", "Ctrl-q": "AbortCommand", "Ctrl-e": "EndOfLine", "Ctrl-a": "StartOfLine", "Ctrl-w": "DeleteWordLeft", "Insert": "ToggleOverwriteMode", "Ctrl-b": "WordLeft", "Ctrl-f": "WordRight", "Ctrl-d": "DeleteWordLeft", "Ctrl-m": "ExecuteCommand", "Ctrl-n": "HistoryDown", "Ctrl-p": "HistoryUp", "Ctrl-u": "SelectToStart", // Emacs-style keybindings "Alt-f": "WordRight", "Alt-b": "WordLeft", "Alt-a": "StartOfText", "Alt-e": "EndOfLine", // Integration with file managers "F10": "AbortCommand", "Esc": "AbortCommand", // Mouse bindings "MouseWheelUp": "HistoryUp", "MouseWheelDown": "HistoryDown", "MouseLeft": "MousePress", "MouseLeftDrag": "MouseDrag", "MouseLeftRelease": "MouseRelease", "MouseMiddle": "PastePrimary" } } ``` ## Final notes Note: On some old terminal emulators and on Windows machines, `Ctrl-h` should be used for backspace. Additionally, alt keys can be bound by using `Alt-key`. For example `Alt-a` or `Alt-Up`. Micro supports an optional `-` between modifiers like `Alt` and `Ctrl` so `Alt-a` could be rewritten as `Alta` (case matters for alt bindings). This is why in the default keybindings you can see `AltShiftLeft` instead of `Alt-ShiftLeft` (they are equivalent). Please note that terminal emulators are strange applications and micro only receives key events that the terminal decides to send. Some terminal emulators may not send certain events even if this document says micro can receive the event. To see exactly what micro receives from the terminal when you press a key, run the `> raw` command. micro-2.0.14/runtime/help/options.md0000664000175000017510000005663314663411671016716 0ustar nileshnilesh# Options Micro stores all of the user configuration in its configuration directory. Micro uses `$MICRO_CONFIG_HOME` as the configuration directory. If this environment variable is not set, it uses `$XDG_CONFIG_HOME/micro` instead. If that environment variable is not set, it uses `~/.config/micro` as the configuration directory. In the documentation, we use `~/.config/micro` to refer to the configuration directory (even if it may in fact be somewhere else if you have set either of the above environment variables). Here are the available options: * `autoindent`: when creating a new line, use the same indentation as the previous line. default value: `true` * `autosave`: automatically save the buffer every n seconds, where n is the value of the autosave option. Also when quitting on a modified buffer, micro will automatically save and quit. Be warned, this option saves the buffer without prompting the user, so data may be overwritten. If this option is set to `0`, no autosaving is performed. default value: `0` * `autosu`: When a file is saved that the user doesn't have permission to modify, micro will ask if the user would like to use super user privileges to save the file. If this option is enabled, micro will automatically attempt to use super user privileges to save without asking the user. default value: `false` * `backup`: micro will automatically keep backups of all open buffers. Backups are stored in `~/.config/micro/backups` and are removed when the buffer is closed cleanly. In the case of a system crash or a micro crash, the contents of the buffer can be recovered automatically by opening the file that was being edited before the crash, or manually by searching for the backup in the backup directory. Backups are made in the background for newly modified buffers every 8 seconds, or when micro detects a crash. default value: `true` * `backupdir`: the directory micro should place backups in. For the default value of `""` (empty string), the backup directory will be `ConfigDir/backups`, which is `~/.config/micro/backups` by default. The directory specified for backups will be created if it does not exist. default value: `""` (empty string) * `basename`: in the infobar and tabbar, show only the basename of the file being edited rather than the full path. default value: `false` * `clipboard`: specifies how micro should access the system clipboard. Possible values are: * `external`: accesses clipboard via an external tool, such as xclip/xsel or wl-clipboard on Linux, pbcopy/pbpaste on MacOS, and system calls on Windows. On Linux, if you do not have one of the tools installed, or if they are not working, micro will throw an error and use an internal clipboard. * `terminal`: accesses the clipboard via your terminal emulator. Note that there is limited support among terminal emulators for this feature (called OSC 52). Terminals that are known to work are Kitty (enable reading with `clipboard_control` setting), iTerm2 (only copying), st, rxvt-unicode and xterm if enabled (see `> help copypaste` for details). Note that Gnome-terminal does not support this feature. With this setting, copy-paste **will** work over ssh. See `> help copypaste` for details. * `internal`: micro will use an internal clipboard. default value: `external` * `colorcolumn`: if this is not set to 0, it will display a column at the specified column. This is useful if you want column 80 to be highlighted special for example. default value: `0` * `colorscheme`: loads the colorscheme stored in $(configDir)/colorschemes/`option`.micro, This setting is `global only`. default value: `default` Note that the default colorschemes (default, solarized, and solarized-tc) are not located in configDir, because they are embedded in the micro binary. The colorscheme can be selected from all the files in the ~/.config/micro/colorschemes/ directory. Micro comes by default with three colorschemes: You can read more about micro's colorschemes in the `colors` help topic (`help colors`). * `cursorline`: highlight the line that the cursor is on in a different color (the color is defined by the colorscheme you are using). default value: `true` * `detectlimit`: if this is not set to 0, it will limit the amount of first lines in a file that are matched to determine the filetype. A higher limit means better accuracy of guessing the filetype, but also taking more time. default value: `100` * `diffgutter`: display diff indicators before lines. default value: `false` * `divchars`: specifies the "divider" characters used for the dividing line between vertical/horizontal splits. The first character is for vertical dividers, and the second is for horizontal dividers. By default, for horizontal splits the statusline serves as a divider, but if the statusline is disabled the horizontal divider character will be used. default value: `|-` * `divreverse`: colorschemes provide the color (foreground and background) for the characters displayed in split dividers. With this option enabled, the colors specified by the colorscheme will be reversed (foreground and background colors swapped). default value: `true` * `encoding`: the encoding to open and save files with. Supported encodings are listed at https://www.w3.org/TR/encoding/. default value: `utf-8` * `eofnewline`: micro will automatically add a newline to the end of the file if one does not exist. default value: `true` * `fakecursor`: forces micro to render the cursor using terminal colors rather than the actual terminal cursor. This is useful when the terminal's cursor is slow or otherwise unavailable/undesirable to use. default value: `false` * `fastdirty`: this determines what kind of algorithm micro uses to determine if a buffer is modified or not. When `fastdirty` is on, micro just uses a boolean `modified` that is set to `true` as soon as the user makes an edit. This is fast, but can be inaccurate. If `fastdirty` is off, then micro will hash the current buffer against a hash of the original file (created when the buffer was loaded). This is more accurate but obviously more resource intensive. This option will be automatically disabled if the file size exceeds 50KB. default value: `false` * `fileformat`: this determines what kind of line endings micro will use for the file. Unix line endings are just `\n` (linefeed) whereas dos line endings are `\r\n` (carriage return + linefeed). The two possible values for this option are `unix` and `dos`. The fileformat will be automatically detected (when you open an existing file) and displayed on the statusline, but this option is useful if you would like to change the line endings or if you are starting a new file. Changing this option while editing a file will change its line endings. Opening a file with this option set will only have an effect if the file is empty/newly created, because otherwise the fileformat will be automatically detected from the existing line endings. default value: `unix` on Unix systems, `dos` on Windows * `filetype`: sets the filetype for the current buffer. Set this option to `off` to completely disable filetype detection. default value: `unknown`. This will be automatically overridden depending on the file you open. * `hlsearch`: highlight all instances of the searched text after a successful search. This highlighting can be temporarily turned off via the `UnhighlightSearch` action (triggered by the Esc key by default) or toggled on/off via the `ToggleHighlightSearch` action. Note that these actions don't change the `hlsearch` setting. As long as `hlsearch` is set to true, the next search will have the highlighting turned on again. default value: `false` * `hltaberrors`: highlight tabs when spaces are expected, and spaces when tabs are expected. More precisely: if `tabstospaces` option is on, highlight all tab characters; if `tabstospaces` is off, highlight space characters in the initial indent part of the line. default value: `false` * `hltrailingws`: highlight trailing whitespaces at ends of lines. Note that it doesn't highlight newly added trailing whitespaces that naturally occur while typing text. It highlights only nasty forgotten trailing whitespaces. default value: `false` * `incsearch`: enable incremental search in "Find" prompt (matching as you type). default value: `true` * `ignorecase`: perform case-insensitive searches. default value: `true` * `indentchar`: sets the indentation character. This will not be inserted into files; it is only a visual indicator that whitespace is present. If set to a printing character, it functions as a subset of the "show invisibles" setting available in many other text editors. The color of this character is determined by the `indent-char` field in the current theme rather than the default text color. default value: ` ` (space) * `infobar`: enables the line at the bottom of the editor where messages are printed. This option is `global only`. default value: `true` * `keepautoindent`: when using autoindent, whitespace is added for you. This option determines if when you move to the next line without any insertions the whitespace that was added should be deleted to remove trailing whitespace. By default, the autoindent whitespace is deleted if the line was left empty. default value: `false` * `keymenu`: display the nano-style key menu at the bottom of the screen. Note that ToggleKeyMenu is bound to `Alt-g` by default and this is displayed in the statusline. To disable the key binding, bind `Alt-g` to `None`. default value: `false` * `matchbrace`: show matching braces for '()', '{}', '[]' when the cursor is on a brace character or (if `matchbraceleft` is enabled) next to it. default value: `true` * `matchbraceleft`: simulate I-beam cursor behavior (cursor located not on a character but "between" characters): when showing matching braces, if there is no brace character directly under the cursor, match the brace character to the left of the cursor instead. Also when jumping to the matching brace, move the cursor either to the matching brace character or to the character next to it, depending on whether the initial cursor position was on the brace character or next to it (i.e. "inside" or "outside" the braces). With `matchbraceleft` disabled, micro will only match the brace directly under the cursor and will only jump to precisely to the matching brace. default value: `true` * `matchbracestyle`: whether to underline or highlight matching braces when `matchbrace` is enabled. The color of highlight is determined by the `match-brace` field in the current theme. Possible values: * `underline`: underline matching braces. * `highlight`: use `match-brace` style from the current theme. default value: `underline` * `mkparents`: if a file is opened on a path that does not exist, the file cannot be saved because the parent directories don't exist. This option lets micro automatically create the parent directories in such a situation. default value: `false` * `mouse`: mouse support. When mouse support is disabled, usually the terminal will be able to access mouse events which can be useful if you want to copy from the terminal instead of from micro (if over ssh for example, because the terminal has access to the local clipboard and micro does not). default value: `true` * `multiopen`: specifies how to layout multiple files opened at startup. Most useful as a command-line option, like `-multiopen vsplit`. Possible values correspond to commands (see `> help commands`) that open files: * `tab`: open each file in a separate tab. * `vsplit`: open files side-by-side. * `hsplit`: open files stacked top to bottom. default value: `tab` * `paste`: treat characters sent from the terminal in a single chunk as a paste event rather than a series of manual key presses. If you are pasting using the terminal keybinding (not `Ctrl-v`, which is micro's default paste keybinding) then it is a good idea to enable this option during the paste and disable once the paste is over. See `> help copypaste` for details about copying and pasting in a terminal environment. default value: `false` * `parsecursor`: if enabled, this will cause micro to parse filenames such as file.txt:10:5 as requesting to open `file.txt` with the cursor at line 10 and column 5. The column number can also be dropped to open the file at a given line and column 0. Note that with this option enabled it is not possible to open a file such as `file.txt:10:5`, where `:10:5` is part of the filename. It is also possible to open a file with a certain cursor location by using the `+LINE:COL` flag syntax. See `micro -help` for the command line options. default value: `false` * `permbackup`: this option causes backups (see `backup` option) to be permanently saved. With permanent backups, micro will not remove backups when files are closed and will never apply them to existing files. Use this option if you are interested in manually managing your backup files. default value: `false` * `pluginchannels`: list of URLs pointing to plugin channels for downloading and installing plugins. A plugin channel consists of a json file with links to plugin repos, which store information about plugin versions and download URLs. By default, this option points to the official plugin channel hosted on GitHub at https://github.com/micro-editor/plugin-channel. default value: `https://raw.githubusercontent.com/micro-editor/plugin-channel/master/channel.json` * `pluginrepos`: a list of links to plugin repositories. default value: `` * `readonly`: when enabled, disallows edits to the buffer. It is recommended to only ever set this option locally using `setlocal`. default value: `false` * `reload`: controls the reload behavior of the current buffer in case the file has changed. The available options are `prompt`, `auto` & `disabled`. default value: `prompt` * `rmtrailingws`: micro will automatically trim trailing whitespaces at ends of lines. Note: This setting overrides `keepautoindent` and isn't used at timed `autosave` or forced `autosave` in case the buffer didn't change. A manual save will involve the action regardless if the buffer has been changed or not. default value: `false` * `ruler`: display line numbers. default value: `true` * `relativeruler`: make line numbers display relatively. If set to true, all lines except for the line that the cursor is located will display the distance from the cursor's line. default value: `false` * `savecursor`: remember where the cursor was last time the file was opened and put it there when you open the file again. Information is saved to `~/.config/micro/buffers/` default value: `false` * `savehistory`: remember command history between closing and re-opening micro. Information is saved to `~/.config/micro/buffers/history`. default value: `true` * `saveundo`: when this option is on, undo is saved even after you close a file so if you close and reopen a file, you can keep undoing. Information is saved to `~/.config/micro/buffers/`. default value: `false` * `scrollbar`: display a scroll bar default value: `false` * `scrollbarchar`: specifies the character used for displaying the scrollbar default value: `|` * `scrollmargin`: margin at which the view starts scrolling when the cursor approaches the edge of the view. default value: `3` * `scrollspeed`: amount of lines to scroll for one scroll event. default value: `2` * `smartpaste`: add leading whitespace when pasting multiple lines. This will attempt to preserve the current indentation level when pasting an unindented block. default value: `true` * `softwrap`: wrap lines that are too long to fit on the screen. default value: `false` * `splitbottom`: when a horizontal split is created, create it below the current split. default value: `true` * `splitright`: when a vertical split is created, create it to the right of the current split. default value: `true` * `statusformatl`: format string definition for the left-justified part of the statusline. Special directives should be placed inside `$()`. Special directives include: `filename`, `modified`, `line`, `col`, `lines`, `percentage`, `opt`, `bind`. The `opt` and `bind` directives take either an option or an action afterward and fill in the value of the option or the key bound to the action. default value: `$(filename) $(modified)($(line),$(col)) $(status.paste)| ft:$(opt:filetype) | $(opt:fileformat) | $(opt:encoding)` * `statusformatr`: format string definition for the right-justified part of the statusline. default value: `$(bind:ToggleKeyMenu): bindings, $(bind:ToggleHelp): help` * `statusline`: display the status line at the bottom of the screen. default value: `true` * `sucmd`: specifies the super user command. On most systems this is "sudo" but on BSD it can be "doas." This option can be customized and is only used when saving with su. default value: `sudo` * `syntax`: enables syntax highlighting. default value: `true` * `tabmovement`: navigate spaces at the beginning of lines as if they are tabs (e.g. move over 4 spaces at once). This option only does anything if `tabstospaces` is on. default value: `false` * `tabhighlight`: inverts the tab characters' (filename, save indicator, etc) colors with respect to the tab bar. default value: false * `tabreverse`: reverses the tab bar colors when active. default value: true * `tabsize`: the size in spaces that a tab character should be displayed with. default value: `4` * `tabstospaces`: use spaces instead of tabs. Note: This option will be overridden by [the `ftoptions` plugin](https://github.com/zyedidia/micro/blob/master/runtime/plugins/ftoptions/ftoptions.lua) for certain filetypes. To disable this behavior, add `"ftoptions": false` to your config. See [issue #2213](https://github.com/zyedidia/micro/issues/2213) for more details. default value: `false` * `useprimary` (only useful on unix): defines whether or not micro will use the primary clipboard to copy selections in the background. This does not affect the normal clipboard using `Ctrl-c` and `Ctrl-v`. default value: `true` * `wordwrap`: wrap long lines by words, i.e. break at spaces. This option only does anything if `softwrap` is on. default value: `false` * `xterm`: micro will assume that the terminal it is running in conforms to `xterm-256color` regardless of what the `$TERM` variable actually contains. Enabling this option may cause unwanted effects if your terminal in fact does not conform to the `xterm-256color` standard. default value: `false` --- Plugin options: all plugins come with a special option to enable or disable them. The option is a boolean with the same name as the plugin itself. By default, the following plugins are provided, each with an option to enable or disable them: * `autoclose`: automatically closes brackets, quotes, etc... * `comment`: provides automatic commenting for a number of languages * `ftoptions`: alters some default options depending on the filetype * `linter`: provides extensible linting for many languages * `literate`: provides advanced syntax highlighting for the Literate programming tool. * `status`: provides some extensions to the status line (integration with Git and more). * `diff`: integrates the `diffgutter` option with Git. If you are in a Git directory, the diff gutter will show changes with respect to the most recent Git commit rather than the diff since opening the file. Any option you set in the editor will be saved to the file ~/.config/micro/settings.json so, in effect, your configuration file will be created for you. If you'd like to take your configuration with you to another machine, simply copy the settings.json to the other machine. ## Settings.json file The settings.json file should go in your configuration directory (by default at `~/.config/micro`), and should contain only options which have been modified from their default setting. Here is the full list of options in json format, so that you can see what the formatting should look like. ```json { "autoclose": true, "autoindent": true, "autosave": 0, "autosu": false, "backup": true, "backupdir": "", "basename": false, "clipboard": "external", "colorcolumn": 0, "colorscheme": "default", "comment": true, "cursorline": true, "diff": true, "diffgutter": false, "divchars": "|-", "divreverse": true, "encoding": "utf-8", "eofnewline": true, "fastdirty": false, "fileformat": "unix", "filetype": "unknown", "incsearch": true, "ftoptions": true, "ignorecase": true, "indentchar": " ", "infobar": true, "initlua": true, "keepautoindent": false, "keymenu": false, "linter": true, "literate": true, "matchbrace": true, "matchbraceleft": true, "matchbracestyle": "underline", "mkparents": false, "mouse": true, "parsecursor": false, "paste": false, "permbackup": false, "pluginchannels": [ "https://raw.githubusercontent.com/micro-editor/plugin-channel/master/channel.json" ], "pluginrepos": [], "readonly": false, "relativeruler": false, "rmtrailingws": false, "ruler": true, "savecursor": false, "savehistory": true, "saveundo": false, "scrollbar": false, "scrollmargin": 3, "scrollspeed": 2, "smartpaste": true, "softwrap": false, "splitbottom": true, "splitright": true, "status": true, "statusformatl": "$(filename) $(modified)($(line),$(col)) $(status.paste)| ft:$(opt:filetype) | $(opt:fileformat) | $(opt:encoding)", "statusformatr": "$(bind:ToggleKeyMenu): bindings, $(bind:ToggleHelp): help", "statusline": true, "sucmd": "sudo", "syntax": true, "tabmovement": false, "tabhighlight": true, "tabreverse": false, "tabsize": 4, "tabstospaces": false, "useprimary": true, "xterm": false } ``` ## Global and local settings You can set these settings either globally or locally. Locally means that the setting won't be saved to `~/.config/micro/settings.json` and that it will only be set in the current buffer. Setting an option globally is the default, and will set the option in all buffers. Use the `setlocal` command to set an option locally rather than globally. The `colorscheme` option is global only, and the `filetype` option is local only. To set an option locally, use `setlocal` instead of `set`. In the `settings.json` file you can also put set options locally by specifying either a glob or a filetype. Here is an example which has `tabstospaces` on for all files except Go files, and `tabsize` 4 for all files except Ruby files: ```json { "ft:go": { "tabstospaces": false }, "ft:ruby": { "tabsize": 2 }, "tabstospaces": true, "tabsize": 4 } ``` Or similarly you can match with globs: ```json { "*.go": { "tabstospaces": false }, "*.rb": { "tabsize": 2 }, "tabstospaces": true, "tabsize": 4 } ``` micro-2.0.14/runtime/help/plugins.md0000664000175000017510000005350414663411671016676 0ustar nileshnilesh# Plugins This help topic is about creating plugins. If you need help installing or managing plugins, look for `plugin` commands in `help commands`. If you want to enable or disable a plugin, look for `Plugin options` in `help options`. Micro supports creating plugins with a simple Lua system. Plugins are folders containing Lua files and possibly other source files placed in `~/.config/micro/plug`. The plugin directory (within `plug`) should contain at least one Lua file and a `repo.json` file. The `repo.json` file provides additional information such as the name of the plugin, the plugin's website, dependencies, etc. [Here is an example `repo.json` file](https://github.com/micro-editor/updated-plugins/blob/master/go-plugin/repo.json) from the go plugin, which has the following file structure: ``` ~/.config/micro/plug/go-plugin/ go.lua repo.json help/ go-plugin.md ``` The `go.lua` file contains the main code for the plugin, though the code may be distributed across multiple Lua files. The `repo.json` file contains information about the plugin, such as the website, description, version, and any requirements. Plugins may also have additional files that can be added to micro's runtime files, of which there are 5 types: * Colorschemes * Syntax files * Help files * Plugin files * Syntax header files In most cases, a plugin will want to add help files, but in certain cases a plugin may also want to add colorschemes or syntax files. No directory structure is enforced, but keeping runtime files in their own directories is good practice. ## Lua callbacks Plugins use Lua but also have access to many functions, both from micro and from the Go standard library. Plugins can also define functions that micro will call when certain events happen. Here is the list of callbacks that micro defines: * `init()`: this function should be used for your plugin initialization. This function is called after buffers have been initialized. * `preinit()`: initialization function called before buffers have been initialized. * `postinit()`: initialization function called after the `init()` function of all plugins has been called. * `deinit()`: cleanup function called when your plugin is unloaded or reloaded. * `onSetActive(bufpane)`: runs when changing the currently active panel. * `onBufferOpen(buf)`: runs when a buffer is opened. The input contains the buffer object. * `onBufPaneOpen(bufpane)`: runs when a bufpane is opened. The input contains the bufpane object. * `onSetActive(bufpane)`: runs when changing the currently active bufpane. * `onAction(bufpane)`: runs when `Action` is triggered by the user, where `Action` is a bindable action (see `> help keybindings`). A bufpane is passed as input and the function should return a boolean defining whether the view should be relocated after this action is performed. * `preAction(bufpane)`: runs immediately before `Action` is triggered by the user. Returns a boolean which defines whether the action should be canceled. * `onRune(bufpane, rune)`: runs when the composed rune has been inserted * `preRune(bufpane, rune)`: runs before the composed rune will be inserted * `onAnyEvent()`: runs when literally anything happens. It is useful for detecting various changes of micro's state that cannot be detected using other callbacks. For example, a function that is run every time the user saves the buffer would be: ```lua function onSave(bp) ... return false end ``` The `bp` variable is a reference to the bufpane the action is being executed within. This is almost always the current bufpane. All available actions are listed in the keybindings section of the help. These functions should also return a boolean specifying whether the bufpane should be relocated to the cursor or not after the action is complete. ## Accessing micro functions Some of micro's internal information is exposed in the form of packages, which can be imported by Lua plugins. A package can be imported in Lua, and a value within it can be accessed using the following syntax: ```lua local micro = import("micro") micro.Log("Hello") ``` The packages and their contents are listed below (in Go type signatures): * `micro` - `TermMessage(msg interface{}...)`: temporarily close micro and print a message - `TermError(filename string, lineNum int, err string)`: temporarily close micro and print an error formatted as `filename, lineNum: err`. - `InfoBar() *InfoPane`: return the infobar BufPane object. - `Log(msg interface{}...)`: write a message to `log.txt` (requires `-debug` flag, or binary built with `build-dbg`). - `SetStatusInfoFn(fn string)`: register the given lua function as accessible from the statusline formatting options. - `CurPane() *BufPane`: returns the current BufPane, or nil if the current pane is not a BufPane. - `CurTab() *Tab`: returns the current tab. - `Tabs() *TabList`: returns the global tab list. - `After(t time.Duration, f func())`: run function `f` in the background after time `t` elapses. See https://pkg.go.dev/time#Duration for the usage of `time.Duration`. Relevant links: [Time](https://pkg.go.dev/time#Duration) [BufPane](https://pkg.go.dev/github.com/zyedidia/micro/v2/internal/action#BufPane) [InfoPane](https://pkg.go.dev/github.com/zyedidia/micro/v2/internal/action#InfoPane) [Tab](https://pkg.go.dev/github.com/zyedidia/micro/v2/internal/action#Tab) [TabList](https://pkg.go.dev/github.com/zyedidia/micro/v2/internal/action#TabList) [interface{} / any](https://go.dev/tour/methods/14) * `micro/config` - `MakeCommand(name string, action func(bp *BufPane, args[]string), completer buffer.Completer)`: create a command with the given name, and lua callback function when the command is run. A completer may also be given to specify how autocompletion should work with the custom command. Any lua function that takes a Buffer argument and returns a pair of string arrays is a valid completer, as are the built in completers below: - `FileComplete`: autocomplete using files in the current directory - `HelpComplete`: autocomplete using names of help documents - `OptionComplete`: autocomplete using names of options - `OptionValueComplete`: autocomplete using names of options, and valid values afterwards - `NoComplete`: no autocompletion suggestions - `TryBindKey(k, v string, overwrite bool) (bool, error)`: bind the key `k` to the string `v` in the `bindings.json` file. If `overwrite` is true, this will overwrite any existing binding to key `k`. Returns true if the binding was made, and a possible error (for example writing to `bindings.json` can cause an error). - `Reload()`: reload configuration files. - `AddRuntimeFileFromMemory(filetype RTFiletype, filename, data string)`: add a runtime file to the `filetype` runtime filetype, with name `filename` and data `data`. - `AddRuntimeFilesFromDirectory(plugin string, filetype RTFiletype, directory, pattern string)`: add runtime files for the given plugin with the given RTFiletype from a directory within the plugin root. Only adds files that match the pattern using Go's `filepath.Match` - `AddRuntimeFile(plugin string, filetype RTFiletype, filepath string)`: add a given file inside the plugin root directory as a runtime file to the given RTFiletype category. - `ListRuntimeFiles(fileType RTFiletype) []string`: returns a list of names of runtime files of the given type. - `ReadRuntimeFile(fileType RTFiletype, name string) string`: returns the contents of a given runtime file. - `NewRTFiletype() int`: creates a new RTFiletype, and returns its value. - `RTColorscheme`: runtime files for colorschemes. - `RTSyntax`: runtime files for syntax files. - `RTHelp`: runtime files for help documents. - `RTPlugin`: runtime files for plugin source code. - `RegisterCommonOption(pl string, name string, defaultvalue interface{})`: registers a new option for the given plugin. The name of the option will be `pl.name`, and will have the given default value. Since this registers a common option, the option will be modifiable on a per-buffer basis, while also having a global value (in the GlobalSettings map). - `RegisterGlobalOption(pl string, name string, defaultvalue interface{})`: same as `RegisterCommonOption`, but the option cannot be modified locally to each buffer. - `GetGlobalOption(name string) interface{}`: returns the value of a given plugin in the `GlobalSettings` map. - `SetGlobalOption(option, value string) error`: sets an option to a given value. Same as using the `> set` command. This will try to convert the value into the proper type for the option. Can return an error if the option name is not valid, or the value can not be converted. - `SetGlobalOptionNative(option string, value interface{}) error`: sets an option to a given value, where the type of value is the actual type of the value internally. Can return an error if the provided value is not valid for the given option. - `ConfigDir`: the path to micro's currently active config directory. Relevant links: [Buffer](https://pkg.go.dev/github.com/zyedidia/micro/v2/internal/buffer#Buffer) [buffer.Completer](https://pkg.go.dev/github.com/zyedidia/micro/v2/internal/buffer#Completer) [Error](https://pkg.go.dev/builtin#error) [interface{} / any](https://go.dev/tour/methods/14) [filepath.Match](https://pkg.go.dev/path/filepath#Match) * `micro/shell` - `ExecCommand(name string, arg ...string) (string, error)`: runs an executable with the given arguments, and pipes the output (stderr and stdout) of the executable to an internal buffer, which is returned as a string, along with a possible error. - `RunCommand(input string) (string, error)`: same as `ExecCommand`, except this uses micro's argument parser to parse the arguments from the input. For example, `cat 'hello world.txt' file.txt`, will pass two arguments in the `ExecCommand` argument list (quoting arguments will preserve spaces). - `RunBackgroundShell(input string) (func() string, error)`: returns a function that will run the given shell command and return its output. - `RunInteractiveShell(input string, wait bool, getOutput bool) (string, error)`: temporarily closes micro and runs the given command in the terminal. If `wait` is true, micro will wait for the user to press enter before returning to text editing. If `getOutput` is true, micro will redirect stdout from the command to the returned string. - `JobStart(cmd string, onStdout, onStderr, onExit func(string, []interface{}), userargs ...interface{}) *exec.Cmd`: Starts a background job by running the shell on the given command (using `sh -c`). Three callbacks can be provided which will be called when the command generates stdout, stderr, or exits. The userargs will be passed to the callbacks, along with the output as the first argument of the callback. Returns the started command. - `JobSpawn(cmd string, cmdArgs []string, onStdout, onStderr, onExit func(string, []interface{}), userargs ...interface{}) *exec.Cmd`: same as `JobStart`, except doesn't run the command through the shell and instead takes as inputs the list of arguments. Returns the started command. - `JobStop(cmd *exec.Cmd)`: kills a job. - `JobSend(cmd *exec.Cmd, data string)`: sends some data to a job's stdin. - `RunTermEmulator(h *BufPane, input string, wait bool, getOutput bool, callback func(out string, userargs []interface{}), userargs []interface{}) error`: starts a terminal emulator from a given BufPane with the input command. If `wait` is true, it will wait for the user to exit by pressing enter once the executable has terminated, and if `getOutput` is true, it will redirect the stdout of the process to a pipe, which will be passed to the callback, which is a function that takes a string and a list of optional user arguments. This function returns an error on systems where the terminal emulator is not supported. - `TermEmuSupported`: true on systems where the terminal emulator is supported and false otherwise. Supported systems: * Linux * MacOS * Dragonfly * OpenBSD * FreeBSD Relevant links: [Cmd](https://pkg.go.dev/os/exec#Cmd) [BufPane](https://pkg.go.dev/github.com/zyedidia/micro/v2/internal/action#BufPane) [Error](https://pkg.go.dev/builtin#error) * `micro/buffer` - `NewMessage(owner string, msg string, start, end, Loc, kind MsgType) *Message`: creates a new message with an owner over a range defined by the start and end locations. - `NewMessageAtLine(owner string, msg string, line int, kindMsgType) *Message`: creates a new message with owner, type, and text at a given line. - `MTInfo`: info message. - `MTWarning`: warning message. - `MTError` error message. - `Loc(x, y int) Loc`: creates a new location struct. - `SLoc(line, row int) display.SLoc`: creates a new scrolling location struct. - `BTDefault`: default buffer type. - `BTHelp`: help buffer type. - `BTLog`: log buffer type. - `BTScratch`: scratch buffer type (cannot be saved). - `BTRaw`: raw buffer type. - `BTInfo`: info buffer type. - `NewBuffer(text, path string) *Buffer`: creates a new buffer with the given text at a certain path. - `NewBufferFromFile(path string) (*Buffer, error)`: creates a new buffer by reading the file at the given path from disk. Returns an error if the read operation fails (for example, due to the file not existing). - `ByteOffset(pos Loc, buf *Buffer) int`: returns the byte index of the given position in a buffer. - `Log(s string)`: writes a string to the log buffer. - `LogBuf() *Buffer`: returns the log buffer. Relevant links: [Message](https://pkg.go.dev/github.com/zyedidia/micro/v2/internal/buffer#Message) [Loc](https://pkg.go.dev/github.com/zyedidia/micro/v2/internal/buffer#Loc) [display.SLoc](https://pkg.go.dev/github.com/zyedidia/micro/v2/internal/display#SLoc) [Buffer](https://pkg.go.dev/github.com/zyedidia/micro/v2/internal/buffer#Buffer) [Error](https://pkg.go.dev/builtin#error) * `micro/util` - `RuneAt(str string, idx int) string`: returns the utf8 rune at a given index within a string. - `GetLeadingWhitespace(s string) string`: returns the leading whitespace of a string. - `IsWordChar(s string) bool`: returns true if the first rune in a string is a word character. - `String(b []byte) string`: converts a byte array to a string. - `RuneStr(r rune) string`: converts a rune to a string. - `Unzip(src, dest string) error`: unzips a file to given folder. - `Version`: micro's version number or commit hash - `SemVersion`: micro's semantic version - `HttpRequest(method string, url string, headers []string) (http.Response, error)`: makes a http request. - `CharacterCountInString(str string) int`: returns the number of characters in a string - `RuneStr(r rune) string`: converts a rune to a string. Relevant links: [Rune](https://pkg.go.dev/builtin#rune) This may seem like a small list of available functions, but some of the objects returned by the functions have many methods. The Lua plugin may access any public methods of an object returned by any of the functions above. Unfortunately, it is not possible to list all the available functions on this page. Please go to the internal documentation at https://pkg.go.dev/github.com/zyedidia/micro/v2/internal to see the full list of available methods. Note that only methods of types that are available to plugins via the functions above can be called from a plugin. For an even more detailed reference, see the source code on Github. For example, with a BufPane object called `bp`, you could call the `Save` function in Lua with `bp:Save()`. Note that Lua uses the `:` syntax to call a function rather than Go's `.` syntax. ```go micro.InfoBar().Message() ``` turns to ```lua micro.InfoBar():Message() ``` ## Accessing the Go standard library It is possible for your lua code to access many of the functions in the Go standard library. Simply import the package you'd like, and then you can use it. For example: ```lua local ioutil = import("io/ioutil") local fmt = import("fmt") local micro = import("micro") local data, err = ioutil.ReadFile("SomeFile.txt") if err ~= nil then micro.InfoBar():Error("Error reading file: SomeFile.txt") else -- Data is returned as an array of bytes -- Using Sprintf will convert it to a string local str = fmt.Sprintf("%s", data) -- Do something with the file you just read! -- ... end ``` Here are the packages from the Go standard library that you can access. Nearly all functions from these packages are supported. For an exact list of functions that are supported, you can look through `lua.go` (which should be easy to understand). * [fmt](https://pkg.go.dev/fmt) * [io](https://pkg.go.dev/io) * [io/ioutil](https://pkg.go.dev/io/ioutil) * [net](https://pkg.go.dev/net) * [math](https://pkg.go.dev/math) * [math/rand](https://pkg.go.dev/math/rand) * [os](https://pkg.go.dev/os) * [runtime](https://pkg.go.dev/runtime) * [path](https://pkg.go.dev/path) * [filepath](https://pkg.go.dev/filepath) * [strings](https://pkg.go.dev/strings) * [regexp](https://pkg.go.dev/regexp) * [errors](https://pkg.go.dev/errors) * [time](https://pkg.go.dev/time) * [unicode/utf8](https://pkg.go.dev/unicode/utf8) * [archive/zip](https://pkg.go.dev/archive/zip) * [net/http](https://pkg.go.dev/net/http) The following functions from the go-humanize package are also available: * `humanize`: - `Bytes(s uint64) string`: produces a human readable representation of an SI size. - `Ordinal(x int) string`: gives you the input number in a rank/ordinal format. [The Lua standard library](https://www.lua.org/manual/5.1/manual.html#5) is also available to plugins, though it is rather small. ## Adding help files, syntax files, or colorschemes in your plugin You can use the `AddRuntimeFile(name string, type config.RTFiletype, path string)` function to add various kinds of files to your plugin. For example, if you'd like to add a help topic to your plugin called `test`, you would create a `test.md` file and call the function: ```lua config = import("micro/config") config.AddRuntimeFile("test", config.RTHelp, "test.md") ``` Use `AddRuntimeFilesFromDirectory(name, type, dir, pattern)` to add a number of files to the runtime. To read the content of a runtime file, use `ReadRuntimeFile(fileType, name string)` or `ListRuntimeFiles(fileType string)` for all runtime files. In addition, there is `AddRuntimeFileFromMemory` which adds a runtime file based on a string that may have been constructed at runtime. ## Default plugins The following plugins come pre-installed with micro: * `autoclose`: automatically closes brackets, quotes, etc... * `comment`: provides automatic commenting for a number of languages * `ftoptions`: alters some default options (notably indentation) depending on the filetype * `linter`: provides extensible linting for many languages * `literate`: provides advanced syntax highlighting for the Literate programming tool. * `status`: provides some extensions to the status line (integration with Git and more). * `diff`: integrates the `diffgutter` option with Git. If you are in a Git directory, the diff gutter will show changes with respect to the most recent Git commit rather than the diff since opening the file. See `> help linter`, `> help comment`, and `> help status` for additional documentation specific to those plugins. These are good examples for many use-cases if you are looking to write your own plugins. ## Plugin Manager Micro also has a built in plugin manager, which you can invoke with the `> plugin ...` command, or in the shell with `micro -plugin ...`. For the valid commands you can use, see the `commands` help topic. The manager fetches plugins from the channels (which is simply a list of plugin metadata) which it knows about. By default, micro only knows about the official channel which is located at github.com/micro-editor/plugin-channel but you can add your own third-party channels using the `pluginchannels` option and you can directly link third-party plugins to allow installation through the plugin manager with the `pluginrepos` option. If you'd like to publish a plugin you've made as an official plugin, you should upload your plugin online (preferably to Github) and add a `repo.json` file. This file will contain the metadata for your plugin. Here is an example: ```json [{ "Name": "pluginname", "Description": "Here is a nice concise description of my plugin", "Website": "https://github.com/user/plugin", "Tags": ["python", "linting"], "Versions": [ { "Version": "1.0.0", "Url": "https://github.com/user/plugin/archive/v1.0.0.zip", "Require": { "micro": ">=1.0.3" } } ] }] ``` Then open a pull request at github.com/micro-editor/plugin-channel, adding a link to the raw `repo.json` that is in your plugin repository. To make updating the plugin work, the first line of your plugin's lua code should contain the version of the plugin. (Like this: `VERSION = "1.0.0"`) Please make sure to use [semver](http://semver.org/) for versioning. micro-2.0.14/runtime/help/tutorial.md0000664000175000017510000000721514663411671017056 0ustar nileshnilesh# Tutorial This is a brief intro to micro's configuration system that will give some simple examples showing how to configure settings, rebind keys, and use `init.lua` to configure micro to your liking. Hopefully you'll find this useful. See `> help defaultkeys` for a list an explanation of the default keybindings. ### Settings In micro, your settings are stored in `~/.config/micro/settings.json`, a file that is created the first time you run micro. It is a json file which holds all the settings and their values. To change an option, you can either change the value in the `settings.json` file, or you can type it in directly while using micro. Press Ctrl-e to go to command mode, and type `set option value` (in the future, I will use `> set option value` to indicate pressing Ctrl-e). The change will take effect immediately and will also be saved to the `settings.json` file so that the setting will stick even after you close micro. You can also set options locally which means that the setting will only have the value you give it in the buffer you set it in. For example, if you have two splits open, and you type `> setlocal tabsize 2`, the tabsize will only be 2 in the current buffer. Also micro will not save this local change to the `settings.json` file. However, you can still set options locally in the `settings.json` file. For example, if you want the `tabsize` to be 2 only in Ruby files, and 4 otherwise, you could put the following in `settings.json`: ```json { "*.rb": { "tabsize": 2 }, "tabsize": 4 } ``` Micro will set the `tabsize` to 2 only in files which match the glob `*.rb`. If you would like to know more about all the available options, see the `options` topic (`> help options`). ### Keybindings Keybindings work in much the same way as options. You configure them using the `~/.config/micro/bindings.json` file. For example if you would like to bind `Ctrl-r` to redo you could put the following in `bindings.json`: ```json { "Ctrl-r": "Redo" } ``` Very simple. You can also bind keys while in micro by using the `> bind key action` command, but the bindings you make with the command won't be saved to the `bindings.json` file. For more information about keybindings, like which keys can be bound, and what actions are available, see the `keybindings` help topic (`> help keybindings`). ### Configuration with Lua If you need more power than the json files provide, you can use the `init.lua` file. Create it in `~/.config/micro`. This file is a lua file that is run when micro starts and is essentially a one-file plugin. The plugin name is `initlua`. This example will show you how to use the `init.lua` file by creating a binding to `Ctrl-r` which will execute the bash command `go run` on the current file, given that the current file is a Go file. You can do that by putting the following in `init.lua`: ```lua local config = import("micro/config") local shell = import("micro/shell") function init() -- true means overwrite any existing binding to Ctrl-r -- this will modify the bindings.json file config.TryBindKey("Ctrl-r", "lua:initlua.gorun", true) end function gorun(bp) local buf = bp.Buf if buf:FileType() == "go" then -- the true means run in the foreground -- the false means send output to stdout (instead of returning it) shell.RunInteractiveShell("go run " .. buf.Path, true, false) end end ``` Alternatively, you could get rid of the `TryBindKey` line, and put this line in the `bindings.json` file: ```json { "Ctrl-r": "lua:initlua.gorun" } ``` For more information about plugins and the lua system that micro uses, see the `plugins` help topic (`> help plugins`). micro-2.0.14/runtime/plugins/0000775000175000017510000000000014663411671015415 5ustar nileshnileshmicro-2.0.14/runtime/plugins/autoclose/0000775000175000017510000000000014663411671017413 5ustar nileshnileshmicro-2.0.14/runtime/plugins/autoclose/autoclose.lua0000664000175000017510000000470314663411671022120 0ustar nileshnileshVERSION = "1.0.0" local uutil = import("micro/util") local utf8 = import("utf8") local autoclosePairs = {"\"\"", "''", "``", "()", "{}", "[]"} local autoNewlinePairs = {"()", "{}", "[]"} function charAt(str, i) -- lua indexing is one off from go return uutil.RuneAt(str, i-1) end function onRune(bp, r) for i = 1, #autoclosePairs do if r == charAt(autoclosePairs[i], 2) then local curLine = bp.Buf:Line(bp.Cursor.Y) if charAt(curLine, bp.Cursor.X+1) == charAt(autoclosePairs[i], 2) then bp:Backspace() bp:CursorRight() break end if bp.Cursor.X > 1 and (uutil.IsWordChar(charAt(curLine, bp.Cursor.X-1)) or charAt(curLine, bp.Cursor.X-1) == charAt(autoclosePairs[i], 1)) then break end end if r == charAt(autoclosePairs[i], 1) then local curLine = bp.Buf:Line(bp.Cursor.Y) if bp.Cursor.X == uutil.CharacterCountInString(curLine) or not uutil.IsWordChar(charAt(curLine, bp.Cursor.X+1)) then -- the '-' here is to derefence the pointer to bp.Cursor.Loc which is automatically made -- when converting go structs to lua -- It needs to be dereferenced because the function expects a non pointer struct bp.Buf:Insert(-bp.Cursor.Loc, charAt(autoclosePairs[i], 2)) bp:CursorLeft() break end end end return true end function preInsertNewline(bp) local curLine = bp.Buf:Line(bp.Cursor.Y) local curRune = charAt(curLine, bp.Cursor.X) local nextRune = charAt(curLine, bp.Cursor.X+1) local ws = uutil.GetLeadingWhitespace(curLine) for i = 1, #autoNewlinePairs do if curRune == charAt(autoNewlinePairs[i], 1) then if nextRune == charAt(autoNewlinePairs[i], 2) then bp.Buf:Insert(-bp.Cursor.Loc, "\n" .. ws) bp:StartOfLine() bp:CursorLeft() bp:InsertNewline() bp:InsertTab() return false end end end return true end function preBackspace(bp) for i = 1, #autoclosePairs do local curLine = bp.Buf:Line(bp.Cursor.Y) if charAt(curLine, bp.Cursor.X+1) == charAt(autoclosePairs[i], 2) and charAt(curLine, bp.Cursor.X) == charAt(autoclosePairs[i], 1) then bp:Delete() end end return true end micro-2.0.14/runtime/plugins/comment/0000775000175000017510000000000014663411671017057 5ustar nileshnileshmicro-2.0.14/runtime/plugins/comment/comment.lua0000664000175000017510000001472614663411671021236 0ustar nileshnileshVERSION = "1.0.0" local util = import("micro/util") local config = import("micro/config") local buffer = import("micro/buffer") local ft = {} ft["apacheconf"] = "# %s" ft["batch"] = ":: %s" ft["c"] = "// %s" ft["c++"] = "// %s" ft["cmake"] = "# %s" ft["conf"] = "# %s" ft["crystal"] = "# %s" ft["css"] = "/* %s */" ft["d"] = "// %s" ft["dart"] = "// %s" ft["dockerfile"] = "# %s" ft["elm"] = "-- %s" ft["fish"] = "# %s" ft["gdscript"] = "# %s" ft["glsl"] = "// %s" ft["go"] = "// %s" ft["haskell"] = "-- %s" ft["html"] = "" ft["ini"] = "; %s" ft["java"] = "// %s" ft["javascript"] = "// %s" ft["jinja2"] = "{# %s #}" ft["json"] = "// %s" ft["julia"] = "# %s" ft["kotlin"] = "// %s" ft["lua"] = "-- %s" ft["markdown"] = "" ft["nginx"] = "# %s" ft["nim"] = "# %s" ft["objc"] = "// %s" ft["ocaml"] = "(* %s *)" ft["pascal"] = "{ %s }" ft["perl"] = "# %s" ft["php"] = "// %s" ft["pony"] = "// %s" ft["powershell"] = "# %s" ft["proto"] = "// %s" ft["python"] = "# %s" ft["python3"] = "# %s" ft["ruby"] = "# %s" ft["rust"] = "// %s" ft["scala"] = "// %s" ft["shell"] = "# %s" ft["sql"] = "-- %s" ft["swift"] = "// %s" ft["tex"] = "% %s" ft["toml"] = "# %s" ft["twig"] = "{# %s #}" ft["v"] = "// %s" ft["xml"] = "" ft["yaml"] = "# %s" ft["zig"] = "// %s" ft["zscript"] = "// %s" ft["zsh"] = "# %s" local last_ft function updateCommentType(buf) if buf.Settings["commenttype"] == nil or (last_ft ~= buf.Settings["filetype"] and last_ft ~= nil) then if ft[buf.Settings["filetype"]] ~= nil then buf:SetOptionNative("commenttype", ft[buf.Settings["filetype"]]) else buf:SetOptionNative("commenttype", "# %s") end last_ft = buf.Settings["filetype"] end end function isCommented(bp, lineN, commentRegex) local line = bp.Buf:Line(lineN) local regex = commentRegex:gsub("%s+", "%s*") if string.match(line, regex) then return true end return false end function commentLine(bp, lineN, indentLen) updateCommentType(bp.Buf) local line = bp.Buf:Line(lineN) local commentType = bp.Buf.Settings["commenttype"] local sel = -bp.Cursor.CurSelection local curpos = -bp.Cursor.Loc local index = string.find(commentType, "%%s") - 1 local indent = string.sub(line, 1, indentLen) local trimmedLine = string.sub(line, indentLen + 1) trimmedLine = trimmedLine:gsub("%%", "%%%%") local commentedLine = commentType:gsub("%%s", trimmedLine) bp.Buf:Replace(buffer.Loc(0, lineN), buffer.Loc(#line, lineN), indent .. commentedLine) if bp.Cursor:HasSelection() then bp.Cursor.CurSelection[1].Y = sel[1].Y bp.Cursor.CurSelection[2].Y = sel[2].Y bp.Cursor.CurSelection[1].X = sel[1].X bp.Cursor.CurSelection[2].X = sel[2].X else bp.Cursor.X = curpos.X + index bp.Cursor.Y = curpos.Y end bp.Cursor:Relocate() bp.Cursor.LastVisualX = bp.Cursor:GetVisualX() end function uncommentLine(bp, lineN, commentRegex) updateCommentType(bp.Buf) local line = bp.Buf:Line(lineN) local commentType = bp.Buf.Settings["commenttype"] local sel = -bp.Cursor.CurSelection local curpos = -bp.Cursor.Loc local index = string.find(commentType, "%%s") - 1 if not string.match(line, commentRegex) then commentRegex = commentRegex:gsub("%s+", "%s*") end if string.match(line, commentRegex) then uncommentedLine = string.match(line, commentRegex) bp.Buf:Replace(buffer.Loc(0, lineN), buffer.Loc(#line, lineN), util.GetLeadingWhitespace(line) .. uncommentedLine) if bp.Cursor:HasSelection() then bp.Cursor.CurSelection[1].Y = sel[1].Y bp.Cursor.CurSelection[2].Y = sel[2].Y bp.Cursor.CurSelection[1].X = sel[1].X bp.Cursor.CurSelection[2].X = sel[2].X else bp.Cursor.X = curpos.X - index bp.Cursor.Y = curpos.Y end end bp.Cursor:Relocate() bp.Cursor.LastVisualX = bp.Cursor:GetVisualX() end function toggleCommentLine(bp, lineN, commentRegex) if isCommented(bp, lineN, commentRegex) then uncommentLine(bp, lineN, commentRegex) else commentLine(bp, lineN, #util.GetLeadingWhitespace(bp.Buf:Line(lineN))) end end function toggleCommentSelection(bp, startLine, endLine, commentRegex) local allComments = true for line = startLine, endLine do if not isCommented(bp, line, commentRegex) then allComments = false break end end -- NOTE: we assume that the indentation is either tabs only or spaces only local indentMin = -1 if not allComments then for line = startLine, endLine do local indentLen = #util.GetLeadingWhitespace(bp.Buf:Line(line)) if indentMin == -1 or indentLen < indentMin then indentMin = indentLen end end end for line = startLine, endLine do if allComments then uncommentLine(bp, line, commentRegex) else commentLine(bp, line, indentMin) end end end function comment(bp, args) updateCommentType(bp.Buf) local commentType = bp.Buf.Settings["commenttype"] local commentRegex = "^%s*" .. commentType:gsub("%%","%%%%"):gsub("%$","%$"):gsub("%)","%)"):gsub("%(","%("):gsub("%?","%?"):gsub("%*", "%*"):gsub("%-", "%-"):gsub("%.", "%."):gsub("%+", "%+"):gsub("%]", "%]"):gsub("%[", "%["):gsub("%%%%s", "(.*)") if bp.Cursor:HasSelection() then if bp.Cursor.CurSelection[1]:GreaterThan(-bp.Cursor.CurSelection[2]) then local endLine = bp.Cursor.CurSelection[1].Y if bp.Cursor.CurSelection[1].X == 0 then endLine = endLine - 1 end toggleCommentSelection(bp, bp.Cursor.CurSelection[2].Y, endLine, commentRegex) else local endLine = bp.Cursor.CurSelection[2].Y if bp.Cursor.CurSelection[2].X == 0 then endLine = endLine - 1 end toggleCommentSelection(bp, bp.Cursor.CurSelection[1].Y, endLine, commentRegex) end else toggleCommentLine(bp, bp.Cursor.Y, commentRegex) end end function string.starts(String,Start) return string.sub(String,1,string.len(Start))==Start end function init() config.MakeCommand("comment", comment, config.NoComplete) config.TryBindKey("Alt-/", "lua:comment.comment", false) config.TryBindKey("CtrlUnderscore", "lua:comment.comment", false) config.AddRuntimeFile("comment", config.RTHelp, "help/comment.md") end micro-2.0.14/runtime/plugins/comment/help/0000775000175000017510000000000014663411671020007 5ustar nileshnileshmicro-2.0.14/runtime/plugins/comment/help/comment.md0000664000175000017510000000326114663411671021775 0ustar nileshnilesh# Comment Plugin The comment plugin provides auto commenting/uncommenting. The default binding to comment/uncomment a line is `Alt-/` and `CtrlUnderscore`, which is equivalent in most terminals to `Ctrl-/`. You can easily modify that in your `bindings.json` file: ```json { "Alt-g": "lua:comment.comment" } ``` You can also execute a command which will do the same thing as the binding: ``` > comment ``` If you have a selection, the plugin will comment all the lines selected. The comment type will be auto detected based on the filetype, but it is only available for certain filetypes: * apacheconf: `# %s` * bat: `:: %s` * c: `// %s` * c++: `// %s` * cmake: `# %s` * conf: `# %s` * crystal: `# %s` * css: `/* %s */` * d: `// %s` * dart: `// %s` * dockerfile: `# %s` * elm: `-- %s` * fish: `# %s` * gdscript: `# %s` * glsl: `// %s` * go: `// %s` * haskell: `-- %s` * html: `` * ini: `; %s` * java: `// %s` * javascript: `// %s` * jinja2: `{# %s #}` * json: `// %s` * julia: `# %s` * kotlin: `// %s` * lua: `-- %s` * markdown: `` * nginx: `# %s` * nim: `# %s` * objc: `// %s` * pascal: `{ %s }` * perl: `# %s` * php: `// %s` * pony: `// %s` * powershell: `# %s` * proto: `// %s` * python: `# %s` * python3: `# %s` * ruby: `# %s` * rust: `// %s` * scala: `// %s` * shell: `# %s` * sql: `-- %s` * swift: `// %s` * tex: `% %s` * toml: `# %s` * twig: `{# %s #}` * v: `// %s` * xml: `` * yaml: `# %s` * zig: `// %s` * zscript: `// %s` * zsh: `# %s` If your filetype is not available here, you can simply modify the `commenttype` option: ``` set commenttype "/* %s */" ``` Or in your `settings.json`: ```json { "*.c": { "commenttype": "/* %s */" } } ``` micro-2.0.14/runtime/plugins/diff/0000775000175000017510000000000014663411671016325 5ustar nileshnileshmicro-2.0.14/runtime/plugins/diff/diff.lua0000664000175000017510000000107414663411671017742 0ustar nileshnileshVERSION = "1.0.0" local os = import("os") local filepath = import("path/filepath") local shell = import("micro/shell") function onBufferOpen(buf) if buf.Settings["diffgutter"] and (not buf.Type.Scratch) and (buf.Path ~= "") then -- check that file exists local _, err = os.Stat(buf.AbsPath) if err == nil then local dirName, fileName = filepath.Split(buf.AbsPath) local diffBase, err = shell.ExecCommand("git", "-C", dirName, "show", "HEAD:./" .. fileName) if err ~= nil then diffBase = buf:Bytes() end buf:SetDiffBase(diffBase) end end end micro-2.0.14/runtime/plugins/ftoptions/0000775000175000017510000000000014663411671017442 5ustar nileshnileshmicro-2.0.14/runtime/plugins/ftoptions/ftoptions.lua0000664000175000017510000000060414663411671022172 0ustar nileshnileshVERSION = "1.0.0" function onBufferOpen(b) local ft = b:FileType() if ft == "go" or ft == "makefile" then b:SetOption("tabstospaces", "off") elseif ft == "fish" or ft == "python" or ft == "python2" or ft == "python3" or ft == "yaml" or ft == "nim" then b:SetOption("tabstospaces", "on") end end micro-2.0.14/runtime/plugins/linter/0000775000175000017510000000000014663411671016712 5ustar nileshnileshmicro-2.0.14/runtime/plugins/linter/help/0000775000175000017510000000000014663411671017642 5ustar nileshnileshmicro-2.0.14/runtime/plugins/linter/help/linter.md0000664000175000017510000000625614663411671021472 0ustar nileshnilesh# Linter The linter plugin runs a compiler or linter on your source code and parses the resulting output so that the messages and line numbers can be viewed from within micro. By default, the plugin supports the following filetypes and linters: * **c**: gcc * **c++**: g++ * **d**: dmd * **go**: go build * **haskell**: hlint * **java**: javac * **javascript**: jshint * **javascript**: eslint * **literate**: lit * **lua**: luacheck * **nim**: nim * **objective-c**: clang * **python**: pyflakes * **python**: mypy * **python**: pylint * **shell**: shfmt * **swift**: swiftc (MacOS and Linux only) * **yaml**: yamllint If the linter plugin is enabled and the file corresponds to one of these filetypes, each time the buffer is saved, or when the `> lint` command is executed, micro will run the corresponding utility in the background and display the messages when it completes. The linter plugin also allows users to extend the supported filetypes. From inside another micro plugin, the function `linter.makeLinter` can be called to register a new filetype. Here is the spec for the `makeLinter` function: `linter.makeLinter(name, filetype, cmd, args, errorformat, os, whitelist, domatch, loffset, coffset, callback)` * **name**: name of the linter * **filetype**: filetype to check for to use linter * **cmd**: main linter process that is executed * **args**: arguments to pass to the linter process * use %f to refer to the current file name * use %d to refer to the current directory name * **errorformat**: how to parse the linter/compiler process output %f: file, %l: line number, %m: error/warning message * **os**: list of OSs this linter is supported or unsupported on optional param, default: {} * **whitelist**: should the OS list be a blacklist (do not run the linter for these OSs) or a whitelist (only run the linter for these OSs) optional param, default: false (should blacklist) * **domatch**: should the filetype be interpreted as a lua pattern to match with the actual filetype, or should the linter only activate on an exact match optional param, default: false (require exact match) * **loffset**: line offset will be added to the line number returned by the linter useful if the linter returns 0-indexed lines optional param, default: 0 * **coffset**: column offset will be added to the col number returned by the linter useful if the linter returns 0-indexed columns optional param, default: 0 * **callback**: function to call before executing the linter, if it returns false the lint is canceled. The callback is passed the buf. optional param, default: nil Below is an example for including a linter for any filetype using the `misspell` linter which checks for misspelled words in a file. ```lua function init() -- uses the default linter plugin -- matches any filetype linter.makeLinter("misspell", "", "misspell", {"%f"}, "%f:%l:%c: %m", {}, false, true) end ``` Here is an example for a more typical use-case, where the linter will only match one filetype (C in this case): ```lua function init() linter.makeLinter("gcc", "c", "gcc", {"-fsyntax-only", "-Wall", "-Wextra", "%f"}, "%f:%l:%c:.+: %m") end ``` micro-2.0.14/runtime/plugins/linter/linter.lua0000664000175000017510000001762714663411671020727 0ustar nileshnileshVERSION = "1.0.0" local micro = import("micro") local runtime = import("runtime") local filepath = import("path/filepath") local shell = import("micro/shell") local buffer = import("micro/buffer") local config = import("micro/config") local util = import("micro/util") local os = import("os") local linters = {} -- creates a linter entry, call from within an initialization function, not -- directly at initial load time -- -- name: name of the linter -- filetype: filetype to check for to use linter -- cmd: main linter process that is executed -- args: arguments to pass to the linter process -- use %f to refer to the current file name -- use %d to refer to the current directory name -- errorformat: how to parse the linter/compiler process output -- %f: file, %l: line number, %m: error/warning message -- os: list of OSs this linter is supported or unsupported on -- optional param, default: {} -- whitelist: should the OS list be a blacklist (do not run the linter for these OSs) -- or a whitelist (only run the linter for these OSs) -- optional param, default: false (should blacklist) -- domatch: should the filetype be interpreted as a lua pattern to match with -- the actual filetype, or should the linter only activate on an exact match -- optional param, default: false (require exact match) -- loffset: line offset will be added to the line number returned by the linter -- useful if the linter returns 0-indexed lines -- optional param, default: 0 -- coffset: column offset will be added to the col number returned by the linter -- useful if the linter returns 0-indexed columns -- optional param, default: 0 -- callback: function to call before executing the linter, if it returns -- false the lint is canceled. The callback is passed the buf. -- optional param, default: nil function makeLinter(name, filetype, cmd, args, errorformat, os, whitelist, domatch, loffset, coffset, callback) if linters[name] == nil then linters[name] = {} linters[name].filetype = filetype linters[name].cmd = cmd linters[name].args = args linters[name].errorformat = errorformat linters[name].os = os or {} linters[name].whitelist = whitelist or false linters[name].domatch = domatch or false linters[name].loffset = loffset or 0 linters[name].coffset = coffset or 0 linters[name].callback = callback or nil end end function removeLinter(name) linters[name] = nil end function preinit() local devnull = "/dev/null" if runtime.GOOS == "windows" then devnull = "NUL" end makeLinter("gcc", "c", "gcc", {"-fsyntax-only", "-Wall", "-Wextra", "%f"}, "%f:%l:%c:.+: %m") makeLinter("g++", "c++", "gcc", {"-fsyntax-only","-std=c++14", "-Wall", "-Wextra", "%f"}, "%f:%l:%c:.+: %m") makeLinter("dmd", "d", "dmd", {"-color=off", "-o-", "-w", "-wi", "-c", "%f"}, "%f%(%l%):.+: %m") makeLinter("eslint", "javascript", "eslint", {"-f","compact","%f"}, "%f: line %l, col %c, %m") makeLinter("gobuild", "go", "go", {"build", "-o", devnull, "%d"}, "%f:%l:%c:? %m") makeLinter("govet", "go", "go", {"vet"}, "%f:%l:%c: %m") makeLinter("clippy", "rust", "cargo", {"clippy", "--message-format", "short"}, "%f:%l:%c: %m") makeLinter("hlint", "haskell", "hlint", {"%f"}, "%f:%(?%l[,:]%c%)?.-: %m") makeLinter("javac", "java", "javac", {"-d", "%d", "%f"}, "%f:%l: error: %m") makeLinter("jshint", "javascript", "jshint", {"%f"}, "%f: line %l,.+, %m") makeLinter("literate", "literate", "lit", {"-c", "%f"}, "%f:%l:%m", {}, false, true) makeLinter("luacheck", "lua", "luacheck", {"--no-color", "%f"}, "%f:%l:%c: %m") makeLinter("nim", "nim", "nim", {"check", "--listFullPaths", "--stdout", "--hints:off", "%f"}, "%f.%l, %c. %m") makeLinter("clang", "objective-c", "xcrun", {"clang", "-fsyntax-only", "-Wall", "-Wextra", "%f"}, "%f:%l:%c:.+: %m") makeLinter("pyflakes", "python", "pyflakes", {"%f"}, "%f:%l:.-:? %m") makeLinter("mypy", "python", "mypy", {"%f"}, "%f:%l: %m") makeLinter("pylint", "python", "pylint", {"--output-format=parseable", "--reports=no", "%f"}, "%f:%l: %m") makeLinter("flake8", "python", "flake8", {"%f"}, "%f:%l:%c: %m") makeLinter("shfmt", "shell", "shfmt", {"%f"}, "%f:%l:%c: %m") makeLinter("shellcheck", "shell", "shellcheck", {"-f", "gcc", "%f"}, "%f:%l:%c:.+: %m") makeLinter("swiftc", "swift", "xcrun", {"swiftc", "%f"}, "%f:%l:%c:.+: %m", {"darwin"}, true) makeLinter("swiftc-linux", "swift", "swiftc", {"%f"}, "%f:%l:%c:.+: %m", {"linux"}, true) makeLinter("yaml", "yaml", "yamllint", {"--format", "parsable", "%f"}, "%f:%l:%c:.+ %m") makeLinter("nix-linter", "nix", "nix-linter", {"%f"}, "%m at %f:%l:%c", {"linux"}, true) config.MakeCommand("lint", function(bp, args) bp:Save() runLinter(bp.Buf) end, config.NoComplete) config.AddRuntimeFile("linter", config.RTHelp, "help/linter.md") end function contains(list, element) for k, v in pairs(list) do if v == element then return true end end return false end function runLinter(buf) local ft = buf:FileType() local file = buf.Path local dir = "." .. util.RuneStr(os.PathSeparator) .. filepath.Dir(file) for k, v in pairs(linters) do local ftmatch = ft == v.filetype if v.domatch then ftmatch = string.match(ft, v.filetype) end local hasOS = contains(v.os, runtime.GOOS) if not hasOS and v.whitelist then ftmatch = false end if hasOS and not v.whitelist then ftmatch = false end if ftmatch then local args = {} for k, arg in pairs(v.args) do args[k] = arg:gsub("%%f", file):gsub("%%d", dir) end lint(buf, k, v.cmd, args, v.errorformat, v.loffset, v.coffset, v.callback) end end end function onSave(bp) runLinter(bp.Buf) return true end function lint(buf, linter, cmd, args, errorformat, loff, coff, callback) buf:ClearMessages(linter) if callback ~= nil then if not callback(buf) then return end end shell.JobSpawn(cmd, args, nil, nil, onExit, buf, linter, errorformat, loff, coff) end function onExit(output, args) local buf, linter, errorformat, loff, coff = args[1], args[2], args[3], args[4], args[5] local lines = split(output, "\n") local regex = errorformat:gsub("%%f", "(..-)"):gsub("%%l", "(%d+)"):gsub("%%c", "(%d+)"):gsub("%%m", "(.+)") for _,line in ipairs(lines) do -- Trim whitespace line = line:match("^%s*(.+)%s*$") if string.find(line, regex) then local file, line, col, msg = string.match(line, regex) local hascol = true if not string.find(errorformat, "%%c") then hascol = false msg = col elseif col == nil then hascol = false end if basename(buf.Path) == basename(file) then local bmsg = nil if hascol then local mstart = buffer.Loc(tonumber(col-1+coff), tonumber(line-1+loff)) local mend = buffer.Loc(tonumber(col+coff), tonumber(line-1+loff)) bmsg = buffer.NewMessage(linter, msg, mstart, mend, buffer.MTError) else bmsg = buffer.NewMessageAtLine(linter, msg, tonumber(line+loff), buffer.MTError) end buf:AddMessage(bmsg) end end end end function split(str, sep) local result = {} local regex = ("([^%s]+)"):format(sep) for each in str:gmatch(regex) do table.insert(result, each) end return result end function basename(file) local sep = "/" if runtime.GOOS == "windows" then sep = "\\" end local name = string.gsub(file, "(.*" .. sep .. ")(.*)", "%2") return name end micro-2.0.14/runtime/plugins/literate/0000775000175000017510000000000014663411671017226 5ustar nileshnileshmicro-2.0.14/runtime/plugins/literate/README.md0000664000175000017510000000034214663411671020504 0ustar nileshnilesh# Literate Micro Support for reading `.lit` files from [zyedidia/Literate](https://github.com/zyedidia/Literate). This plugin will automatically detect the filetype and highlight the correct language inside the code blocks. micro-2.0.14/runtime/plugins/literate/literate.lua0000664000175000017510000000403714663411671021546 0ustar nileshnileshVERSION = "1.0.0" local config = import("micro/config") function startswith(str, start) return string.sub(str,1,string.len(start))==start end function endswith(str, endStr) return endStr=='' or string.sub(str,-string.len(endStr))==endStr end function split(string, sep) local sep, fields = sep or ":", {} local pattern = string.format("([^%s]+)", sep) string:gsub(pattern, function(c) fields[#fields+1] = c end) return fields end function onBufferOpen(buf) if not endswith(buf.Path, ".lit") then return end local codetype = "unknown" for i=1,buf:LinesNum() do local line = buf:Line(i-1) if startswith(line, "@code_type") then codetype = split(line, " ")[2] break end end local syntaxFile = "" syntaxFile = syntaxFile .. "filetype: literate-" .. codetype .. "\n" syntaxFile = syntaxFile .. "detect:\n" syntaxFile = syntaxFile .. " filename: \"\\\\.lit$\"\n" syntaxFile = syntaxFile .. "rules:\n" syntaxFile = syntaxFile .. " - include: \"markdown\"\n" syntaxFile = syntaxFile .. " - special: \"^(@s|@title|@code_type|@comment_type|@include|@change|@change_end)\"\n" syntaxFile = syntaxFile .. " - special: \"(@add_css|@overwrite_css|@colorscheme|@compiler|@error_format|@book)\"\n" syntaxFile = syntaxFile .. " - default:\n" syntaxFile = syntaxFile .. " start: \"---.*$\"\n" syntaxFile = syntaxFile .. " end: \"---$\"\n" syntaxFile = syntaxFile .. " limit-group: \"identifier\"\n" syntaxFile = syntaxFile .. " rules:\n" syntaxFile = syntaxFile .. " - special:\n" syntaxFile = syntaxFile .. " start: \"@\\\\{\"\n" syntaxFile = syntaxFile .. " end: \"\\\\}\"\n" syntaxFile = syntaxFile .. " rules: []\n" syntaxFile = syntaxFile .. " - include: " .. codetype .. "\n" config.AddRuntimeFileFromMemory(config.RTSyntax, "literate.yaml", syntaxFile) config.Reload() buf:UpdateRules() end micro-2.0.14/runtime/plugins/status/0000775000175000017510000000000014663411671016740 5ustar nileshnileshmicro-2.0.14/runtime/plugins/status/help/0000775000175000017510000000000014663411671017670 5ustar nileshnileshmicro-2.0.14/runtime/plugins/status/help/status.md0000664000175000017510000000153314663411671021537 0ustar nileshnilesh# Status The status plugin provides some functions for modifying the status line. Using the `statusformatl` and `statusformatr` options, the exact contents of the status line can be modified. Please see the documentation for those options (`> help options`) for more information. This plugin provides functions that can be used in the status line format: * `status.branch`: returns the name of the current git branch. * `status.hash`: returns the hash of the current git commit. * `status.paste`: returns "" if the paste option is disabled and "PASTE" if it is enabled. * `status.lines`: returns the number of lines in the buffer. * `status.vcol`: returns the visual column number of the cursor. * `status.bytes`: returns the number of bytes in the current buffer. * `status.size`: returns the size of the current buffer in a human-readable format. micro-2.0.14/runtime/plugins/status/status.lua0000664000175000017510000000305514663411671020771 0ustar nileshnileshVERSION = "1.0.0" local micro = import("micro") local buffer = import("micro/buffer") local config = import("micro/config") local humanize = import("humanize") function init() micro.SetStatusInfoFn("status.branch") micro.SetStatusInfoFn("status.hash") micro.SetStatusInfoFn("status.paste") micro.SetStatusInfoFn("status.vcol") micro.SetStatusInfoFn("status.lines") micro.SetStatusInfoFn("status.bytes") micro.SetStatusInfoFn("status.size") config.AddRuntimeFile("status", config.RTHelp, "help/status.md") end function lines(b) return tostring(b:LinesNum()) end function vcol(b) return tostring(b:GetActiveCursor():GetVisualX()) end function bytes(b) return tostring(b:Size()) end function size(b) return humanize.Bytes(b:Size()) end function branch(b) if b.Type.Kind ~= buffer.BTInfo then local shell = import("micro/shell") local strings = import("strings") local branch, err = shell.ExecCommand("git", "rev-parse", "--abbrev-ref", "HEAD") if err == nil then return strings.TrimSpace(branch) end return "" end end function hash(b) if b.Type.Kind ~= 5 then local shell = import("micro/shell") local strings = import("strings") local hash, err = shell.ExecCommand("git", "rev-parse", "--short", "HEAD") if err == nil then return strings.TrimSpace(hash) end return "" end end function paste(b) if config.GetGlobalOption("paste") then return "PASTE " end return "" end micro-2.0.14/runtime/runtime.go0000664000175000017510000000131714663411671015750 0ustar nileshnileshpackage config import ( "embed" "path/filepath" "strings" ) //go:generate go run syntax/make_headers.go syntax //go:embed colorschemes help plugins syntax var runtime embed.FS func fixPath(name string) string { return strings.TrimLeft(filepath.ToSlash(name), "runtime/") } // AssetDir lists file names in folder func AssetDir(name string) ([]string, error) { name = fixPath(name) entries, err := runtime.ReadDir(name) if err != nil { return nil, err } names := make([]string, len(entries)) for i, entry := range entries { names[i] = entry.Name() } return names, nil } // Asset returns a file content func Asset(name string) ([]byte, error) { name = fixPath(name) return runtime.ReadFile(name) } micro-2.0.14/runtime/runtime_test.go0000664000175000017510000000043014663411671017002 0ustar nileshnileshpackage config import ( "testing" "github.com/stretchr/testify/assert" ) func TestAssetDir(t *testing.T) { t.Parallel() // Test AssetDir entries, err := AssetDir("syntax") assert.NoError(t, err) assert.Contains(t, entries, "go.yaml") assert.True(t, len(entries) > 5) } micro-2.0.14/runtime/syntax/0000775000175000017510000000000014663411671015262 5ustar nileshnileshmicro-2.0.14/runtime/syntax/LICENSE0000664000175000017510000000215614663411671016273 0ustar nileshnileshMicro's syntax files are licensed under the MIT "Expat" License: Copyright (c) 2020: Zachary Yedidia, et al. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 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 AUTHORS 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 IN THE SOFTWARE. micro-2.0.14/runtime/syntax/PowerShell.yaml0000664000175000017510000001164414663411671020240 0ustar nileshnilesh# PowerShell syntax highlighting file for micro - https://micro-editor.github.io/ # PowerShell syntax taken from: https://github.com/PowerShell/EditorSyntax filetype: powershell detect: filename: "\\.ps(1|m1|d1)$" rules: # - comment.block: # Block Comment # - comment.doc: # Doc Comment # - comment.line: # Line Comment # - comment.shebang: # Shebang Line # - constant: # Constant # - constant.bool: # Constant (true, false) # - constant.interpolation: # - constant.number: # Constant (null) # - constant.specialChar: # - constant.string: # String # - constant.string.char: # - constant.string.url: # Uri # - constant.unicode: # - identifier: # Also used for functions # - identifier.class: # Also used for functions # - identifier.macro: # - identifier.var: # - preproc: # Preprocessor # - preproc.DebugIdentifier: # Preprocessor # - preproc.shebang: # The #! at the beginning of a file that tells the os what script interpreter to use # - special: # Special (global|local|private|script|using|workflow) # - statement: # Statements Keywords # - statement.built_in: # - statement.declaration: # Declaration Keywords # - statement.meta: # Meta # - statement.reserved: # Reserved Keywords # - symbol # - symbol.brackets: # {}()[] and sometimes <> # - symbol.operator: # Operators # - symbol.tag: # For html tags, among other things # - type # - type.collections: # Collections (array, hashtable) # - type.ctypes: # CTypes (CBool, CChar, etc.) # - type.keyword: # If you want a special highlight for keywords like 'private' # - type.storage: # Storage Types (int, uint, string, etc.) # Class - identifier.class: "class +[A-Za-z0-9]+ *((:) +[A-Za-z0-9.]+)?" - identifier.class: "(function)(?:([[:space:]][A-Za-z0-9]+[[:space:]]*))" # Verbs taken from PwSh 6.0.2 - identifier: "(Add|Approve|Assert|Backup|Block|Build|Checkpoint|Clear|Close|Compare|Complete|Compress|Confirm|Connect|Convert|ConvertFrom|ConvertTo|Copy)[-][A-Za-z0-9]+" - identifier: "(Debug|Deny|Deploy|Disable|Disconnect|Dismount|Edit|Enable|Enter|Exit|Expand|Export|Find|Format|Get|Grant|Group|Hide)[-][A-Za-z0-9]+" - identifier: "(Import|Initialize|Install|Invoke|Join|Limit|Lock|Measure|Merge|Mount|Move|New|Open|Optimize|Out|Ping|Pop|Protect|Publish|Push)[-][A-Za-z0-9]+" - identifier: "(Read|Receive|Redo|Register|Remove|Rename|Repair|Request|Reset|Resize|Resolve|Restart|Restore|Resume|Revoke)[-][A-Za-z0-9]+" - identifier: "(Save|Search|Select|Send|Set|Show|Skip|Split|Start|Step|Stop|Submit|Suspend|Switch|Sync|Test|Trace)[-][A-Za-z0-9]+" - identifier: "(Unblock|Undo|Uninstall|Unlock|Unprotect|Unpublish|Unregister|Update|Use|Wait|Watch|Write)[-][A-Za-z0-9]+" - identifier.var: "\\$(?i)((Global|Local|Private|Script|Using|Workflow)[:])?[A-Za-z0-9]*" # Expression and types - type: "\\[\\b([A-Za-z]+|[A-Za-z]+[0-9]+)\\b\\]" # Keywords - statement: "\\b(alias|as|begin|break|catch|continue|data|default|define|do|dynamicparam)\\b" - statement: "\\b(else|elseif|end|exit|finally|for|foreach|foreach-object|from|if|in|inlinescript)\\b" - statement: "\\b(parallel|param|process|return|switch|throw|trap|try|until|using|var|where|where-object|while)\\b" # Special Keywords - special: "\\b(break|continue|exit)\\b" - symbol.brackets: "(\\{|\\})" - symbol.brackets: "(\\(|\\))" - symbol.brackets: "(\\[|\\])" - symbol.operator: "[\\-+/*=<>?:!~%&|]" - symbol.operator: "[[:space:]][-](ne|eq|gt|ge|lt|le|like|notlike|match|notmatch|contains|notcontains|in|notin|replace|is|isnot)[[:space:]]" # Constants - constant.bool: "\\b\\$(true|false|null)\\b" - constant.number: "\\b([0-9._]+|0x[A-Fa-f0-9_]+|0b[0-1_]+)[FL]?\\b" # Expression Mode String - constant.string: start: "\"" end: "\"" #skip: "\\\\." rules: - constant.specialChar: "\\\\([btnfr]|'|\\\"|\\\\)" - constant.specialChar: "\\\\u[A-Fa-f0-9]{4}" # Argument Mode String - constant.string: start: "'" end: "'" #skip: "\\\\." rules: - constant.specialChar: "\\\\([btnfr]|'|\\\"|\\\\)" - constant.specialChar: "\\\\u[A-Fa-f0-9]{4}" # Line Comment - comment: start: "#" end: "$" rules: - todo: "(TODO|XXX|FIXME|BUG):?" # Block Comment - comment: start: "<#" end: "#>" rules: - todo: "(TODO|XXX|FIXME|BUG):?" # Embedded C# - default: start: "@\"" end: "\"@" rules: - include: "csharp" # Todo - todo: "(TODO|XXX|FIXME|BUG):?" micro-2.0.14/runtime/syntax/README.md0000664000175000017510000000472614663411671016552 0ustar nileshnilesh# Syntax Files Here are micro's syntax files. Each yaml file specifies how to detect the filetype based on file extension or header (first line of the line). In addition, a signature can be provided to help resolving ambiguities when multiple matching filetypes are detected. Then there are patterns and regions linked to highlight groups which tell micro how to highlight that filetype. Making your own syntax files is very simple. I recommend you check the file after you are finished with the [`syntax_checker.go`](./syntax_checker.go) program (located in this directory). Just place your yaml syntax file in the current directory and run `go run syntax_checker.go` and it will check every file. If there are no errors it will print `No issues!`. You can read more about how to write syntax files (and colorschemes) in the [colors](../help/colors.md) documentation. # Legacy '.micro' filetype Micro used to use the `.micro` filetype for syntax files which is no longer supported. If you have `.micro` syntax files that you would like to convert to the new filetype, you can use the [`syntax_converter.go`](./syntax_converter.go) program (also located in this directory): ``` $ go run syntax_converter.go c.micro > c.yaml ``` Most of the syntax files here have been converted using that tool. Note that the tool isn't perfect and though it is unlikely, you may run into some small issues that you will have to fix manually (about 4 files from this directory had issues after being converted). # Micro syntax highlighting files These are the syntax highlighting files for micro. To install them, just put all the syntax files in `~/.config/micro/syntax`. They are taken from Nano, specifically from [this repository](https://github.com/scopatz/nanorc). Micro syntax files are almost identical to Nano's, except for some key differences: * Micro does not use `icolor`. Instead, for a case insensitive match, use the case insensitive flag (`i`) in the regular expression * For example, `icolor green ".*"` would become `color green "(?i).*"` # Using with colorschemes Not all of these files have been converted to use micro's colorscheme feature. Most of them just hardcode the colors, which can be problematic depending on the colorscheme you use. Here is a list of the files that have been converted to properly use colorschemes: * vi * go * c * d * markdown * html * lua * swift * rust * java * javascript * pascal * python * ruby * sh * git * tex * solidity # License See [LICENSE](LICENSE). micro-2.0.14/runtime/syntax/ada.yaml0000664000175000017510000000276514663411671016705 0ustar nileshnileshfiletype: ada detect: filename: "(\\.ads$|\\.adb$|\\.ada$)" rules: # Operators - symbol.operator: ([.:;,+*|=!?\\%]|<|>|/|-|&) - symbol.brackets: "[(){}]|\\[|\\]" # keyword.reserved - statement.reserved: \b(abort|abs|abstract|accept|access|aliased|all|and|array|at|begin|body|case)\b - statement.reserved: \b(constant|declare|delay|delta|digits|do|else|elsif|end|entry|exception|exit|for|function)\b - statement.reserved: \b(generic|goto|if|in|interface|is|limited|loop|mod|new|not|null|of|or|others|out|overriding)\b - statement.reserved: \b(package|pragma|private|procedure|protected|raise|range|record|rem|renames|return|requeue)\b - statement.reserved: \b(reverse|select|separate|some|subtype|synchronized|tagged|task|terminate|then|type|until)\b - statement.reserved: \b(use|when|while|with|xor)\b # Constant - constant.bool: \b(TRUE|FALSE) - constant.number: ([0-9]+) # Storage Types - type.storage: \b(INTEGER|NATURAL|POSITIVE|FLOAT|CHARACTER|STRING)\b - type.storage: \b(LONG_INTEGER|SHORT_INTEGER|LONG_FLOAT|SHORT_FLOAT)\b #Character - constant.string.char: \'.\' # String - constant.string: start: \" end: \" skip: \\. rules: - constant.specialChar: (\\0|\\\\|\\t|\\n|\\r|\\"|\\') - constant.interpolation: \\\([[:graph:]]*\) - constant.unicode: \\u\{[[:xdigit:]]+} # Line Comment - comment.line: "--.*" # Todo - todo: "(TODO|XXX|FIXME):?" micro-2.0.14/runtime/syntax/apacheconf.yaml0000664000175000017510000001457714663411671020253 0ustar nileshnileshfiletype: apacheconf detect: filename: "httpd\\.conf|mime\\.types|vhosts\\.d\\\\*|\\.htaccess" rules: - identifier: "(AcceptMutex|AcceptPathInfo|AccessFileName|Action|AddAlt|AddAltByEncoding|AddAltByType|AddCharset|AddDefaultCharset|AddDescription|AddEncoding)" - identifier: "(AddHandler|AddIcon|AddIconByEncoding|AddIconByType|AddInputFilter|AddLanguage|AddModuleInfo|AddOutputFilter|AddOutputFilterByType|AddType|Alias|AliasMatch)" - identifier: "(Allow|AllowCONNECT|AllowEncodedSlashes|AllowOverride|Anonymous|Anonymous_Authoritative|Anonymous_LogEmail|Anonymous_MustGiveEmail|Anonymous_NoUserID)" - identifier: "(Anonymous_VerifyEmail|AssignUserID|AuthAuthoritative|AuthDBMAuthoritative|AuthDBMGroupFile|AuthDBMType|AuthDBMUserFile|AuthDigestAlgorithm)" - identifier: "(AuthDigestDomain|AuthDigestFile|AuthDigestGroupFile|AuthDigestNcCheck|AuthDigestNonceFormat|AuthDigestNonceLifetime|AuthDigestQop|AuthDigestShmemSize)" - identifier: "(AuthGroupFile|AuthLDAPAuthoritative|AuthLDAPBindDN|AuthLDAPBindPassword|AuthLDAPCharsetConfig|AuthLDAPCompareDNOnServer|AuthLDAPDereferenceAliases)" - identifier: "(AuthLDAPEnabled|AuthLDAPFrontPageHack|AuthLDAPGroupAttribute|AuthLDAPGroupAttributeIsDN|AuthLDAPRemoteUserIsDN|AuthLDAPUrl|AuthName|AuthType|AuthUserFile)" - identifier: "(BrowserMatch|BrowserMatchNoCase|BS2000Account|BufferedLogs|CacheDefaultExpire|CacheDirLength|CacheDirLevels|CacheDisable|CacheEnable|CacheExpiryCheck)" - identifier: "(CacheFile|CacheForceCompletion|CacheGcClean|CacheGcDaily|CacheGcInterval|CacheGcMemUsage|CacheGcUnused|CacheIgnoreCacheControl|CacheIgnoreHeaders)" - identifier: "(CacheIgnoreNoLastMod|CacheLastModifiedFactor|CacheMaxExpire|CacheMaxFileSize|CacheMinFileSize|CacheNegotiatedDocs|CacheRoot|CacheSize|CacheTimeMargin)" - identifier: "(CGIMapExtension|CharsetDefault|CharsetOptions|CharsetSourceEnc|CheckSpelling|ChildPerUserID|ContentDigest|CookieDomain|CookieExpires|CookieLog|CookieName)" - identifier: "(CookieStyle|CookieTracking|CoreDumpDirectory|CustomLog|Dav|DavDepthInfinity|DavLockDB|DavMinTimeout|DefaultIcon|DefaultLanguage|DefaultType)" - identifier: "(DeflateBufferSize|DeflateCompressionLevel|DeflateFilterNote|DeflateMemLevel|DeflateWindowSize|Deny|Directory|DirectoryIndex|DirectoryMatch|DirectorySlash)" - identifier: "(DocumentRoot|DumpIOInput|DumpIOOutput|EnableExceptionHook|EnableMMAP|EnableSendfile|ErrorDocument|ErrorLog|Example|ExpiresActive|ExpiresByType)" - identifier: "(ExpiresDefault|ExtendedStatus|ExtFilterDefine|ExtFilterOptions|FileETag|Files|FilesMatch|ForceLanguagePriority|ForceType|ForensicLog|Group|Header)" - identifier: "(HeaderName|HostnameLookups|IdentityCheck|IfDefine|IfModule|IfVersion|ImapBase|ImapDefault|ImapMenu|Include|IndexIgnore|IndexOptions|IndexOrderDefault)" - identifier: "(ISAPIAppendLogToErrors|ISAPIAppendLogToQuery|ISAPICacheFile|ISAPIFakeAsync|ISAPILogNotSupported|ISAPIReadAheadBuffer|KeepAlive|KeepAliveTimeout)" - identifier: "(LanguagePriority|LDAPCacheEntries|LDAPCacheTTL|LDAPConnectionTimeout|LDAPOpCacheEntries|LDAPOpCacheTTL|LDAPSharedCacheFile|LDAPSharedCacheSize)" - identifier: "(LDAPTrustedCA|LDAPTrustedCAType|Limit|LimitExcept|LimitInternalRecursion|LimitRequestBody|LimitRequestFields|LimitRequestFieldSize|LimitRequestLine)" - identifier: "(LimitXMLRequestBody|Listen|ListenBackLog|LoadFile|LoadModule|Location|LocationMatch|LockFile|LogFormat|LogLevel|MaxClients|MaxKeepAliveRequests)" - identifier: "(MaxMemFree|MaxRequestsPerChild|MaxRequestsPerThread|MaxSpareServers|MaxSpareThreads|MaxThreads|MaxThreadsPerChild|MCacheMaxObjectCount|MCacheMaxObjectSize)" - identifier: "(MCacheMaxStreamingBuffer|MCacheMinObjectSize|MCacheRemovalAlgorithm|MCacheSize|MetaDir|MetaFiles|MetaSuffix|MimeMagicFile|MinSpareServers|MinSpareThreads)" - identifier: "(MMapFile|ModMimeUsePathInfo|MultiviewsMatch|NameVirtualHost|NoProxy|NumServers|NWSSLTrustedCerts|NWSSLUpgradeable|Options|Order|PassEnv|PidFile)" - identifier: "(ProtocolEcho|Proxy|ProxyBadHeader|ProxyBlock|ProxyDomain|ProxyErrorOverride|ProxyIOBufferSize|ProxyMatch|ProxyMaxForwards|ProxyPass|ProxyPassReverse)" - identifier: "(ProxyPreserveHost|ProxyReceiveBufferSize|ProxyRemote|ProxyRemoteMatch|ProxyRequests|ProxyTimeout|ProxyVia|ReadmeName|Redirect|RedirectMatch)" - identifier: "(RedirectPermanent|RedirectTemp|RemoveCharset|RemoveEncoding|RemoveHandler|RemoveInputFilter|RemoveLanguage|RemoveOutputFilter|RemoveType|RequestHeader)" - identifier: "(Require|RewriteBase|RewriteCond|RewriteEngine|RewriteLock|RewriteLog|RewriteLogLevel|RewriteMap|RewriteOptions|RewriteRule|RLimitCPU|RLimitMEM|RLimitNPROC)" - identifier: "(Satisfy|ScoreBoardFile|Script|ScriptAlias|ScriptAliasMatch|ScriptInterpreterSource|ScriptLog|ScriptLogBuffer|ScriptLogLength|ScriptSock|SecureListen)" - identifier: "(SendBufferSize|ServerAdmin|ServerAlias|ServerLimit|ServerName|ServerPath|ServerRoot|ServerSignature|ServerTokens|SetEnv|SetEnvIf|SetEnvIfNoCase|SetHandler)" - identifier: "(SetInputFilter|SetOutputFilter|SSIEndTag|SSIErrorMsg|SSIStartTag|SSITimeFormat|SSIUndefinedEcho|SSLCACertificateFile|SSLCACertificatePath)" - identifier: "(SSLCARevocationFile|SSLCARevocationPath|SSLCertificateChainFile|SSLCertificateFile|SSLCertificateKeyFile|SSLCipherSuite|SSLEngine|SSLMutex|SSLOptions)" - identifier: "(SSLPassPhraseDialog|SSLProtocol|SSLProxyCACertificateFile|SSLProxyCACertificatePath|SSLProxyCARevocationFile|SSLProxyCARevocationPath|SSLProxyCipherSuite)" - identifier: "(SSLProxyEngine|SSLProxyMachineCertificateFile|SSLProxyMachineCertificatePath|SSLProxyProtocol|SSLProxyVerify|SSLProxyVerifyDepth|SSLRandomSeed|SSLRequire)" - identifier: "(SSLRequireSSL|SSLSessionCache|SSLSessionCacheTimeout|SSLUserName|SSLVerifyClient|SSLVerifyDepth|StartServers|StartThreads|SuexecUserGroup|ThreadLimit)" - identifier: "(ThreadsPerChild|ThreadStackSize|TimeOut|TraceEnable|TransferLog|TypesConfig|UnsetEnv|UseCanonicalName|User|UserDir|VirtualDocumentRoot)" - identifier: "(VirtualDocumentRootIP|VirtualHost|VirtualScriptAlias|VirtualScriptAliasIP|Win32DisableAcceptEx|XBitHack)" - symbol.tag: "<[^>]+>" - identifier: ")" - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\." - comment: start: "#" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" micro-2.0.14/runtime/syntax/arduino.yaml0000664000175000017510000000541714663411671017616 0ustar nileshnileshfiletype: ino detect: filename: "\\.?ino$" rules: - identifier: "\\b[A-Z_][0-9A-Z_]+\\b" ## Sized (u)int types - type: "\\b((s?size)|((u_?)?int(8|16|32|64|ptr)))_t\\b" ## Constants - constant: "(?i)\\b(HIGH|LOW|INPUT|OUTPUT)\\b" ## Serial Print - constant: "(?i)\\b(DEC|BIN|HEX|OCT|BYTE)\\b" ## PI Constants - constant: "(?i)\\b(PI|HALF_PI|TWO_PI)\\b" ## ShiftOut - constant: "(?i)\\b(LSBFIRST|MSBFIRST)\\b" ## Attach Interrupt - constant: "(?i)\\b(CHANGE|FALLING|RISING)\\b" ## Analog Reference - constant: "(?i)\\b(DEFAULT|EXTERNAL|INTERNAL|INTERNAL1V1|INTERNAL2V56)\\b" ## === FUNCTIONS === ## ## Data Types - type: "\\b(boolean|byte|char|float|int|long|word)\\b" ## Control Structions - statement: "\\b(case|class|default|do|double|else|false|for|if|new|null|private|protected|public|short|signed|static|String|switch|this|throw|try|true|unsigned|void|while)\\b" - statement: "\\b(goto|continue|break|return)\\b" ## Math - identifier: "\\b(abs|acos|asin|atan|atan2|ceil|constrain|cos|degrees|exp|floor|log|map|max|min|radians|random|randomSeed|round|sin|sq|sqrt|tan)\\b" ## Bits & Bytes - identifier: "\\b(bitRead|bitWrite|bitSet|bitClear|bit|highByte|lowByte)\\b" ## Analog I/O - identifier: "\\b(analogReference|analogRead|analogWrite)\\b" ## External Interrupts - identifier: "\\b(attachInterrupt|detachInterrupt)\\b" ## Time - identifier: "\\b(delay|delayMicroseconds|millis|micros)\\b" ## Digital I/O - identifier: "\\b(pinMode|digitalWrite|digitalRead)\\b" ## Interrupts - identifier: "\\b(interrupts|noInterrupts)\\b" ## Advanced I/O - identifier: "\\b(noTone|pulseIn|shiftIn|shiftOut|tone)\\b" ## Serial - identifier: "\\b(Serial|Serial1|Serial2|Serial3|begin|end|peek|read|print|println|available|flush)\\b" ## Structure - identifier: "\\b(setup|loop)\\b" ## - statement: "^[[:space:]]*#[[:space:]]*(define|include(_next)?|(un|ifn?)def|endif|el(if|se)|if|warning|error|pragma)" ## GCC builtins - constant: "(__attribute__[[:space:]]*\\(\\([^)]*\\)\\)|__(aligned|asm|builtin|hidden|inline|packed|restrict|section|typeof|weak)__)" - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\." - constant.string: start: "'" end: "'" skip: "\\\\." rules: - preproc: "..+" - constant.specialChar: "\\\\." - comment: start: "//" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" - comment: start: "/\\*" end: "\\*/" rules: - todo: "(TODO|XXX|FIXME):?" micro-2.0.14/runtime/syntax/asciidoc.yaml0000664000175000017510000000235214663411671017726 0ustar nileshnileshfiletype: asciidoc detect: filename: "\\.(asc|asciidoc|adoc)$" rules: # main header - preproc: "^====+$" # h1 - statement: "^==[[:space:]].*$" - statement: "^----+$" # h2 - symbol: "^===[[:space:]].*$" - symbol: "^~~~~+$" # h4 - type: "^====[[:space:]].*$" - type: "^\\^\\^\\^\\^+$" # h5 - constant: "^=====[[:space:]].*$" - constant: "^\\+\\+\\+\\++$" # attributes - type.keyword: ":.*:" - identifier.macro: "\\{[a-z0-9]*\\}" - identifier: "\\\\\\{[a-z0-9]*\\}" - identifier: "\\+\\+\\+\\{[a-z0-9]*\\}\\+\\+\\+" # Paragraph Title - statement: "^\\..*$" # source - identifier: "^\\[(source,.+|NOTE|TIP|IMPORTANT|WARNING|CAUTION)\\]" # Other markup - constant.string: ".*[[:space:]]\\+$" - constant.string: "_[^_]+_" - constant.string: "\\*[^\\*]+\\*" - constant.string: "\\+[^\\+]+\\+" - constant.string: "`[^`]+`" - constant.string: "\\^[^\\^]+\\^" - constant.string: "~[^~]+~" - constant.string: "'[^']+'" - constant: "`{1,2}[^']+'{1,2}" # bullets - symbol: "^[[:space:]]*[\\*\\.-]{1,5}[[:space:]]" # anchors - "bold default": "\\[\\[.*\\]\\]" - "bold default": "<<.*>>" micro-2.0.14/runtime/syntax/asm.yaml0000664000175000017510000002260314663411671016731 0ustar nileshnileshfiletype: asm detect: filename: "\\.(S|s|asm)$" rules: # This file is made for NASM assembly ## Instructions # x86 - statement: "\\b(?i)(mov|aaa|aad|aam|aas|adc|add|and|call|cbw|clc|cld|cli|cmc|cmp|cmpsb|cmpsw|cwd|daa|das|dec|div|esc|hlt|idiv|imul|in|inc|int|into|iret|ja|jae|jb|jbe|jc|je|jg|jge|jl|jle|jna|jnae|jnb|jnbe|jnc|jne|jng|jnge|jnl|jnle|jno|jnp|jns|jnz|jo|jp|jpe|jpo|js|jz|jcxz|jmp|lahf|lds|lea|les|lock|lodsb|lodsw|loop|loope|loopne|loopnz|loopz|movsb|movsw|mul|neg|nop|or|pop|popf|push|pushf|rcl|rcr|rep|repe|repne|repnz|repz|ret|retn|retf|rol|ror|sahf|sal|sar|sbb|scasb|scasw|shl|shr|stc|std|sti|stosb|stosw|sub|test|wait|xchg|xlat|xor)(?-i)\\b" - statement: "\\b(?i)(bound|enter|ins|leave|outs|popa|pusha)(?-i)\\b" - statement: "\\b(?i)(arpl|clts|lar|lgdt|lidt|lldt|lmsw|loadall|lsl|ltr|sgdt|sidt|sldt|smsw|str|verr|verw)(?-i)\\b" - statement: "\\b(?i)(bsf|bsr|bt|btc|btr|bts|cdq|cmpsd|cwde|insd|iret|iretd|iretf|jecxz|lfs|lgs|lss|lodsd|loopw|loopew|loopnew|loopnzw|loopzw|loopd|looped|loopned|loopnzd|loopzd|cr|tr|dr|movsd|movsx|movzx|outsd|popad|popfd|pushad|pushfd|scasd|seta|setae|setb|setbe|setc|sete|setg|setge|setl|setle|setna|setnae|setnb|setnbe|setnc|setne|setng|setnge|setnl|setnle|setno|setnp|setns|setnz|seto|setp|setpe|setpo|sets|setz|shdl|shrd|stosd)(?-i)\\b" - statement: "\\b(?i)(bswap|cmpxcgh|invd|invlpg|wbinvd|xadd)(?-i)\\b" - statement: "\\b(?i)(cpuid|cmpxchg8b|rdmsr|rdtsc|wrmsr|rsm)(?-i)\\b" - statement: "\\b(?i)(rdpmc)(?-i)\\b" - statement: "\\b(?i)(syscall|sysret)(?-i)\\b" - statement: "\\b(?i)(cmova|cmovae|cmovb|cmovbe|cmovc|cmove|cmovg|cmovge|cmovl|cmovle|cmovna|cmovnae|cmovnb|cmovnbe|cmovnc|cmovne|cmovng|cmovnge|cmovnle|cmovno|cmovpn|cmovns|cmovnz|cmovo|cmovp|cmovpe|cmovpo|cmovs|cmovz|sysenter|sysexit|ud2)(?-i)\\b" - statement: "\\b(?i)(maskmovq|movntps|movntq|prefetch0|prefetch1|prefetch2|prefetchnta|sfence)(?-i)\\b" - statement: "\\b(?i)(clflush|lfence|maskmovdqu|mfence|movntdq|movnti|movntpd|pause)(?-i)\\b" - statement: "\\b(?i)(monitor|mwait)(?-i)\\b" - statement: "\\b(?i)(cdqe|cqo|cmpsq|cmpxchg16b|iretq|jrcxz|lodsq|movsdx|popfq|pushfq|rdtscp|scasq|stosq|swapgs)(?-i)\\b" - statement: "\\b(?i)(clgi|invlpga|skinit|stgi|vmload|vmmcall|vmrun|vmsave)(?-i)\\b" - statement: "\\b(?i)(vmptrdl|vmptrst|vmclear|vmread|vmwrite|vmcall|vmlaunch|vmresume|vmxoff|vmxon)(?-i)\\b" - statement: "\\b(?i)(lzcnt|popcnt)(?-i)\\b" - statement: "\\b(?i)(bextr|blcfill|blci|blcic|blcmask|blcs|blsfill|blsic|t1mskc|tzmsk)(?-i)\\b" # x87 - statement: "\\b(?i)(f2xm1|fabs|fadd|faddp|fbld|fbstp|fchs|fclex|fcom|fcomp|fcompp|fdecstp|fdisi|fdiv|fvidp|fdivr|fdivrp|feni|ffree|fiadd|ficom|ficomp|fidiv|fidivr|fild|fimul|fincstp|finit|fist|fistp|fisub|fisubr|fld|fld1|fldcw|fldenv|fldenvw|fldl2e|fldl2t|fldlg2|fldln2|fldpi|fldz|fmul|fmulp|fnclex|fndisi|fneni|fninit|fnop|fnsave|fnsavenew|fnstcw|fnstenv|fnstenvw|fnstsw|fpatan|fprem|fptan|frndint|frstor|frstorw|fsave|fsavew|fscale|fsqrt|fst|fstcw|fstenv|fstenvw|fstp|fstpsw|fsub|fsubp|fsubr|fsubrp|ftst|fwait|fxam|fxch|fxtract|fyl2x|fyl2xp1)(?-i)\\b" - statement: "\\b(?i)(fsetpm)(?-i)\\b" - statement: "\\b(?i)(fcos|fldenvd|fsaved|fstenvd|fprem1|frstord|fsin|fsincos|fstenvd|fucom|fucomp|fucompp)(?-i)\\b" - statement: "\\b(?i)(fcmovb|fcmovbe|fcmove|fcmove|fcmovnb|fcmovnbe|fcmovne|fcmovnu|fcmovu)(?-i)\\b" - statement: "\\b(?i)(fcomi|fcomip|fucomi|fucomip)(?-i)\\b" - statement: "\\b(?i)(fxrstor|fxsave)(?-i)\\b" - statement: "\\b(?i)(fisttp)(?-i)\\b" - statement: "\\b(?i)(ffreep)(?-i)\\b" # SIMD - statement: "\\b(?i)(emms|movd|movq|packssdw|packsswb|packuswb|paddb|paddw|paddd|paddsb|paddsw|paddusb|paddusw|pand|pandn|por|pxor|pcmpeqb|pcmpeqw|pcmpeqd|pcmpgtb|pcmpgtw|pcmpgtd|pmaddwd|pmulhw|pmullw|psllw|pslld|psllq|psrad|psraw|psrlw|psrld|psrlq|psubb|psubw|psubd|psubsb|psubsw|psubusb|punpckhbw|punpckhwd|punpckhdq|punkcklbw|punpckldq|punpcklwd)(?-i)\\b" - statement: "\\b(?i)(paveb|paddsiw|pmagw|pdistib|psubsiw|pmwzb|pmulhrw|pmvnzb|pmvlzb|pmvgezb|pmulhriw|pmachriw)(?-i)\\b" - statement: "\\b(?i)(femms|pavgusb|pf2id|pfacc|pfadd|pfcmpeq|pfcmpge|pfcmpgt|pfmax|pfmin|pfmul|pfrcp|pfrcpit1|pfrcpit2|pfrsqit1|pfrsqrt|pfsub|pfsubr|pi2fd|pmulhrw|prefetch|prefetchw)(?-i)\\b" - statement: "\\b(?i)(pf2iw|pfnacc|pfpnacc|pi2fw|pswapd)(?-i)\\b" - statement: "\\b(?i)(pfrsqrtv|pfrcpv)(?-i)\\b" - statement: "\\b(?i)(addps|addss|cmpps|cmpss|comiss|cvtpi2ps|cvtps2pi|cvtsi2ss|cvtss2si|cvttps2pi|cvttss2si|divps|divss|ldmxcsr|maxps|maxss|minps|minss|movaps|movhlps|movhps|movlhps|movlps|movmskps|movntps|movss|movups|mulps|mulss|rcpps|rcpss|rsqrtps|rsqrtss|shufps|sqrtps|sqrtss|stmxcsr|subps|subss|ucomiss|unpckhps|unpcklps)(?-i)\\b" - statement: "\\b(?i)(andnps|andps|orps|pavgb|pavgw|pextrw|pinsrw|pmaxsw|pmaxub|pminsw|pminub|pmovmskb|pmulhuw|psadbw|pshufw|xorps)(?-i)\\b" - statement: "\\b(?i)(movups|movss|movlps|movhlps|movlps|unpcklps|unpckhps|movhps|movlhps|prefetchnta|prefetch0|prefetch1|prefetch2|nop|movaps|cvtpi2ps|cvtsi2ss|cvtps2pi|cvttss2si|cvtps2pi|cvtss2si|ucomiss|comiss|sqrtps|sqrtss|rsqrtps|rsqrtss|rcpps|andps|orps|xorps|addps|addss|mulps|mulss|subps|subss|minps|minss|divps|divss|maxps|maxss|pshufw|ldmxcsr|stmxcsr|sfence|cmpps|cmpss|pinsrw|pextrw|shufps|pmovmskb|pminub|pmaxub|pavgb|pavgw|pmulhuw|movntq|pminsw|pmaxsw|psadbw|maskmovq)(?-i)\\b" - statement: "\\b(?i)(addpd|addsd|addnpd|cmppd|cmpsd)(?-i)\\b" - statement: "\\b(?i)(addpd|addsd|andnpd|andpd|cmppd|cmpsd|comisd|cvtdq2pd|cvtdq2ps|cvtpd2dq|cvtpd2pi|cvtpd2ps|cvtpi2pd|cvtps2dq|cvtps2pd|cvtsd2si|cvtsd2ss|cvtsi2sd|cvtss2sd|cvttpd2dq|cvttpd2pi|cvttps2dq|cvttsd2si|divpd|divsd|maxpd|maxsd|minpd|minsd|movapd|movhpd|movlpd|movmskpd|movsd|movupd|mulpd|mulsd|orpd|shufpd|sqrtpd|sqrtsd|subpd|subsd|ucomisd|unpckhpd|unpcklpd|xorpd)(?-i)\\b" - statement: "\\b(?i)(movdq2q|movdqa|movdqu|movq2dq|paddq|psubq|pmuludq|pshufhw|pshuflw|pshufd|pslldq|psrldq|punpckhqdq|punpcklqdq)(?-i)\\b" - statement: "\\b(?i)(addsubpd|addsubps|haddpd|haddps|hsubpd|hsubps|movddup|movshdup|movsldu)(?-i)\\b" - statement: "\\b(?i)(lddqu)(?-i)\\b" - statement: "\\b(?i)(psignw|psignd|psignb|pshufb|pmulhrsw|pmaddubsw|phsubw|phsubsw|phsubd|phaddw|phaddsw|phaddd|palignr|pabsw|pabsd|pabsb)(?-i)\\b" - statement: "\\b(?i)(dpps|dppd|blendps|blendpd|blendvps|blendvpd|roundps|roundss|roundpd|roundsd|insertps|extractps)(?-i)\\b" - statement: "\\b(?i)(mpsadbw|phminposuw|pmulld|pmuldq|pblendvb|pblendw|pminsb|pmaxsb|pminuw|pmaxuw|pminud|pmaxud|pminsd|pmaxsd|pinsrb|pinsrd/pinsrq|pextrb|pextrw|pextrd/pextrq|pmovsxbw|pmovzxbw|pmovsxbd|pmovzxbd|pmovsxbq|pmovzxbq|pmovsxwd|pmovzxwd|pmovsxwq|pmovzxwq|pmovsxdq|pmovzxdq|ptest|pcmpeqq|packusdw|movntdqa)(?-i)\\b" - statement: "\\b(?i)(extrq|insertq|movntsd|movntss)(?-i)\\b" - statement: "\\b(?i)(crc32|pcmpestri|pcmpestrm|pcmpistri|pcmpistrm|pcmpgtq)(?-i)\\b" - statement: "\\b(?i)(vfmaddpd|vfmaddps|vfmaddsd|vfmaddss|vfmaddsubpd|vfmaddsubps|vfmsubaddpd|vfmsubaddps|vfmsubpd|vfmsubps|vfmsubsd|vfmsubss|vfnmaddpd|vfnmaddps|vfnmaddsd|vfnmaddss|vfnmsubps|vfnmsubsd|vfnmsubss)(?-i)\\b" # Crypto - statement: "\\b(?i)(aesenc|aesenclast|aesdec|aesdeclast|aeskeygenassist|aesimc)(?-i)\\b" - statement: "\\b(?i)(sha1rnds4|sha1nexte|sha1msg1|sha1msg2|sha256rnds2|sha256msg1|sha256msg2)(?-i)\\b" # Undocumented - statement: "\\b(?i)(aam|aad|salc|icebp|loadall|loadalld|ud1)(?-i)\\b" ## Registers - identifier: "\\b(?i)(al|ah|bl|bh|cl|ch|dl|dh|bpl|sil|r8b|r9b|r10b|r11b|dil|spl|r12b|r13b|r14b|r15)(?-i)\\b" - identifier: "\\b(?i)(cw|sw|tw|fp_ds|fp_opc|fp_ip|fp_dp|fp_cs|cs|ss|ds|es|fs|gs|gdtr|idtr|tr|ldtr|ax|bx|cx|dx|bp|si|r8w|r9w|r10w|r11w|di|sp|r12w|r13w|r14w|r15w|ip)(?-i)\\b" - identifier: "\\b(?i)(fp_dp|fp_ip|eax|ebx|ecx|edx|ebp|esi|r8d|r9d|r10d|r11d|edi|esp|r12d|r13d|r14d|r15d|eip|eflags|mxcsr)(?-i)\\b" - identifier: "\\b(?i)(mm0|mm1|mm2|mm3|mm4|mm5|mm6|mm7|rax|rbx|rcx|rdx|rbp|rsi|r8|r9|r10|r11|rdi|rsp|r12|r13|r14|r15|rip|rflags|cr0|cr1|cr2|cr3|cr4|cr5|cr6|cr7|cr8|cr9|cr10|cr11|cr12|cr13|cr14|cr15|msw|dr0|dr1|dr2|dr3|r4|dr5|dr6|dr7|dr8|dr9|dr10|dr11|dr12|dr13|dr14|dr15)(?-i)\\b" - identifier: "\\b(?i)(st0|st1|st2|st3|st4|st5|st6|st7)(?-i)\\b" - identifier: "\\b(?i)(xmm0|xmm1|xmm2|xmm3|xmm4|xmm5|xmm6|xmm7|xmm8|xmm9|xmm10|xmm11|xmm12|xmm13|xmm14|xmm15)(?-i)\\b" - identifier: "\\b(?i)(ymm0|ymm1|ymm2|ymm3|ymm4|ymm5|ymm6|ymm7|ymm8|ymm9|ymm10|ymm11|ymm12|ymm13|ymm14|ymm15)(?-i)\\b" - identifier: "\\b(?i)(zmm0|zmm1|zmm2|zmm3|zmm4|zmm5|zmm6|zmm7|zmm8|zmm9|zmm10|zmm11|zmm12|zmm13|zmm14|zmm15|zmm16|zmm17|zmm18|zmm19|zmm20|zmm21|zmm22|zmm23|zmm24|zmm25|zmm26|zmm27|zmm28|zmm29|zmm30|zmm31)(?-i)\\b" ## Constants # Number - it works - constant.number: "\\b(|h|A|0x)+[0-9]+(|h|A)+\\b" - constant.number: "\\b0x[0-9 a-f A-F]+\\b" ## Preprocessor (NASM) - preproc: "%+(\\+|\\?|\\?\\?|)[a-z A-Z 0-9]+" - preproc: "%\\[[. a-z A-Z 0-9]*\\]" ## Other - statement: "\\b(?i)(extern|global|section|segment|_start|\\.text|\\.data|\\.bss)(?-i)\\b" - statement: "\\b(?i)(db|dw|dd|dq|dt|ddq|do)(?-i)\\b" - identifier: "[a-z A-Z 0-9 _]+:" - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\." - constant.string: start: "'" end: "'" skip: "\\\\." rules: - constant.specialChar: "\\\\." - comment: start: ";" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" micro-2.0.14/runtime/syntax/ats.yaml0000664000175000017510000000775314663411671016751 0ustar nileshnileshfiletype: ATS detect: filename: "\\.(d|h|s)ats$" rules: - default: \b[[:alnum:]]+[^0-9A-Za-z] # Operators - symbol.operator: "[.:;+`|~<>?='\\&]|/|%|-|,|!|\\*|@|\\#" - symbol.operator: \^ # Types, abstract types and some predefined sorts - type: \b(addr|age?z|bool|char|cls|schar|uchar|double|ldouble|eff|exn|float|int(ptr)?|lincloptr|uint)\b - type: \b(lint|ulint|llint|ullint|nat|ptr|real|ref|size_t|ssize_t|sint|usint|string|tkind|viewt|v?t0p|vt|void)\b - type: \b(prop|t[@0]ype|type|viewt[@0]ype|viewtype|vt[@0]ype|vtype|view)\b - type: \b(prop[+-]|t[@0]ype[+-]|type[+-]|viewt[@0]ype[+-]|viewtype[+-]|vt[@0]ype[+-]|vtype[+-]|view[+-]) # Statements - statement: \b(abstype|abst|absprop|absviewt|absvt(ype)?|absview|and|andalso|as|(re)?assume|begin|(pr)?case|s?case)\b - statement: \b(classdec|dataprop|data(v|view)?type|dataview|datasort|do|dynload|else|end|exception|extype|extva(r|l)|s?if)\b - statement: \b(ifcase|import|for|in|infix(l|r)?|let|local|macrodef|macdef|not|of|op|or|orelse|overload|(pre|post|non)fix)\b - statement: \b(propdef|rec|sortdef|stacst|stadef|staload|stavar|sta(tic)?|symelim|symintr|tkindef|then|try|viewdef|v?typedef)\b - statement: \b(viewtypedef|(pr)?va(l|r)|when|where|while|with|withtype|withprop|withv(iew)?type|withview)\b - statement: \b(abst[@0]ype|absviewt[@0]?ype|absvt[@0]ype|abstbox|abstflat|absvtbox|absvtflat|absimpl|absreimpl|abs)\b - statement: \b(case[+-]|(pr)?va(l|r)[+-]|for\*|while\*) # Numbers - constant.number: \b[0-9.]+([eE][+-]?[0-9]+)?[fFlL]?\b - constant.number: \b0[xX][0-9A-Fa-f]*(\.[0-9A-Fa-f]*)?[pP][+-]?[0-9]+[fFlL]?\b - constant.number: \b([0-9]+|0[xX][0-9A-Fa-f]+)[lLuU]*\b # Function-related keywords, special functions and namespaces. Not really identifiers - identifier: \b(fix|(pr)?fu?n|fnx|castfn|praxi|extern|lam|llam|(pr)?implement|(pr)?implmnt)\b - identifier: \b(fix@|fold@|free@|lam@|llam@|addr@|view@|ref@|fn\*) - identifier: \$\w*\b # Other keywords, function effects... - special: (\$(arrpsz|arrptrsize|break|continue|d2ctype|delay|effmask_(ntm|exn|ref|wrt|all)))\b - special: (\$(effmask|extern|extype_struct|extype|extkind|extval|extfcall|extmcall|ldelay|literal))\b - special: (\$(li?st_vt|li?st_t|li?st|myfilename|myfunction|mylocation|raise|rec(ord)?_vt))\b - special: (\$(rec(ord)?_t|rec(ord)?|showtype|solver_assert|solver_verify|tempenver))\b - special: (\$(tup(le)?_vt|tup(le)?_t|tup(le)?|tyrep|vararg|vcopyenv_vt|vcopyenv_v))\b - special: \!(wrt|exnref|exnwrt|exn|refwrt|ref|all|ntm|laz)\b - special: \b(fun(0|1)|(lin)?cloptr(0|1)?|cloref(0|1)?|clo(0|1)?|lin|prf)\b - special: \b(f?print(ln)?!|prerr(ln)?!|tupz!) # Some C macros and other ATS macros - preproc: ^[[:space:]]*#[[:space:]]*(define|pragma|include|(un|ifn?)def|endif|el(if|se)|if|warning|error|assert)\b - preproc: ^[[:space:]]*#[[:space:]]*(codegen2|codegen3|elifdef|elifndef|prerr|print|require|then|staload|dynload)\b # Boolean values - constant.bool: \b(true|false|null)\b # The "%{ ... %}" block inserts foreign code into ATS at compile-time # Code inside of it is generally C or JavaScript - default: start: "%{[#^$]?" end: "%}" skip: "\\." limit-group: symbol.operator rules: - include: "c" - include: "javascript" # Strings and chars - constant.string: \"[^"]*\" - constant.string: \'[^']*\' # Comments # "////" comments everything until it reaches EOF - comment.block: start: //// end: $a rules: - todo: (TODO|XXX|FIXME) - comment.line: start: // end: $ rules: - todo: (TODO|XXX|FIXME) # ML-like block comment - comment.block: start: \(\* end: \*\) rules: - todo: (TODO|XXX|FIXME) # C-like block comment - comment.block: start: /\* end: \*\/ rules: - todo: (TODO|XXX|FIXME) micro-2.0.14/runtime/syntax/awk.yaml0000664000175000017510000000273314663411671016735 0ustar nileshnileshfiletype: awk detect: filename: "\\.awk$" header: "^#!.*bin/(env +)?awk( |$)" rules: - preproc: "\\$[A-Za-z0-9_!@#$*?\\-]+" - preproc: "\\b(ARGC|ARGIND|ARGV|BINMODE|CONVFMT|ENVIRON|ERRNO|FIELDWIDTHS)\\b" - preproc: "\\b(FILENAME|FNR|FS|IGNORECASE|LINT|NF|NR|OFMT|OFS|ORS)\\b" - preproc: "\\b(PROCINFO|RS|RT|RSTART|RLENGTH|SUBSEP|TEXTDOMAIN)\\b" - identifier.class: "\\b(function|extension|BEGIN|END)\\b" - symbol.operator: "[\\-+*/%^|!=&<>?;:]|\\\\|\\[|\\]" - statement: "\\b(for|if|while|do|else|in|delete|exit)\\b" - special: "\\b(break|continue|return)\\b" - statement: "\\b(close|getline|next|nextfile|print|printf|system|fflush)\\b" - statement: "\\b(atan2|cos|exp|int|log|rand|sin|sqrt|srand)\\b" - statement: "\\b(asort|asorti|gensub|gsub|index|length|match)\\b" - statement: "\\b(split|sprintf|strtonum|sub|substr|tolower|toupper)\\b" - statement: "\\b(mktime|strftime|systime)\\b" - statement: "\\b(and|compl|lshift|or|rshift|xor)\\b" - statement: "\\b(bindtextdomain|dcgettext|dcngettext)\\b" - special: "/.*[^\\\\]/" - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\." - constant.string: start: "'" end: "'" skip: "\\\\." rules: - constant.specialChar: "\\\\." - comment: start: "#" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" micro-2.0.14/runtime/syntax/bat.yaml0000664000175000017510000001005514663411671016715 0ustar nileshnileshfiletype: batch detect: filename: "(\\.bat$|\\.cmd$)" rules: # Numbers - constant.number: "\\b[0-9]+\\b" # Brackets and symbols - special: "(\\{|\\}|\\(|\\)|\\;|\\]|\\[|`|\\\\|\\$|<|>|!|=|&|\\|)" # Conditionals and control flow # note (?i) means case insensitive match - type: "\\b(?i)(case|do|done|elif|else|esac|exit|fi|for|function|if|in|local|read|return|select|then|until|while)\\b" - type: "\\b(?i)(equ|neq|lss|leq|gtr|geq|on|off)\\b" - type: "\\b(?i)(goto|for|in|do|call|exit|not|exist|errorlevel|defined)\\b" - type: "\\b(?i)(prn|nul|lpt3|lpt2|lpt1|con|com4|com3|com2|com1|aux)\\b" # keywords - statement: "\\b(?i)(adprep|append|arp|assoc|at|atmadm|attrib|auditpol|autochk|autoconv|autofmt|bcdboot|bcdedit|bdehdcfg|bitsadmin|bootcfg|break|brea)\\b" - statement: "\\b(?i)(cacls|cd|certreq|certutil|chcp|change|choice|cipher|chdir|chkdsk|chkntfs|chglogon|chgport|chgusr|clip|cls|clscluadmin|cluster|cmd|cmdkey|cmstp|color)\\b" - statement: "\\b(?i)(comp|compact|convert|copy|cprofile|cscript|csvde|date|dcdiag|dcgpofix|dcpromo|defra|del|dfscmd|dfsdiag|dfsrmig|diantz|dir|dirquota|diskcomp|diskcopy|diskpart|diskperf|diskraid|diskshadow|dispdiag|doin|dnscmd|doskey|driverquery|dsacls|dsadd|dsamain|dsdbutil|dsget|dsmgmt|dsmod|dsmove|dsquery|dsrm)\\b" - statement: "\\b(?i)(echo|edit|endlocal|erase|esentutl|eventcreate|eventquery|eventtriggers|evntcmd|expand|extract)\\b" - statement: "\\b(?i)(fc|filescrn|find|findstr|finger|flattemp|fonde|forfiles|format|freedisk|fs|fsutil|ftp|ftype|fveupdate|getmac|gettype|gpfixup|gpresult|gpupdate|graftabl)\\b" - statement: "\\b(?i)(hashgen|hep|help|helpctr|hostname|icacls|iisreset|inuse|ipconfig|ipxroute|irftp|ismserv|jetpack|keyb|klist|ksetup|ktmutil|ktpass|label|ldifd|ldp|lodctr|logman|logoff|lpq|lpr|macfile)\\b" - statement: "\\b(?i)(makecab|manage-bde|mapadmin|md|mkdir|mklink|mmc|mode|more|mount|mountvol|move|mqbup|mqsvc|mqtgsvc|msdt|msg|msiexec|msinfo32|mstsc|nbtstat|net computer|net group)\\b" - statement: "\\b(?i)(net localgroup|net print|net session|net share|net start|net stop|net use|net user|net view|net|netcfg|netdiag|netdom|netsh|netstat|nfsadmin|nfsshare|nfsstat|nlb)\\b" - statement: "\\b(?i)(nlbmgr|nltest|nslookup|ntackup|ntcmdprompt|ntdsutil|ntfrsutl|openfiles|pagefileconfig|path|pathping|pause|pbadmin|pentnt|perfmon|ping|pnpunatten|pnputil|popd)\\b" - statement: "\\b(?i)(powercfg|powershell|powershell_ise|print|prncnfg|prndrvr|prnjobs|prnmngr|prnport|prnqctl|prompt|pubprn|pushd|pushprinterconnections|pwlauncher|qappsrv|qprocess)\\b" - statement: "\\b(?i)(query|quser|qwinsta|rasdial|rcp|rd|rdpsign|regentc|recover|redircmp|redirusr|reg|regini|regsvr32|relog|ren|rename|rendom|repadmin|repair-bde|replace|reset|restore)\\b" - statement: "\\b(?i)(rxec|risetup|rmdir|robocopy|route|rpcinfo|rpcping|rsh|runas|rundll32|rwinsta|scp|sc|setlocal|session|schtasks|scwcmd|secedit|serverceipoptin|servrmanagercmd|serverweroptin|set|setspn)\\b" - statement: "\\b(?i)(setx|sfc|shadow|shift|showmount|shutdown|sort|ssh|start|storrept|subst|sxstrace|ysocmgr|systeminfo|takeown|tapicfg|taskkill|tasklist|tcmsetup|telnet|tftp|time)\\b" - statement: "\\b(?i)(timeout|title|tlntadmn|tpmvscmgr|tpmvscmgr|tacerpt|tracert|tree|tscon|tsdiscon|tsecimp|tskill|tsprof|type|typeperf|tzutil|uddiconfig|umount|unlodctr|ver|verify)\\b" - statement: "\\b(?i)(verifier|verif|vol|vssadmin|w32tm|waitfor|wbadmin|wdsutil|wecutil|wevtutil|where|whoami|winnt|winnt32|winpop|winrm|winrs|winsat|wlbs|mic|wscript|xcopy)\\b" # / Flags - constant: "(/\\w+)" # Variables - special: "(%%\\w+)" - special: "(%\\w+%)" # Conditional flags - type: "--[a-z-]+" - type: "\\ -[a-z]+" - identifier: "\\$\\{?[0-9A-Z_!@#$*?-]+\\}?" - identifier: "\\$\\{?[0-9A-Z_!@#$*?-]+\\}?" # "" String - constant.string: start: \" end: \" skip: \. rules: - constant.specialChar: (\\0|\\\\|\\t|\\n|\\r|\\"|\\') - constant.unicode: \\u\{[[:xdigit:]]+} # '' string - constant.string: "(\\'.+\\')" # rem as comment - comment.rem: "(?i)(rem\\s.*)" # :: as comment - comment.rem: "(?i)(\\:\\:\\s.*)" micro-2.0.14/runtime/syntax/c.yaml0000664000175000017510000000535214663411671016375 0ustar nileshnileshfiletype: c detect: filename: "(\\.(c|C)$|\\.(h|H)$|\\.ii?$|\\.(def)$)" rules: - identifier: "\\b[A-Z_][0-9A-Z_]+\\b" - type: "\\b(_Atomic|_BitInt|float|double|_Decimal32|_Decimal64|_Decimal128|_Complex|complex|_Imaginary|imaginary|_Bool|bool|char|int|short|long|enum|void|struct|union|typedef|typeof|typeof_unqual|(un)?signed|inline|_Noreturn)\\b" - type: "\\b((s?size)|((u_?)?int(8|16|32|64|ptr))|char(8|16|32)|wchar)_t\\b" # GCC float/decimal/fixed types - type: "\\b(_Float16|__fp16|_Float32|_Float32x|_Float64|_Float64x|__float80|_Float128|_Float128x|__float128|__ibm128|__int128|_Fract|_Sat|_Accum)\\b" - type: "\\b[a-z_][0-9a-z_]+(_t|_T)\\b" - statement: "\\b(auto|volatile|register|restrict|_Alignas|alignas|_Alignof|alignof|static|const|constexpr|extern|_Thread_local|thread_local)\\b" - statement: "\\b(for|if|while|do|else|case|default|switch|_Generic|_Static_assert|static_assert)\\b" - statement: "\\b(goto|continue|break|return)\\b" - statement: "\\b(asm|fortran)\\b" - preproc: "^[[:space:]]*#[[:space:]]*(define|embed|pragma|include|(un|ifn?)def|endif|el(if|ifdef|ifndef|se)|if|line|warning|error|__has_include|__has_embed|__has_c_attribute)" - preproc: "^[[:space:]]*_Pragma\\b" # GCC builtins - statement: "__attribute__[[:space:]]*\\(\\([^)]*\\)\\)" - statement: "__(aligned|asm|builtin|extension|hidden|inline|packed|restrict|section|typeof|weak)__" # Operator Color - symbol.operator: "[-+*/%=<>.:;,~&|^!?]|\\b(offsetof|sizeof)\\b" - symbol.brackets: "[(){}]|\\[|\\]" # Integer Constants - constant.number: "(\\b([1-9][0-9]*|0[0-7]*|0[Xx][0-9A-Fa-f]+|0[Bb][01]+)([Uu][Ll]?[Ll]?|[Ll][Ll]?[Uu]?)?\\b)" # Decimal Floating Constants - constant.number: "(\\b(([0-9]*[.][0-9]+|[0-9]+[.][0-9]*)([Ee][+-]?[0-9]+)?|[0-9]+[Ee][+-]?[0-9]+)[FfLl]?\\b)" # Hexadecimal Floating Constants - constant.number: "(\\b0[Xx]([0-9A-Za-z]*[.][0-9A-Za-z]+|[0-9A-Za-z]+[.][0-9A-Za-z]*)[Pp][+-]?[0-9]+[FfLl]?\\b)" - constant.bool: "(\\b(true|false|NULL|nullptr|TRUE|FALSE)\\b)" - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\([\"'abfnrtv\\\\]|[0-3]?[0-7]{1,2}|x[0-9A-Fa-f]{1,2}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})" - constant.string: start: "'" end: "'" skip: "\\\\." rules: - error: "..+" - constant.specialChar: "\\\\([\"'abfnrtv\\\\]|[0-3]?[0-7]{1,2}|x[0-9A-Fa-f]{1,2}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})" - comment: start: "//" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" - comment: start: "/\\*" end: "\\*/" rules: - todo: "(TODO|XXX|FIXME):?" micro-2.0.14/runtime/syntax/caddyfile.yaml0000664000175000017510000000072614663411671020077 0ustar nileshnileshfiletype: caddyfile detect: filename: "Caddyfile" rules: - identifier: "^\\s*\\S+(\\s|$)" - type: "^([\\w.:/-]+,? ?)+[,{]$" - constant.specialChar: "\\s{$" - constant.specialChar: "^\\s*}$" - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\." - preproc: "\\{(\\w+|\\$\\w+|%\\w+%)\\}" - comment: start: "#" end: "$" rules: [] micro-2.0.14/runtime/syntax/cake.yaml0000664000175000017510000000023614663411671017052 0ustar nileshnileshfiletype: cake detect: filename: "\\.cake$" rules: - include: "csharp" - preproc: "^[[:space:]]*#(addin|break|l(oad)?|module|r(eference)?|tool)" micro-2.0.14/runtime/syntax/clojure.yaml0000664000175000017510000000172714663411671017620 0ustar nileshnileshfiletype: clojure detect: filename: "\\.(clj[sc]?|edn)$" rules: # Constants - constant.bool: "\\b(true|false)\\b" - constant.macro: "\\b(nil)\\b" # Valid numbers - constant.number: "[\\-]?[0-9]+?\\b" - constant.number: "0x[0-9][A-Fa-f]+?\\b" - constant.number: "[\\-]?(3[0-6]|2[0-9]|1[0-9]|[2-9])r[0-9A-Z]+?\\b" # Invalid numbers - error: "[\\-]?([4-9][0-9]|3[7-9]|1|0)r[0-9A-Z]+?\\b" # Symbols - symbol.operator: "[=>+\\-*/'?]" # Types/casting - type: "\\b(byte|short|(big)?int(eger)?|long|float|num|bigdec|rationalize)\\b" # String highlighting - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "(\\\\u[0-9A-fa-f]{4,4}|\\\\newline|\\\\space|\\\\tab|\\\\formfeed|\\\\backspace|\\\\return|\\\\.)" # Comments - comment: start: ";" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" micro-2.0.14/runtime/syntax/cmake.yaml0000664000175000017510000000223514663411671017230 0ustar nileshnileshfiletype: cmake detect: filename: "(CMakeLists\\.txt|\\.cmake)$" rules: - identifier.var: "^[[:space:]]*[A-Z0-9_]+" - preproc: "^[[:space:]]*(include|include_directories|include_external_msproject)\\b" - statement: "^[[:space:]]*\\b((else|end)?if|else|(end)?while|(end)?foreach|break)\\b" - statement: "\\b(COPY|NOT|COMMAND|PROPERTY|POLICY|TARGET|EXISTS|IS_(DIRECTORY|ABSOLUTE)|DEFINED)\\b[[:space:]]" - statement: "[[:space:]]\\b(OR|AND|IS_NEWER_THAN|MATCHES|(STR|VERSION_)?(LESS|GREATER|EQUAL))\\b[[:space:]]" - special: "^[[:space:]]*\\b((end)?(function|macro)|return)" - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\." - constant.string: start: "'" end: "'" skip: "\\\\." rules: - constant.specialChar: "\\\\." - preproc: start: "\\$(\\{|ENV\\{)" end: "\\}" rules: [] - identifier.macro: "\\b(APPLE|UNIX|WIN32|CYGWIN|BORLAND|MINGW|MSVC(_IDE|60|71|80|90)?)\\b" - comment: start: "#" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" micro-2.0.14/runtime/syntax/coffeescript.yaml0000664000175000017510000000374214663411671020630 0ustar nileshnileshfiletype: coffeescript detect: filename: "\\.coffee$" rules: - symbol.operator: "([-+/*=<>!~%?:&|]|[.]{3})|\\b(and|or|is|isnt|not)\\b" - identifier.class: "([A-Za-z_][A-Za-z0-9_]*:[[:space:]]*(->|\\()|->)" - symbol.brackets: "[()]" - statement: "\\b(await|when|catch|continue|debugger|default|by|until)\\b" - statement: "\\b(delete|do|else|export|finally|for|class|extends|while|then)\\b" - statement: "\\b(get|if|import|from|in|instanceof|new|reject|resolve|return)\\b" - statement: "\\b(set|super|switch|this|throw|try|typeof|with|yield|unless)\\b" - constant.bool: "\\b(true|false|yes|no|on|off)\\b" - constant.bool.false: "\\b(false|no|off)\\b" - constant.bool.true: "\\b(true|yes|on)\\b" - constant.number: "\\b[-+]?([1-9][0-9]*|0[0-7]*|0x[0-9a-fA-F]+)([uU][lL]?|[lL][uU]?)?\\b" - constant.number: "\\b[-+]?([0-9]+\\.[0-9]*|[0-9]*\\.[0-9]+)([EePp][+-]?[0-9]+)?[fFlL]?" - constant.number: "\\b[-+]?([0-9]+[EePp][+-]?[0-9]+)[fFlL]?" - identifier: "@[A-Za-z0-9_]*" - error: "\\b(enum|implements|interface|package|private|protected|public)" - constant: "\\b(globalThis|Infinity|null|undefined|NaN)\\b" - constant: "\\b(null|undefined|NaN)\\b" - constant: "\\b(true|false|yes|no|on|off)\\b" - type: "\\b(Array|Boolean|Date|Enumerator|Error|Function|Generator|Map|Math)\\b" - type: "\\b(Number|Object|Promise|Proxy|Reflect|RegExp|Set|String|Symbol|WeakMap|WeakSet)\\b" - type: "\\b(BigInt64Array|BigUint64Array|Float32Array|Float64Array|Int16Array)\\b" - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\." - constant.string: start: "'" end: "'" skip: "\\\\." rules: - constant.specialChar: "\\\\." - comment: start: "###" end: "###" rules: [] - comment: start: "#" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" micro-2.0.14/runtime/syntax/colortest.yaml0000664000175000017510000000074314663411671020170 0ustar nileshnileshfiletype: colortest detect: filename: "ColorTest$" rules: - black: "\\bPLAIN\\b" - red: "\\bred\\b" - green: "\\bgreen\\b" - yellow: "\\byellow\\b" - blue: "\\bblue\\b" - magenta: "\\bmagenta\\b" - cyan: "\\bcyan\\b" - brightred: "\\bbrightred\\b" - brightgreen: "\\bbrightgreen\\b" - brightyellow: "\\bbrightyellow\\b" - brightblue: "\\bbrightblue\\b" - brightmagenta: "\\bbrightmagenta\\b" - brightcyan: "\\bbrightcyan\\b" micro-2.0.14/runtime/syntax/conky.yaml0000664000175000017510000001430714663411671017276 0ustar nileshnileshfiletype: conky detect: filename: "(\\.*conkyrc.*$|conky.conf)" rules: - type: "\\b(alignment|append_file|background|border_inner_margin|border_outer_margin|border_width|color0|color1|color2|color3|color4|color5|color6|color7|color8|color9|colorN|cpu_avg_samples|default_bar_height|default_bar_width|default_color|default_gauge_height|default_gauge_width|default_graph_height|default_graph_width|default_outline_color|default_shade_color|diskio_avg_samples|display|double_buffer|draw_borders|draw_graph_borders|draw_outline|draw_shades|extra_newline|font|format_human_readable|gap_x|gap_y|http_refresh|if_up_strictness|imap|imlib_cache_flush_interval|imlib_cache_size|lua_draw_hook_post|lua_draw_hook_pre|lua_load|lua_shutdown_hook|lua_startup_hook|mail_spool|max_port_monitor_connections|max_text_width|max_user_text|maximum_width|minimum_height|minimum_width|mpd_host|mpd_password|mpd_port|music_player_interval|mysql_host|mysql_port|mysql_user|mysql_password|mysql_db|net_avg_samples|no_buffers|nvidia_display|out_to_console|out_to_http|out_to_ncurses|out_to_stderr|out_to_x|override_utf8_locale|overwrite_file|own_window|own_window_class|own_window_colour|own_window_hints|own_window_title|own_window_transparent|own_window_type|pad_percents|pop3|sensor_device|short_units|show_graph_range|show_graph_scale|stippled_borders|temperature_unit|template|template0|template1|template2|template3|template4|template5|template6|template7|template8|template9|text|text_buffer_size|times_in_seconds|top_cpu_separate|top_name_width|total_run_times|update_interval|update_interval_on_battery|uppercase|use_spacer|use_xft|xftalpha|xftfont)\\b" # Configuration item constants - statement: "\\b(above|below|bottom_left|bottom_right|bottom_middle|desktop|dock|no|none|normal|override|skip_pager|skip_taskbar|sticky|top_left|top_right|top_middle|middle_left|middle_right|middle_middle|undecorated|yes)\\b" # Variables - preproc: "\\b(acpiacadapter|acpifan|acpitemp|addr|addrs|alignc|alignr|apcupsd|apcupsd_cable|apcupsd_charge|apcupsd_lastxfer|apcupsd_linev|apcupsd_load|apcupsd_loadbar|apcupsd_loadgauge|apcupsd_loadgraph|apcupsd_model|apcupsd_name|apcupsd_status|apcupsd_temp|apcupsd_timeleft|apcupsd_upsmode|apm_adapter|apm_battery_life|apm_battery_time|audacious_bar|audacious_bitrate|audacious_channels|audacious_filename|audacious_frequency|audacious_length|audacious_length_seconds|audacious_main_volume|audacious_playlist_length|audacious_playlist_position|audacious_position|audacious_position_seconds|audacious_status|audacious_title|battery|battery_bar|battery_percent|battery_short|battery_time|blink|bmpx_album|bmpx_artist|bmpx_bitrate|bmpx_title|bmpx_track|bmpx_uri|buffers|cached|cmdline_to_pid|color|color0|color1|color2|color3|color4|color5|color6|color7|color8|color9|combine|conky_build_arch|conky_build_date|conky_version|cpu|cpubar|cpugauge|cpugraph|curl|desktop|desktop_name|desktop_number|disk_protect|diskio|diskio_read|diskio_write|diskiograph|diskiograph_read|diskiograph_write|distribution|downspeed|downspeedf|downspeedgraph|draft_mails|else|endif|entropy_avail|entropy_bar|entropy_perc|entropy_poolsize|eval|eve|exec|execbar|execgauge|execgraph|execi|execibar|execigauge|execigraph|execp|execpi|flagged_mails|font|format_time|forwarded_mails|freq|freq_g|fs_bar|fs_bar_free|fs_free|fs_free_perc|fs_size|fs_type|fs_used|fs_used_perc|goto|gw_iface|gw_ip|hddtemp|head|hr|hwmon|i2c|i8k_ac_status|i8k_bios|i8k_buttons_status|i8k_cpu_temp|i8k_left_fan_rpm|i8k_left_fan_status|i8k_right_fan_rpm|i8k_right_fan_status|i8k_serial|i8k_version|ibm_brightness|ibm_fan|ibm_temps|ibm_volume|ical|iconv_start|iconv_stop|if_empty|if_existing|if_gw|if_match|if_mixer_mute|if_mounted|if_mpd_playing|if_running|if_smapi_bat_installed|if_up|if_updatenr|if_xmms2_connected|image|imap_messages|imap_unseen|ioscheduler|irc|kernel|laptop_mode|lines|loadavg|loadgraph|lua|lua_bar|lua_gauge|lua_graph|lua_parse|machine|mails|mboxscan|mem|memwithbuffers|membar|memwithbuffersbar|memeasyfree|memfree|memgauge|memgraph|memmax|memperc|mixer|mixerbar|mixerl|mixerlbar|mixerr|mixerrbar|moc_album|moc_artist|moc_bitrate|moc_curtime|moc_file|moc_rate|moc_song|moc_state|moc_timeleft|moc_title|moc_totaltime|monitor|monitor_number|mpd_album|mpd_artist|mpd_bar|mpd_bitrate|mpd_elapsed|mpd_file|mpd_length|mpd_name|mpd_percent|mpd_random|mpd_repeat|mpd_smart|mpd_status|mpd_title|mpd_track|mpd_vol|mysql|nameserver|new_mails|nodename|nodename_short|no_update|nvidia|obsd_product|obsd_sensors_fan|obsd_sensors_temp|obsd_sensors_volt|obsd_vendor|offset|outlinecolor|pb_battery|pid_chroot|pid_cmdline|pid_cwd|pid_environ|pid_environ_list|pid_exe|pid_nice|pid_openfiles|pid_parent|pid_priority|pid_state|pid_state_short|pid_stderr|pid_stdin|pid_stdout|pid_threads|pid_thread_list|pid_time_kernelmode|pid_time_usermode|pid_time|pid_uid|pid_euid|pid_suid|pid_fsuid|pid_gid|pid_egid|pid_sgid|pid_fsgid|pid_read|pid_vmpeak|pid_vmsize|pid_vmlck|pid_vmhwm|pid_vmrss|pid_vmdata|pid_vmstk|pid_vmexe|pid_vmlib|pid_vmpte|pid_write|platform|pop3_unseen|pop3_used|processes|read_tcp|read_udp|replied_mails|rss|running_processes|running_threads|scroll|seen_mails|shadecolor|smapi|smapi_bat_bar|smapi_bat_perc|smapi_bat_power|smapi_bat_temp|sony_fanspeed|stippled_hr|stock|swap|swapbar|swapfree|swapmax|swapperc|sysname|tab|tail|tcp_ping|tcp_portmon|template0|template1|template2|template3|template4|template5|template6|template7|template8|template9|texeci|texecpi|threads|time|to_bytes|top|top_io|top_mem|top_time|totaldown|totalup|trashed_mails|tztime|gid_name|uid_name|unflagged_mails|unforwarded_mails|unreplied_mails|unseen_mails|updates|upspeed|upspeedf|upspeedgraph|uptime|uptime_short|user_names|user_number|user_terms|user_times|user_time|utime|voffset|voltage_mv|voltage_v|weather|wireless_ap|wireless_bitrate|wireless_essid|wireless_link_bar|wireless_link_qual|wireless_link_qual_max|wireless_link_qual_perc|wireless_mode|words|xmms2_album|xmms2_artist|xmms2_bar|xmms2_bitrate|xmms2_comment|xmms2_date|xmms2_duration|xmms2_elapsed|xmms2_genre|xmms2_id|xmms2_percent|xmms2_playlist|xmms2_size|xmms2_smart|xmms2_status|xmms2_timesplayed|xmms2_title|xmms2_tracknr|xmms2_url)\\b" - identifier.var: "\\$\\{?[0-9A-Z_!@#$*?-]+\\}?" - symbol.operator: "(\\{|\\}|\\(|\\)|\\;|\\]|\\[|`|\\\\|\\$|<|>|!|=|&|\\|)" - constant.macro: "^TEXT$" micro-2.0.14/runtime/syntax/cpp.yaml0000664000175000017510000000550514663411671016735 0ustar nileshnileshfiletype: c++ detect: filename: "(\\.c(c|pp|xx)$|\\.h(h|pp|xx)?$|\\.ii?$|\\.(def)$)" signature: "namespace|template|public|protected|private" rules: - identifier: "\\b[A-Z_][0-9A-Z_]*\\b" - type: "\\b(float|double|bool|char|int|short|long|enum|void|struct|union|typedef|(un)?signed|inline)\\b" - type: "\\b(((s?size)|((u_?)?int(8|16|32|64|ptr))|char(8|16|32))_t|wchar_t)\\b" - type: "\\b[a-z_][0-9a-z_]+(_t|_T)\\b" - type: "\\b(final|override)\\b" - statement: "\\b(auto|volatile|const(expr|eval|init)?|mutable|register|thread_local|static|extern|decltype|explicit|virtual)\\b" - statement: "\\b(class|namespace|template|typename|this|friend|using|public|protected|private|noexcept)\\b" - statement: "\\b(concept|requires)\\b" - statement: "\\b(import|export|module)\\b" - statement: "\\b(for|if|while|do|else|case|default|switch)\\b" - statement: "\\b(try|throw|catch|operator|new|delete|static_assert)\\b" - statement: "\\b(goto|continue|break|return)\\b" - preproc: "^[[:space:]]*#[[:space:]]*(define|pragma|include|(un|ifn?)def|endif|el(if|se)|if|warning|error)|_Pragma" # Conditionally-supported/extension keywords - statement: "\\b(asm|fortran)\\b" # GCC builtins - statement: "(__attribute__[[:space:]]*\\(\\([^)]*\\)\\)|__(aligned|asm|builtin|hidden|inline|packed|restrict|section|typeof|weak)__)" # Operator Color - symbol.operator: "[-+*/%=<>.:;,~&|^!?]|\\b(sizeof|alignof|typeid|(and|or|xor|not)(_eq)?|bitor|compl|bitand|(const|dynamic|reinterpret|static)_cast)\\b" # Parenthetical Color - symbol.brackets: "[(){}]|\\[|\\]" # Integer Literals - constant.number: "(\\b([1-9][0-9']*|0[0-7']*|0[Xx][0-9a-fA-F']+|0[Bb][01]+)([Uu]?[Ll][Ll]?|[Ll][Ll]?[Uu]?)?\\b)" # Decimal Floating-point Literals - constant.number: "(\\b(([0-9']*[.][0-9']+|[0-9']+[.][0-9']*)([Ee][+-]?[0-9']+)?|[0-9']+[Ee][+-]?[0-9']+)[FfLl]?\\b)" # Hexadecimal Floating-point Literals - constant.number: "(\\b0[Xx]([0-9a-zA-Z']*[.][0-9a-zA-Z']+|[0-9a-zA-Z']+[.][0-9a-zA-Z']*)[Pp][+-]?[0-9']+[FfLl]?\\b)" - constant.bool: "(\\b(true|false|NULL|nullptr|TRUE|FALSE)\\b)" - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\([\"'abfnrtv\\\\]|[0-3]?[0-7]{1,2}|x[0-9A-Fa-f]{1,2}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})" - constant.string: start: "'" end: "'" skip: "\\\\." rules: - error: "..+" - constant.specialChar: "\\\\([\"'abfnrtv\\\\]|[0-3]?[0-7]{1,2}|x[0-9A-Fa-f]{1,2}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})" - comment: start: "//" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" - comment: start: "/\\*" end: "\\*/" rules: - todo: "(TODO|XXX|FIXME):?" micro-2.0.14/runtime/syntax/crontab.yaml0000664000175000017510000000272114663411671017600 0ustar nileshnileshfiletype: crontab detect: filename: "crontab$|/tmp/crontab\\.\\w+$" header: "^#.*?/etc/crontab" rules: # The time and date fields are: # field allowed values # ----- -------------- # minute 0-59 # hour 0-23 # day of month 0-31 # month 0-12 (or names, see below) # day of week 0-7 (0 or 7 is Sun, or use names) - statement: "^([\\*0-9,\\-\\/]+)\\s+([\\*0-9,\\-\\/]+)\\s+([\\*0-9,\\-\\/]+)\\s+(([\\*0-9,\\-\\/]+)|(\\b(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)\\b))\\s+(([\\*0-9,\\-\\/]+)|(\\b(sun|mon|tue|wed|thu|fri|sat)\\b))\\s+(.*)$\\n?" - constant: "^([\\*0-9,\\-\\/]+)\\s+([\\*0-9,\\-\\/]+)\\s+([\\*0-9,\\-\\/]+)\\s+(([\\*0-9,\\-\\/]+)|(\\b(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)\\b))\\s+(([\\*0-9,\\-\\/]+)|(\\b(sun|mon|tue|wed|thu|fri|sat)\\b))" # Shell Values - type: "^[A-Z]+\\=" # Months and weekday keywords - constant: "\\b(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)\\b" - constant: "\\b(sun|mon|tue|wed|thu|fri|sat)\\b" - type: "\\@(reboot|yearly|annually|monthly|weekly|daily|midnight|hourly)\\b" # Conditionals - special: "(\\{|\\}|\\(|\\)|\\;|\\]|\\[|`|\\\\|\\$|<|>|^|!|=|&|\\|)" - comment: start: "#" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" micro-2.0.14/runtime/syntax/crystal.yaml0000664000175000017510000000413014663411671017625 0ustar nileshnileshfiletype: crystal detect: filename: "\\.cr$" rules: # Asciibetical list of reserved words - statement: "\\b(abstract|alias|as|asm|begin|break|case|class|def|do|else|elsif|end|ensure|enum|extend|for|fun|if|in|include|instance_sizeof|lib|loop|macro|module|next|of|out|pointerof|private|protected|raise|require|rescue|return|select|self|sizeof|spawn|struct|super|then|type|typeof|uninitialized|union|unless|until|verbatim|when|while|with|yield)\\b" # Constants - constant: "\\b(true|false|nil)\\b" - constant.number: "\\b[0-9]+\\b" # Ones that can't be in the same regex because they include non-words. # The nil? one has to be after the constants. - statement: "\\b(nil\\?|as(\\?|\\b)|is_a\\?|responds_to\\?)" - type: "(\\$|@|@@)?\\b[A-Z]+[0-9A-Z_a-z]*" # Crystal "symbols" - constant: "([ ]|^):[0-9A-Z_]+\\b" # Some unique things we want to stand out - constant: "\\b(__FILE__|__LINE__)\\b" # Regular expressions - constant: "/([^/]|(\\\\/))*/[iomx]*|%r\\{([^}]|(\\\\}))*\\}[iomx]*" # Shell command expansion is in `backticks` or like %x{this}. These are # "double-quotish" (to use a perlism). - constant.string: "`[^`]*`|%x\\{[^}]*\\}" - constant.string: start: "`" end: "`" rules: [] - constant.string: start: "%x\\{" end: "\\}" rules: [] - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\." - symbol.brackets: start: "#\\{" end: "\\}" rules: - default: ".*" - constant.string: start: "'" end: "'" skip: "\\\\." rules: - constant.specialChar: "\\\\." - comment: start: "#" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" - comment.bright: start: "##" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" - constant: start: "<<-?'?EOT'?" end: "^EOT" rules: [] micro-2.0.14/runtime/syntax/csharp.yaml0000664000175000017510000000420414663411671017426 0ustar nileshnileshfiletype: csharp detect: filename: "\\.cs$" rules: # Class - identifier.class: "class +[A-Za-z0-9]+ *((:) +[A-Za-z0-9.]+)?" # Annotation - identifier.var: "@[A-Za-z]+" - preproc: "^[[:space:]]*#[[:space:]]*(define|elif|else|endif|endregion|error|if|line|nullable|pragma|region|undef|warning)" - identifier: "([A-Za-z0-9_]*[[:space:]]*[()])" - type: "\\b(bool|byte|sbyte|char|decimal|double|float|IntPtr|int|uint|long|ulong|managed|unmanaged|nint|nuint|object|short|ushort|string|base|this|var|void)\\b" - statement: "\\b(alias|as|case|catch|checked|default|do|dynamic|else|finally|fixed|for|foreach|goto|if|is|lock|new|null|return|switch|throw|try|unchecked|when|while|with)\\b" - statement: "\\b(abstract|add|and|args|async|await|class|const|delegate|enum|event|explicit|extern|file|get|global|implicit|in|init|internal|interface|nameof|namespace|not|notnull|operator|or|out|override|params|partial|private|protected|public|readonly|record|ref|remove|required|scoped|sealed|set|sizeof|stackalloc|static|struct|typeof|unsafe|using|value|virtual|volatile|yield)\\b" # LINQ-only keywords (ones that cannot be used outside of a LINQ query - lots others can) - statement: "\\b(from|where|select|group|info|orderby|join|let|in|on|equals|by|ascending|descending)\\b" - special: "\\b(break|continue)\\b" - constant.bool: "\\b(true|false)\\b" - symbol.operator: "[\\-+/*=<>?:!~%&|]" - constant.number: "\\b([0-9._]+|0x[A-Fa-f0-9_]+|0b[0-1_]+)[FL]?\\b" - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\([btnfr]|'|\\\"|\\\\)" - constant.specialChar: "\\\\u[A-Fa-f0-9]{4}" - constant.string: start: "'" end: "'" skip: "\\\\." rules: - constant.specialChar: "\\\\([btnfr]|'|\\\"|\\\\)" - constant.specialChar: "\\\\u[A-Fa-f0-9]{4}" - comment: start: "//" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" - comment: start: "/\\*" end: "\\*/" rules: - todo: "(TODO|XXX|FIXME):?" micro-2.0.14/runtime/syntax/css.yaml0000664000175000017510000001553714663411671016751 0ustar nileshnileshfiletype: css detect: filename: "\\.(css|scss)$" rules: # Classes and IDs - statement: "(?i)." # - normal: # start: "\\{" # end: "\\}" # rules: [] # css commands - type: "(align-content|align-items|alignment-baseline|align-self|all|animation|animation-delay|animation-direction|animation-duration|animation-fill-mode|animation-iteration-count|animation-name|animation-play-state|animation-timing-function|appearance|azimuth|backface-visibility|background|background-attachment|background-blend-mode|background-clip|background-color|background-image|background-origin|background-position|background-repeat|background-size|baseline-shift|bookmark-label|bookmark-level|bookmark-state|border|border-bottom|border-bottom-color|border-bottom-left-radius|border-bottom-right-radius|border-bottom-style|border-bottom-width|border-boundary|border-collapse|border-color|border-image|border-image-outset|border-image-repeat|border-image-slice|border-image-source|border-image-width|border-left|border-left-color|border-left-style|border-left-width|border-radius|border-right|border-right-color|border-right-style|border-right-width|border-spacing|border-style|border-top|border-top-color|border-top-left-radius|border-top-right-radius|border-top-style|border-top-width|border-width|bottom|box-decoration-break|box-shadow|box-sizing|box-snap|box-suppress|break-after|break-before|break-inside|caption-side|caret|caret-animation|caret-color|caret-shape|chains|clear|clip|clip-path|clip-rule|color|color-interpolation-filters|column-count|column-fill|column-gap|column-rule|column-rule-color|column-rule-style|column-rule-width|columns|column-span|column-width|content|continue|counter-increment|counter-reset|counter-set|cue|cue-after|cue-before|cursor|direction|display|dominant-baseline|elevation|empty-cells|filter|flex|flex-basis|flex-direction|flex-flow|flex-grow|flex-shrink|flex-wrap|float|float-defer|float-offset|float-reference|flood-color|flood-opacity|flow|flow-from|flow-into|font|font-family|font-feature-settings|font-kerning|font-language-override|font-size|font-size-adjust|font-stretch|font-style|font-synthesis|font-variant|font-variant-alternates|font-variant-caps|font-variant-east-asian|font-variant-ligatures|font-variant-numeric|font-variant-position|font-weight|footnote-display|footnote-policy|glyph-orientation-vertical|grid|grid-area|grid-auto-columns|grid-auto-flow|grid-auto-rows|grid-column|grid-column-end|grid-column-gap|grid-column-start|grid-gap|grid-row|grid-row-end|grid-row-gap|grid-row-start|grid-template|grid-template-areas|grid-template-columns|grid-template-rows|hanging-punctuation|height|hyphenate-character|hyphenate-limit-chars|hyphenate-limit-last|hyphenate-limit-lines|hyphenate-limit-zone|hyphens|image-orientation|image-rendering|image-resolution|initial-letter|initial-letter-align|initial-letter-wrap|isolation|justify-content|justify-items|justify-self|left|letter-spacing|lighting-color|line-break|line-grid|line-height|line-snap|list-style|list-style-image|list-style-position|list-style-type|margin|margin-bottom|margin-left|margin-right|margin-top|marker|marker-end|marker-knockout-left|marker-knockout-right|marker-mid|marker-pattern|marker-segment|marker-side|marker-start|marquee-direction|marquee-loop|marquee-speed|marquee-style|mask|mask-border|mask-border-mode|mask-border-outset|mask-border-repeat|mask-border-slice|mask-border-source|mask-border-width|mask-clip|mask-composite|mask-image|mask-mode|mask-origin|mask-position|mask-repeat|mask-size|mask-type|max-height|max-lines|max-width|min-height|min-width|mix-blend-mode|motion|motion-offset|motion-path|motion-rotation|nav-down|nav-left|nav-right|nav-up|object-fit|object-position|offset-after|offset-before|offset-end|offset-start|opacity|order|orphans|outline|outline-color|outline-offset|outline-style|outline-width|overflow|overflow-style|overflow-wrap|overflow-x|overflow-y|padding|padding-bottom|padding-left|padding-right|padding-top|page|page-break-after|page-break-before|page-break-inside|pause|pause-after|pause-before|perspective|perspective-origin|pitch|pitch-range|play-during|polar-anchor|polar-angle|polar-distance|polar-origin|position|presentation-level|quotes|region-fragment|resize|rest|rest-after|rest-before|richness|right|rotation|rotation-point|ruby-align|ruby-merge|ruby-position|running|scroll-behavior|scroll-snap-align|scroll-snap-margin|scroll-snap-margin-block|scroll-snap-margin-block-end|scroll-snap-margin-block-start|scroll-snap-margin-bottom|scroll-snap-margin-inline|scroll-snap-margin-inline-end|scroll-snap-margin-inline-start|scroll-snap-margin-left|scroll-snap-margin-right|scroll-snap-margin-top|scroll-snap-padding|scroll-snap-padding-block|scroll-snap-padding-block-end|scroll-snap-padding-block-start|scroll-snap-padding-bottom|scroll-snap-padding-inline|scroll-snap-padding-inline-end|scroll-snap-padding-inline-start|scroll-snap-padding-left|scroll-snap-padding-right|scroll-snap-padding-top|scroll-snap-type|shape-image-threshold|shape-inside|shape-margin|shape-outside|size|speak|speak-as|speak-header|speak-numeral|speak-punctuation|speech-rate|stress|string-set|stroke|stroke-alignment|stroke-dashadjust|stroke-dasharray|stroke-dashcorner|stroke-dashoffset|stroke-linecap|stroke-linejoin|stroke-miterlimit|stroke-opacity|stroke-width|table-layout|tab-size|text-align|text-align-all|text-align-last|text-combine-upright|text-decoration|text-decoration-color|text-decoration-line|text-decoration-skip|text-decoration-style|text-emphasis|text-emphasis-color|text-emphasis-position|text-emphasis-style|text-indent|text-justify|text-orientation|text-overflow|text-shadow|text-space-collapse|text-space-trim|text-spacing|text-transform|text-underline-position|text-wrap|top|transform|transform-box|transform-origin|transform-style|transition|transition-delay|transition-duration|transition-property|transition-timing-function|unicode-bidi|user-select|vertical-align|visibility|voice-balance|voice-duration|voice-family|voice-pitch|voice-range|voice-rate|voice-stress|voice-volume|volume|white-space|widows|width|will-change|word-break|word-spacing|word-wrap|wrap-after|wrap-before|wrap-flow|wrap-inside|wrap-through|writing-mode|z-index):" # - default: # start: ":" # end: "[;^\\{]" # rules: [] - special: "!important" - identifier: ":active|:focus|:hover|:link|:visited|:link|:after|:before|$" - special: "(\\{|\\}|\\(|\\)|\\;|:|\\]|~|<|>|,)" # SCSS Varaibles - statement: "@import|@mixin|@extend" # Strings - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\." - constant.string: start: "'" end: "'" skip: "\\\\." rules: - constant.specialChar: "\\\\." - special: "\"|'" # Comments & TODOs - comment: start: "\\/\\*" end: "\\*\\/" rules: - todo: "(TODO|XXX|FIXME):?" micro-2.0.14/runtime/syntax/csx.yaml0000664000175000017510000000026314663411671016744 0ustar nileshnileshfiletype: csharp-script detect: filename: "\\.csx$" header: "^#!.*/(env +)?dotnet-script( |$)" rules: - include: "csharp" - preproc: "\\B(\\#!|\\#[r|load|]+\\b)" micro-2.0.14/runtime/syntax/cuda.yaml0000664000175000017510000000545614663411671017074 0ustar nileshnileshfiletype: cuda detect: filename: "(\\.cu[h]?$)" rules: - identifier: "\\b[A-Z_][0-9A-Z_]*\\b" - type: "\\b(float|double|bool|char|int|short|long|enum|void|struct|union|typedef|(un)?signed|inline)\\b" - type: "\\b(((s?size)|((u_?)?int(8|16|32|64|ptr))|char(8|16|32))_t|wchar_t)\\b" - type: "\\b[a-z_][0-9a-z_]+(_t|_T)\\b" - type: "\\b(final|override)\\b" - type.keyword: "\\b(auto|volatile|const(expr|eval|init)?|mutable|register|thread_local|static|extern|decltype|explicit|virtual)\\b" - statement: "\\b(class|namespace|template|typename|this|friend|using|public|protected|private|noexcept)\\b" - statement: "\\b(concept|requires)\\b" - statement: "\\b(import|export|module)\\b" - statement: "\\b(for|if|while|do|else|case|default|switch)\\b" - statement: "\\b(try|throw|catch|operator|new|delete|static_assert)\\b" - statement: "\\b(goto|continue|break|return)\\b" - preproc: "^[[:space:]]*#[[:space:]]*(define|pragma|include|(un|ifn?)def|endif|el(if|se)|if|warning|error)|_Pragma" # Conditionally-supported/extension keywords - statement: "\\b(asm|fortran)\\b" # GCC builtins - statement: "(__attribute__[[:space:]]*\\(\\([^)]*\\)\\)|__(aligned|asm|builtin|hidden|inline|packed|restrict|section|typeof|weak)__)" # CUDA specific keywords - statement: "__(global|device|host|shared)__" # Operator Color - symbol.operator: "[-+*/%=<>.:;,~&|^!?]|\\b(sizeof|alignof|typeid|(and|or|xor|not)(_eq)?|bitor|compl|bitand|(const|dynamic|reinterpret|static)_cast)\\b" # Parenthetical Color - symbol.brackets: "[(){}]|\\[|\\]" # Integer Literals - constant.number: "(\\b([1-9][0-9']*|0[0-7']*|0[Xx][0-9a-fA-F']+|0[Bb][01]+)([Uu]?[Ll][Ll]?|[Ll][Ll]?[Uu]?)?\\b)" # Decimal Floating-point Literals - constant.number: "(\\b(([0-9']*[.][0-9']+|[0-9']+[.][0-9']*)([Ee][+-]?[0-9']+)?|[0-9']+[Ee][+-]?[0-9']+)[FfLl]?\\b)" # Hexadecimal Floating-point Literals - constant.number: "(\\b0[Xx]([0-9a-zA-Z']*[.][0-9a-zA-Z']+|[0-9a-zA-Z']+[.][0-9a-zA-Z']*)[Pp][+-]?[0-9']+[FfLl]?\\b)" - constant.bool: "(\\b(true|false|NULL|nullptr)\\b)" - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\([\"'abfnrtv\\\\]|[0-3]?[0-7]{1,2}|x[0-9A-Fa-f]{1,2}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})" - constant.string: start: "'" end: "'" skip: "\\\\." rules: - error: "..+" - constant.specialChar: "\\\\([\"'abfnrtv\\\\]|[0-3]?[0-7]{1,2}|x[0-9A-Fa-f]{1,2}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})" - comment: start: "//" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" - comment: start: "/\\*" end: "\\*/" rules: - todo: "(TODO|XXX|FIXME):?" micro-2.0.14/runtime/syntax/cython.yaml0000664000175000017510000000245314663411671017456 0ustar nileshnileshfiletype: cython detect: filename: "\\.pyx$|\\.pxd$|\\.pyi$" rules: # Python Keyword Color - statement: "\\b(and|as|assert|class|def|DEF|del|elif|ELIF|else|ELSE|except|exec|finally|for|from|global|if|IF|import|in|is|lambda|map|not|or|pass|print|raise|try|while|with|yield)\\b" - special: "\\b(continue|break|return)\\b" # Cython Keyword Color - identifier.macro: "\\b(cdef|cimport|cpdef|cppclass|ctypedef|extern|include|namespace|property|struct)\\b" - type: "\\b(bint|char|double|int|public|void|unsigned)\\b" # Operator Color - symbol: "[.:;,+*|=!\\%]|<|>|/|-|&" # Parenthetical Color - symbol.brackets: "[(){}]|\\[|\\]" - constant.string: start: "\"\"\"" end: "\"\"\"" rules: - constant.specialChar: "\\\\." - constant.string: start: "'''" end: "'''" rules: - constant.specialChar: "\\\\." - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\." - constant.string: start: "'" end: "'" skip: "\\\\." rules: - constant.specialChar: "\\\\." - comment: start: "#" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" micro-2.0.14/runtime/syntax/d.yaml0000664000175000017510000001035214663411671016372 0ustar nileshnileshfiletype: d detect: filename: "\\.(d(i|d)?)$" rules: # Operators and punctuation - statement: "(\\*|/|%|\\+|-|>>|<<|>>>|&|\\^(\\^)?|\\||~)?=" - statement: "\\.\\.(\\.)?|!|\\*|&|~|\\(|\\)|\\[|\\]|\\\\|/|\\+|-|%|<|>|\\?|:|;" # Octal integer literals are deprecated - error: "(0[0-7_]*)(L[uU]?|[uU]L?)?" # Decimal integer literals - constant.number: "([0-9]|[1-9][0-9_]*)(L[uU]?|[uU]L?)?\\b" # Binary integer literals - constant: "(0[bB][01_]*)(L[uU]?|[uU]L?)?" # Decimal float literals - constant.number: "[0-9][0-9_]*\\.([0-9][0-9_]*)([eE][+-]?([0-9][0-9_]*))?[fFL]?i?" - constant.number: "[0-9][0-9_]*([eE][+-]?([0-9][0-9_]*))[fFL]?i?" - constant.number: "[^.]\\.([0-9][0-9_]*)([eE][+-]?([0-9][0-9_]*))?[fFL]?i?" - constant.number: "[0-9][0-9_]*([fFL]?i|[fF])" # Hexadecimal integer literals - constant.number: "(0[xX]([0-9a-fA-F][0-9a-fA-F_]*|[0-9a-fA-F_]*[0-9a-fA-F]))(L[uU]?|[uU]L?)?" # Hexadecimal float literals - constant.number: "0[xX]([0-9a-fA-F][0-9a-fA-F_]*|[0-9a-fA-F_]*[0-9a-fA-F])(\\.[0-9a-fA-F][0-9a-fA-F_]*|[0-9a-fA-F_]*[0-9a-fA-F])?[pP][+-]?([0-9][0-9_]*)[fFL]?i?" - constant.number: "0[xX]\\.([0-9a-fA-F][0-9a-fA-F_]*|[0-9a-fA-F_]*[0-9a-fA-F])[pP][+-]?([0-9][0-9_]*)[fFL]?i?" # Character literals - constant.string: start: "'" end: "'" skip: "\\\\." rules: - constant.specialChar: "\\\\." # Keywords # a-e - statement: "\\b(abstract|alias|align|asm|assert|auto|body|break|case|cast|catch|class|const|continue|debug|default|delegate|do|else|enum|export|extern)\\b" # f-l - statement: "\\b(false|final|finally|for|foreach|foreach_reverse|function|goto|if|immutable|import|in|inout|interface|invariant|is|lazy)\\b" # m-r - statement: "\\b(macro|mixin|module|new|nothrow|null|out|override|package|pragma|private|protected|public|pure|ref|return)\\b" # s-w - statement: "\\b(scope|shared|static|struct|super|switch|synchronized|template|this|throw|true|try|typeid|typeof|union|unittest|version|while|with)\\b" # __ - statement: "\\b(__FILE__|__MODULE__|__LINE__|__FUNCTION__|__PRETTY_FUNCTION__|__gshared|__traits|__vector|__parameters)\\b" # Deprecated keywords - error: "\\b(delete|deprecated|typedef|volatile)\\b" # Primitive types - type: "\\b(bool|byte|cdouble|cent|cfloat|char|creal|dchar|double|float|idouble|ifloat|int|ireal|long|real|short|ubyte|ucent|uint|ulong|ushort|void|wchar)\\b" # Globally defined symbols - type: "\\b(string|wstring|dstring|size_t|ptrdiff_t)\\b" # Special tokens - constant: "\\b(__DATE__|__EOF__|__TIME__|__TIMESTAMP__|__VENDOR__|__VERSION__)\\b" # String literals # DoubleQuotedString - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\." # WysiwygString - constant.string: start: "r\"" end: "\"" rules: - constant.specialChar: "\\\\." - constant.string: start: "`" end: "`" rules: - constant.specialChar: "\\\\." # HexString - constant.string: start: "x\"" end: "\"" rules: - constant.specialChar: "\\\\." # DelimitedString - constant.string: start: "q\"\\(" end: "\\)\"" rules: - constant.specialChar: "\\\\." - constant.string: start: "q\"\\{" end: "q\"\\}" rules: - constant.specialChar: "\\\\." - constant.string: start: "q\"\\[" end: "q\"\\]" rules: - constant.specialChar: "\\\\." - constant.string: start: "q\"<" end: "q\">" rules: - constant.specialChar: "\\\\." - constant.string: start: "q\"[^({[<\"][^\"]*$" end: "^[^\"]+\"" rules: - constant.specialChar: "\\\\." - constant.string: start: "q\"([^({[<\"])" end: "\"" rules: - constant.specialChar: "\\\\." # Comments - comment: start: "//" end: "$" rules: [] - comment: start: "/\\*" end: "\\*/" rules: [] - comment: start: "/\\+" end: "\\+/" rules: [] micro-2.0.14/runtime/syntax/dart.yaml0000664000175000017510000000261214663411671017101 0ustar nileshnileshfiletype: dart detect: filename: "\\.dart$" rules: - constant.number: "\\b[-+]?([1-9][0-9]*|0[0-7]*|0x[0-9a-fA-F]+)([uU][lL]?|[lL][uU]?)?\\b" - constant.number: "\\b[-+]?([0-9]+\\.[0-9]*|[0-9]*\\.[0-9]+)([EePp][+-]?[0-9]+)?[fFlL]?" - constant.number: "\\b[-+]?([0-9]+[EePp][+-]?[0-9]+)[fFlL]?" - identifier: "[A-Za-z_][A-Za-z0-9_]*[[:space:]]*[(]" - statement: "\\b(break|case|catch|continue|default|else|finally)\\b" - statement: "\\b(for|function|get|if|in|as|is|new|return|set|switch|final|await|async|sync)\\b" - statement: "\\b(switch|this|throw|try|var|void|while|with|import|library|part|const|export)\\b" - constant: "\\b(true|false|null)\\b" - type: "\\b(List|String)\\b" - type: "\\b(int|num|double|bool)\\b" - statement: "[-+/*=<>!~%?:&|]" - constant: "/[^*]([^/]|(\\\\/))*[^\\\\]/[gim]*" - constant: "\\\\[0-7][0-7]?[0-7]?|\\\\x[0-9a-fA-F]+|\\\\[bfnrt'\"\\?\\\\]" - comment: start: "//" end: "$" rules: - todo: "TODO:?" - comment: start: "/\\*" end: "\\*/" rules: - todo: "TODO:?" - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\." - constant.string: start: "'" end: "'" skip: "\\\\." rules: - constant.specialChar: "\\\\." micro-2.0.14/runtime/syntax/default.yaml0000664000175000017510000000026414663411671017574 0ustar nileshnileshfiletype: unknown detect: filename: "" rules: # Mails - special: "[[:alnum:].%_+-]+@[[:alnum:].-]+" # URLs - identifier: "(https?|ftp|ssh)://\\S*[^])>\\s,.]" micro-2.0.14/runtime/syntax/dockerfile.yaml0000664000175000017510000000146214663411671020260 0ustar nileshnileshfiletype: dockerfile detect: filename: "((Docker|Container)file[^/]*$|\\.(docker|container)file$)" rules: ## Keywords - type.keyword: "(?i)^(FROM|MAINTAINER|RUN|CMD|LABEL|EXPOSE|ENV|ADD|COPY|ENTRYPOINT|VOLUME|USER|WORKDIR|ONBUILD|ARG|HEALTHCHECK|STOPSIGNAL|SHELL)[[:space:]]" ## Brackets & parenthesis - statement: "(\\(|\\)|\\[|\\])" ## Double ampersand - special: "&&" ## Comments - comment: start: "#" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\." - constant.string: start: "'" end: "'" skip: "\\\\." rules: - constant.specialChar: "\\\\." micro-2.0.14/runtime/syntax/dot.yaml0000664000175000017510000000166114663411671016740 0ustar nileshnileshfiletype: dot detect: filename: "\\.(dot|gv)$" rules: - type: "\\b(digraph|edge|graph|node|subgraph)\\b" - statement: "\\b(arrow(head|size|tail)|(bg|fill|font)?color|center|constraint|decorateP|dir|distortion|font(name|size)|head(clip|label)|height|label(angle|distance|font(color|name|size))?|layer(s)?|margin|mclimit|minlen|name|nodesep|nslimit|ordering|orientation|page(dir)?|peripheries|port_label_distance|rank(dir|sep)?|ratio|regular|rotate|same(head|tail)|shape(file)?|sides|size|skew|style|tail(clip|label)|URL|weight|width)\\b" - symbol: "=|->|--" - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\." - comment: start: "//" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" - comment: start: "/\\*" end: "\\*/" rules: - todo: "(TODO|XXX|FIXME):?" micro-2.0.14/runtime/syntax/elixir.yaml0000664000175000017510000000343014663411671017442 0ustar nileshnileshfiletype: elixir detect: filename: "\\.ex$|\\.exs$" rules: - statement: "\\b(abs|trunc|rem|div|round|max|min|and|or|not|throw|raise|reraise|hd|tl|in|length|elem|put_elem|destructure|to_(string|charlist)|is_(atom|binary|bitstring|boolean|float|function|integer|list|map|nil|number|pid|port|reference|tuple)|(bit|byte|map|tuple)_size|binary_part|def(delegate|exception|guard|guardp|impl|macro|macrop|module|overridable|p|protocol|struct)?|sigil_[crswCRSWDNT]|if|else|unless|cond|binding|node|self|spawn|spawn_link|spawn_monitor|send|exit|struct|get_and_update_in|get_in|put_in|pop_in|update_in|apply|inspect|make_ref|use|do|end)\\b" - statement: "\\b(alias|import|require|case|fn|receive|after|try|catch|rescue|super|quote|unquote|unquote_splicing|for|with)\\b" - constant: "\\b\\[A-Z]+\\b" - constant.number: "\\b[0-9]+\\b" - constant.string: "`[^`]*`|%x\\{[^}]*\\}" - constant.string: "\"([^\"]|(\\\\\"))*\"|%[QW]?\\{[^}]*\\}|%[QW]?\\([^)]*\\)|%[QW]?<[^>]*>|%[QW]?\\[[^]]*\\]|%[QW]?\\$[^$]*\\$|%[QW]?\\^[^^]*\\^|%[QW]?![^!]*!" - constant.string: "'([^']|(\\\\'))*'|%[qw]\\{[^}]*\\}|%[qw]\\([^)]*\\)|%[qw]<[^>]*>|%[qw]\\[[^]]*\\]|%[qw]\\$[^$]*\\$|%[qw]\\^[^^]*\\^|%[qw]![^!]*!" - symbol.brackets: "\\{|\\}|\\[|\\]|\\(|\\)" - comment: "#[^{].*$|#$" - comment.bright: "##[^{].*$|##$" - type.keyword: "\\:[a-zA-Z][a-zA-Z0-9_]*" - type.keyword: "\\b(describe|test)" - statement: "\\b(expected|assert|assert_raise|assert_in_delta|assert_received|catch_error|catch_throw|flunk|refute|refute_in_delta|refute_received)\\b" - symbol.tag: "^\\s*\\@[a-zA-Z][a-zA-Z0-9_]*\\b" - identifier.macro: "\\b(__CALLER__|__DIR__|__ENV__|__MODULE__|__aliases__|__block__|defmacro)\\b" - todo: "(XXX|TODO|FIXME|\\?\\?\\?)" - preproc.shebang: "\\W*#!.+?( |$)" micro-2.0.14/runtime/syntax/elm.yaml0000664000175000017510000000163414663411671016727 0ustar nileshnileshfiletype: elm detect: filename: "\\.elm$" rules: - statement: "\\b(as|alias|case|else|exposing|if|import|in|let|module|of|port|then|type|)\\b" - statement: "(\\=|\\:|\\->)" - type: "\\b([A-Z][A-Za-z\\d]*)\\b" - identifier: "^([a-z][A-Za-z\\d]*)\\b" - constant.string: start: "\"\"\"" end: "\"\"\"" skip: "\\\\." rules: - constant.specialChar: "\\\\." - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\." - constant.string: start: "'" end: "'" skip: "\\\\." rules: - constant.specialChar: "\\\\." - comment: start: "--" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" - comment: start: "\\{-" end: "-\\}" rules: - todo: "(TODO|XXX|FIXME):?" micro-2.0.14/runtime/syntax/erb.yaml0000664000175000017510000000456014663411671016723 0ustar nileshnileshfiletype: erb detect: filename: "\\.erb$|\\.rhtml$" rules: - error: "<[^!].*?>" - symbol.tag: "(?i)<[/]?(a(bbr|cronym|ddress|pplet|rea|rticle|side|udio)?|b(ase(font)?|d(i|o)|ig|lockquote|r)?|ca(nvas|ption)|center|cite|co(de|l|lgroup)|d(ata(list)?|d|el|etails|fn|ialog|ir|l|t)|em(bed)?|fieldset|fig(caption|ure)|font|form|(i)?frame|frameset|h[1-6]|hr|i|img|in(put|s)|kbd|keygen|label|legend|li(nk)?|ma(in|p|rk)|menu(item)?|met(a|er)|nav|no(frames|script)|o(l|pt(group|ion)|utput)|p(aram|icture|re|rogress)?|q|r(p|t|uby)|s(trike)?|samp|se(ction|lect)|small|source|span|strong|su(b|p|mmary)|textarea|time|track|u(l)?|var|video|wbr)( .*|>)*?>" - symbol.tag.extended: "(?i)<[/]?(body|div|html|head(er)?|footer|title|table|t(body|d|h(ead)?|r|foot))( .*|>)*?>" - preproc: "(?i)<[/]?(script|style)( .*|>)*?>" - special: "&[^;[[:space:]]]*;" - symbol: "[:=]" - identifier: "(alt|bgcolor|height|href|id|label|longdesc|name|onclick|onfocus|onload|onmouseover|size|span|src|style|target|type|value|width)=" - constant.string: "\"[^\"]*\"" - constant.number: "(?i)#[0-9a-fA-F]{6,6}" - constant.string.url: "(ftp(s)?|http(s)?|git|chrome)://[^ ]+" - comment: "" - preproc: "" - default: start: "<%" end: "%>" rules: [] - preproc: "<%|%>" - red: "&[^;[[:space:]]]*;" - statement: "\\b(BEGIN|END|alias|and|begin|break|case|class|def|defined\\?|do|else|elsif|end|ensure|false|for|if|in|module|next|nil|not|or|redo|rescue|retry|return|self|super|then|true|undef|unless|until|when|while|yield)\\b" - identifier.var: "(\\$|@|@@)?\\b[A-Z]+[0-9A-Z_a-z]*" - magenta: "(?i)([ ]|^):[0-9A-Z_]+\\b" - identifier.macro: "\\b(__FILE__|__LINE__)\\b" - brightmagenta: "!/([^/]|(\\\\/))*/[iomx]*|%r\\{([^}]|(\\\\}))*\\}[iomx]*" - brightblue: "`[^`]*`|%x\\{[^}]*\\}" - constant.string: "\"([^\"]|(\\\\\"))*\"|%[QW]?\\{[^}]*\\}|%[QW]?\\([^)]*\\)|%[QW]?<[^>]*>|%[QW]?\\[[^]]*\\]|%[QW]?\\$[^$]*\\$|%[QW]?\\^[^^]*\\^|%[QW]?![^!]*!" - brightgreen: "#\\{[^}]*\\}" - green: "'([^']|(\\\\'))*'|%[qw]\\{[^}]*\\}|%[qw]\\([^)]*\\)|%[qw]<[^>]*>|%[qw]\\[[^]]*\\]|%[qw]\\$[^$]*\\$|%[qw]\\^[^^]*\\^|%[qw]![^!]*!" - comment: "#[^{].*$|#$" - comment.bright: "##[^{].*$|##$" - identifier.macro: start: "<<-?'?EOT'?" end: "^EOT" rules: [] - todo: "(XXX|TODO|FIXME|\\?\\?\\?)" micro-2.0.14/runtime/syntax/erlang.yaml0000664000175000017510000000532114663411671017417 0ustar nileshnileshfiletype: erlang detect: filename: "\\.erl$" rules: - identifier: "\\b[A-Z][0-9a-z_]*\\b" # See: http://erlang.org/doc/reference_manual/data_types.html - constant.number: "\\b[0-9]+(\\.[0-9]+)?(e-?[0-9]+)?\\b" - constant.number: "\\b[0-9]{1,2}\\#[a-zA-Z0-9]+\\b" - constant.bool: "\\b(true|false)\\b" - constant.number: "\\$\\\\?\\S{1}" # See: http://erlang.org/doc/reference_manual/introduction.html - statement: "\\b(after|and|andalso|band|begin|bnot|bor|bsl|bsr|bxor|case|catch|cond|div|end|fun|if|let|not|of|or|orelse|receive|rem|try|when|xor)\\b" # See: http://erlang.org/doc/reference_manual/macros.html - preproc: "\\-(module|export|record|include|include_lib|define|undef|ifdef|ifndef|else|endif|if|elif|error|warning)\\b" - identifier.macro: "\\?[A-Z0-9_]+\\b" # See: http://erlang.org/doc/man/erlang.html - special: "\\b(ext_binary|binary|iovec|message_queue_data|time(_unit|stamp)|abs|apply|atom(_to_binary|_to_list)|binary_(part|to_atom|to_existing_atom|to_float|to_integer|to_list|to_term)|bit(_size|string_to_list)|byte_size|ceil|check_(old_code|process_code)|date|delete_module|demonitor|disconnect_node|element|erase|error|exit|float(_to_binary|_to_list)?|floor|garbage_collect|get|group_leader|halt|integer(_to_binary|to_list)|iolist_(size|to_binary)|is_(alive|atom|binary|bitstring|boolean|float|function|integer|list|map|map_key|number|pid|port|process_alive|record|reference|tuple|length)|link|list_to_(atom|binary|bitstring|existing_atom|float|integer|pid|port|ref|tuple)|load_module|make_ref|map_(get|size)|max|min|module_loaded|monitor(_node)?|nodes?|now|open_port|pid_to_list|port(_close|command|connect|control|to_list)|pre_loaded|process(_flag|_info|es)|purge_module|put|register(ed)?|round|self|setelement|size|spawn(_link|_monitor|_opt|_binary)?|statistics|trunc|tuple_(size|to_list)|unlink|unregister|whereis)\\b" # See: http://erlang.org/doc/reference_manual/data_types.html#atom - symbol: start: "'" end: "'" skip: "\\\\." rules: [] # - constant.specialChar: "%." # - constant.specialChar: "\\\\[abfnrtv'\\\"\\\\]" # - constant.specialChar: "\\\\([0-7]{3}|x[A-Fa-f0-9]{2}|u[A-Fa-f0-9]{4}|U[A-Fa-f0-9]{8})" - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "%." - constant.specialChar: "\\\\[abfnrtv'\\\"\\\\]" - constant.specialChar: "\\\\([0-7]{3}|x[A-Fa-f0-9]{2}|u[A-Fa-f0-9]{4}|U[A-Fa-f0-9]{8})" - comment: start: "\\(\\*" end: "\\*\\)" rules: - todo: "(TODO|FIXME|WONTFIX|NOTE|HACK):?" - comment: start: "%" end: "$" rules: [] micro-2.0.14/runtime/syntax/fish.yaml0000664000175000017510000000377414663411671017112 0ustar nileshnileshfiletype: fish detect: filename: "\\.fish$" header: "^#!.*/(env +)?fish( |$)" rules: # Numbers - constant: "\\b[0-9]+\\b" # Conditionals and control flow - statement: "\\b(and|begin|break|case|continue|else|end|for|function|if|in|not|or|return|select|shift|switch|while)\\b" - special: "(\\{|\\}|\\(|\\)|\\;|\\]|\\[|`|\\\\|\\$|<|>|^|!|=|&|\\|)" # Fish commands - type: "\\b(bg|bind|block|breakpoint|builtin|cd|count|command|commandline|complete|dirh|dirs|echo|emit|eval|exec|exit|fg|fish|fish_config|fish_ident|fish_pager|fish_prompt|fish_right_prompt|fish_update_completions|fishd|funced|funcsave|functions|help|history|jobs|math|mimedb|nextd|open|popd|prevd|psub|pushd|pwd|random|read|set|set_color|source|status|string|trap|type|ulimit|umask|vared)\\b" # Common linux commands - type: "\\b((g|ig)?awk|bash|dash|find|\\w{0,4}grep|kill|killall|\\w{0,4}less|make|pkill|sed|sh|tar)\\b" # Coreutils commands - type: "\\b(base64|basename|cat|chcon|chgrp|chmod|chown|chroot|cksum|comm|cp|csplit|cut|date|dd|df|dir|dircolors|dirname|du|env|expand|expr|factor|false|fmt|fold|head|hostid|id|install|join|link|ln|logname|ls|md5sum|mkdir|mkfifo|mknod|mktemp|mv|nice|nl|nohup|nproc|numfmt|od|paste|pathchk|pinky|pr|printenv|printf|ptx|pwd|readlink|realpath|rm|rmdir|runcon|seq|(sha1|sha224|sha256|sha384|sha512)sum|shred|shuf|sleep|sort|split|stat|stdbuf|stty|sum|sync|tac|tail|tee|test|time|timeout|touch|tr|true|truncate|tsort|tty|uname|unexpand|uniq|unlink|users|vdir|wc|who|whoami|yes)\\b" # Conditional flags - statement: "--[a-z-]+" - statement: "\\ -[a-z]+" - identifier: "(?i)\\$\\{?[0-9A-Z_!@#$*?-]+\\}?" - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\." - constant.string: start: "'" end: "'" skip: "\\\\." rules: [] - comment: start: "#" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" micro-2.0.14/runtime/syntax/forth.yaml0000664000175000017510000000147314663411671017275 0ustar nileshnileshfiletype: forth detect: filename: "\\.(forth|4th|fs|fs8|ft|fth|frt)$" rules: - identifier: "\\b[A-Za-z_0-9-]*\\b" - statement: "\\b(?i:(if|else|then|do|loop|case|endcase|of|endof|begin|while|repeat|until|again|unloop|leave|exit|done|next|\\?do|\\+do|\\-do|\\+loop|\\-loop|\\?leave))\\b" - statement: "(^:|;$)" - type: "\\b(?i:(variable|constant|cells))\\b" - special: "\\B[?.]\\B" #for some reason, \b and \B are inverted for symbols - constant.number: "\\b[0-9]+\\b" - constant.string: start: "\\b([Ss.]\" )" end: "\"" rules: [] - comment: start: "\\(" end: "\\)" rules: - todo: "(TODO|NOTE|XXX|FIXME):?" - comment: start: "\\\\" end: "$" rules: - todo: "(TODO|NOTE|XXX|FIXME):?" micro-2.0.14/runtime/syntax/fortran.yaml0000664000175000017510000000543614663411671017631 0ustar nileshnileshfiletype: fortran detect: filename: "\\.([Ff]|[Ff]90|[Ff]95|[Ff][Oo][Rr])$" rules: - type: "(?i)\\b(action|advance|all|allocatable|allocated|any|apostrophe)\\b" - type: "(?i)\\b(append|asis|assign|assignment|associated|bind|character|common)\\b" - type: "(?i)\\b(complex|data|default|delim|dimension|double precision)\\b" - type: "(?i)\\b(elemental|enum|enumerator|epsilon|external|file|fmt|form|format|huge)\\b" - type: "(?i)\\b(implicit|include|index|inquire|integer|intent|interface)\\b" - type: "(?i)\\b(intrinsic|iostat|kind|logical|module|none|null|only)\\\\b" - type: "(?i)\\b(operator|optional|pack|parameter|pointer|position|private)\\b" - type: "(?i)\\b(program|public|real|recl|recursive|selected_int_kind)\\b" - type: "(?i)\\b(selected_real_kind|subroutine|status|module|function|logical)\\b" - constant: "(?i)\\b(abs|achar|adjustl|adjustr|allocate|bit_size|call|char)\\b" - constant: "(?i)\\b(close|contains|count|cpu_time|cshift|date_and_time)\\b" - constant: "(?i)\\b(deallocate|digits|dot_product|eor|eoshift|iachar)\\b" - constant: "(?i)\\b(iand|ibclr|ibits|ibset|ichar|ieor|iolength|ior|ishft|ishftc)\\b" - constant: "(?i)\\b(lbound|len|len_trim|matmul|maxexponent|maxloc|maxval|merge)\\b" - constant: "(?i)\\b(minexponent|minloc|minval|mvbits|namelist|nearest|nullify)\\b" - constant: "(?i)\\b(open|pad|present|print|product|pure|quote|radix)\\b" - constant: "(?i)\\b(random_number|random_seed|range|read|readwrite|replace)\\b" - constant: "(?i)\\b(reshape|rewind|save|scan|sequence|shape|sign|size|spacing)\\b" - constant: "(?i)\\b(spread|sum|system_clock|target|transfer|transpose|trim)\\b" - constant: "(?i)\\b(ubound|unpack|verify|write|tiny|type|use|yes|true|false|not)\\b" - constant.number: "\\b([0-9]+)\\b" - statement: "(?i)\\b(.and.|case|do|else|else?if|else?where|end|end?do|end?if)\\b" - statement: "(?i)\\b(end?select|.eqv.|forall|if|lge|lgt|lle|llt|.neqv.|.not.)\\b" - statement: "(?i)\\b(or|and|repeat|select|case|then|where|while|import)\\b" - special: "(?i)\\b(continue|cycle|exit|go?to|result|return)\\b" #Operator Color - symbol.operator: "[.:;,+*|=!\\%]|/|-|>|<|&" #Parenthetical Color - symbol.bracket: "[(){}]|\\[|\\]" # Add preprocessor commands. - preproc: "^[[:space:]]*#[[:space:]]*(define|include|(un|ifn?)def|endif|el(if|se)|if|warning|error)" - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\." - constant.string: start: "'" end: "'" skip: "\\\\." rules: - constant.specialChar: "\\\\." - comment: start: "!" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" micro-2.0.14/runtime/syntax/freebsd-kernel.yaml0000664000175000017510000000041614663411671021037 0ustar nileshnileshfiletype: freebsd-kernel detect: filename: "GENERIC$" rules: - identifier: "^(cpu|ident|options|makeoptions|device|include)" - statement: "\\s\\S*" - comment: start: "#" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" micro-2.0.14/runtime/syntax/fsharp.yaml0000664000175000017510000000333414663411671017434 0ustar nileshnileshfiletype: fsharp detect: filename: "\\.fs?$" rules: - identifier: "\\b[A-Z][0-9a-z_]{2,}\\b" #declarations - statement: "\\b(let|val|method|in|and|rec|private|virtual|constraint)\\b" #structure items - type: "\\b(type|open|class|module|exception|external)\\b" #patterns - statement: "\\b(fun|function|functor|match|try|with)\\b" #patterns-modifiers - statement: "\\b(as|when|of)\\b" #conditions - statement: "\\b(if|then|else)\\b" #blocs - type: "\\b(begin|end|object|struct|sig|for|while|do|done|to|downto)\\b" #constantes - constant.bool: "\\b(true|false)\\b" #modules/classes - special: "\\b(include|inherit|initializer)\\b" #expr modifiers - special: "\\b(new|ref|mutable|lazy|assert|raise)\\b" #keywords which don't exist in ocaml - type: "\\b(base|delegate|downcast|extern|finally|fixed|global|inline|interface|internal|let!|member|namespace|null|override|private|public)\\b" - type: "\\b(return|return!|select|static|upcast|use|use!|void|yield|yield!)\\b" - constant.string: start: "'" end: "'" skip: "\\\\." rules: - constant.specialChar: "%." - constant.specialChar: "\\\\[abfnrtv'\\\"\\\\]" - constant.specialChar: "\\\\([0-7]{3}|x[A-Fa-f0-9]{2}|u[A-Fa-f0-9]{4}|U[A-Fa-f0-9]{8})" - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "%." - constant.specialChar: "\\\\[abfnrtv'\\\"\\\\]" - constant.specialChar: "\\\\([0-7]{3}|x[A-Fa-f0-9]{2}|u[A-Fa-f0-9]{4}|U[A-Fa-f0-9]{8})" - comment: start: "\\(\\*" end: "\\*\\)" rules: [] micro-2.0.14/runtime/syntax/gdscript.yaml0000664000175000017510000000445314663411671017773 0ustar nileshnileshfiletype: gdscript detect: filename: "\\.gd$" rules: # Built-in constants - constant: "\\b(INF|NAN|PI|TAU)\\b" - constant.bool: "\\b(null|true|false)\\b" # Built-in functions - identifier: "\\b(abs|acos|asin|atan|atan2|ceil|clamp|convert|cos|cosh|db2linear|decimals|deg2rad|ease|exp|float|floor|fmod|fposmod|hash|int|isinf|isnan|lerp|linear2db|load|log|max|min|nearest_po2|pow|preload|print|printerr|printraw|prints|printt|rad2deg|rand_range|rand_seed|randomize|randi|randf|range|round|seed|sin|slerp|sqrt|str|str2var|tan|typeof|var2str|weakref)\\b" # Built-in node names - identifier: "\\b(AnimationPlayer|AnimationTreePlayer|Button|Control|Engine|HTTPClient|HTTPRequest|Input|InputEvent|MainLoop|Node|Node2D|OS|SceneTree|Spatial|StreamPeer|PacketPeer|PacketPeerUDP|Timer|Tween)\\b" # Types - type: "\\b(AABB|Array|Basis|Color|Dictionary|NodePath|Object|Plane|PoolByteArray|PoolColorArray|PoolIntArray|PoolRealArray|PoolVector2Array|PoolVector3Array|Quat|Rect2|RID|String|Transform|Transform2D|Vector2|Vector3)\\b" # Definitions - identifier: "func [a-zA-Z_0-9]+" # Keywords - statement: "\\b(and|as|assert|break|breakpoint|class|const|continue|elif|else|enum|export|extends|for|func|if|in|is|map|master|mastersync|match|not|onready|or|pass|remote|remotesync|return|self|setget|slave|slavesync|signal|sync|tool|var|while|yield)\\b" # Operators - statement: "[.:;,+*|=!\\%@]|<|>|/|-|&" # Parentheses - statement: "[(){}]|\\[|\\]" # Numbers - constant: "\\b[0-9]+\\b" - constant.number: "\\b([0-9]+|0x[0-9a-fA-F]*)\\b|'.'" - comment: start: "\"\"\"" end: "\"\"\"" rules: - todo: "(TODO|XXX|FIXME):?" - comment: start: "'''" end: "'''" rules: - todo: "(TODO|XXX|FIXME):?" - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\([0-7]{3}|x[A-Fa-f0-9]{2}|u[A-Fa-f0-9]{4}|U[A-Fa-f0-9]{8})" - constant.string: start: "'" end: "'" skip: "\\\\." rules: - constant.specialChar: "\\\\([0-7]{3}|x[A-Fa-f0-9]{2}|u[A-Fa-f0-9]{4}|U[A-Fa-f0-9]{8})" - comment: start: "#" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" micro-2.0.14/runtime/syntax/gemini.yaml0000664000175000017510000000060414663411671017416 0ustar nileshnileshfiletype: gemini detect: filename: "\\.(gmi|gemini)$" rules: # link lines - constant: "^=>[[:space:]].*" # preformatted text lines - special: start: "^```" end: "^```" rules: [] # heading lines - special: "^#{1,3}.*" # unordered list items - identifier: "^\\*[[:space:]]" # quote lines - statement: "^>.*" micro-2.0.14/runtime/syntax/gentoo-ebuild.yaml0000664000175000017510000000423214663411671020704 0ustar nileshnileshfiletype: ebuild detect: filename: "\\.e(build|class)$" rules: # All the standard portage functions - identifier: "^src_(unpack|compile|install|test)|^pkg_(config|nofetch|setup|(pre|post)(inst|rm))" # Highlight bash related syntax - statement: "\\b(case|do|done|elif|else|esac|exit|fi|for|function|if|in|local|read|return|select|shift|then|time|until|while|continue|break)\\b" - statement: "(\\{|\\}|\\(|\\)|\\;|\\]|\\[|`|\\\\|\\$|<|>|!|=|&|\\|)" - statement: "-(e|d|f|r|g|u|w|x|L)\\b" - statement: "-(eq|ne|gt|lt|ge|le|s|n|z)\\b" # Highlight variables ... official portage ones in red, all others in bright red - preproc: "\\$\\{?[a-zA-Z_0-9]+\\}?" - special: "\\b(ARCH|HOMEPAGE|DESCRIPTION|IUSE|SRC_URI|LICENSE|SLOT|KEYWORDS|FILESDIR|WORKDIR|(P|R)?DEPEND|PROVIDE|DISTDIR|RESTRICT|USERLAND)\\b" - special: "\\b(S|D|T|PV|PF|P|PN|A)\\b|\\bC(XX)?FLAGS\\b|\\bLDFLAGS\\b|\\bC(HOST|TARGET|BUILD)\\b" # Highlight portage commands - identifier: "\\buse(_(with|enable))?\\b [!a-zA-Z0-9_+ -]*|inherit.*" - statement: "\\be(begin|end|conf|install|make|warn|infon?|error|log|patch|new(group|user))\\b" - statement: "\\bdie\\b|\\buse(_(with|enable))?\\b|\\binherit\\b|\\bhas\\b|\\b(has|best)_version\\b|\\bunpack\\b" - statement: "\\b(do|new)(ins|s?bin|doc|lib(\\.so|\\.a)|man|info|exe|initd|confd|envd|pam|menu|icon)\\b" - statement: "\\bdo(python|sed|dir|hard|sym|html|jar|mo)\\b|\\bkeepdir\\b" - statement: "prepall(docs|info|man|strip)|prep(info|lib|lib\\.(so|a)|man|strip)" - statement: "\\b(doc|ins|exe)into\\b|\\bf(owners|perms)\\b|\\b(exe|ins|dir)opts\\b" # Highlight common commands used in ebuilds - type: "\\bmake\\b|\\b(cat|cd|chmod|chown|cp|echo|env|export|grep|let|ln|mkdir|mv|rm|sed|set|tar|touch|unset)\\b" - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\." - constant.string: start: "'" end: "'" skip: "\\\\." rules: - constant.specialChar: "\\\\." - comment: start: "#" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" micro-2.0.14/runtime/syntax/gentoo-etc-portage.yaml0000664000175000017510000000126614663411671021656 0ustar nileshnileshfiletype: etc-portage detect: filename: "\\.(keywords|mask|unmask|use)(/.+)?$" rules: # Use flags: - constant.bool.false: "[[:space:]]+\\+?[a-zA-Z0-9_-]+" - constant.bool.true: "[[:space:]]+-[a-zA-Z0-9_-]+" # Likely version numbers: - special: "-[[:digit:]].*([[:space:]]|$)" # Accepted arches: - identifier.class: "[~-]?\\b(alpha|amd64|arm|hppa|ia64|mips|ppc|ppc64|s390|sh|sparc|x86|x86-fbsd)\\b" - identifier.class: "[[:space:]][~-]?\\*" # Categories: - statement: "^[[:space:]]*.*/" # Masking regulators: - symbol: "^[[:space:]]*(=|~|<|<=|=<|>|>=|=>)" # Comments: - comment: start: "#" end: "$" rules: [] micro-2.0.14/runtime/syntax/git-commit.yaml0000664000175000017510000000225214663411671020220 0ustar nileshnileshfiletype: git-commit detect: filename: "^(.*[\\/])?(COMMIT_EDITMSG|TAG_EDITMSG|MERGE_MSG)$" rules: # File changes - type.keyword: "#[[:space:]](deleted|modified|new file|renamed):[[:space:]].*" - type.keyword: "#[[:space:]]deleted:" - type.keyword: "#[[:space:]]modified:" - type.keyword: "#[[:space:]]new file:" - type.keyword: "#[[:space:]]renamed:" - type.keyword: "^#[[:space:]]Changes.*[:]" - type.keyword: "^#[[:space:]]Your branch and '[^']+" - type.keyword: "^#[[:space:]]Your branch and '" - type.keyword: "^#[[:space:]]On branch [^ ]+" - type.keyword: "^#[[:space:]]On branch" # Color keywords for closing issues (such as on Github) - type.keyword: "\\b(?i)((fix(es|ed)?|close(s|d)?) #[0-9]+)\\b" # Comments - comment.line: start: "^#" end: "$" rules: [] # Diffs (i.e. git commit --verbose) - default: start: "^diff --git" # Diff output puts a space before file contents on each line so this # should never match valid diff output and extend highlighting to the # end of the file end: "^ENDOFFILE" rules: - include: "patch" micro-2.0.14/runtime/syntax/git-config.yaml0000664000175000017510000000051414663411671020174 0ustar nileshnileshfiletype: git-config detect: filename: "git(config|modules)$|\\.git/config$" rules: - constant: "\\<(true|false)\\>" - type.keyword: "^[[:space:]]*[^=]*=" - constant: "^[[:space:]]*\\[.*\\]$" - constant: "\"(\\\\.|[^\"])*\"|'(\\\\.|[^'])*'" - comment: start: "#" end: "$" rules: [] micro-2.0.14/runtime/syntax/git-rebase-todo.yaml0000664000175000017510000000076514663411671021143 0ustar nileshnileshfiletype: git-rebase-todo detect: filename: "^(.*[\\/])?git\\-rebase\\-todo$" rules: # Rebase commands - statement: "^(p(ick)?|r(eword)?|e(dit)?|s(quash)?|f(ixup)?|x|exec|b(reak)?|d(rop)?|l(abel)?|t|reset|m(erge)?)\\b" # Commit IDs - identifier: "\\b([0-9a-fA-F]{7,40})\\b" # Color keywords for Github (and others) - type.keyword: "\\b(?i)((fix(es|ed)?|close(s|d)?) #[0-9]+)\\b" # Comments - comment.line: start: "^#" end: "$" rules: [] micro-2.0.14/runtime/syntax/glsl.yaml0000664000175000017510000000173514663411671017115 0ustar nileshnileshfiletype: glsl detect: filename: "\\.(frag|vert|fp|vp|glsl)$" rules: - identifier: "[A-Za-z_][A-Za-z0-9_]*[[:space:]]*[()]" - type: "\\b(void|bool|bvec2|bvec3|bvec4|int|ivec2|ivec3|ivec4|float|vec2|vec3|vec4|mat2|mat3|mat4|struct|sampler1D|sampler2D|sampler3D|samplerCUBE|sampler1DShadow|sampler2DShadow)\\b" - identifier: "\\bgl_(DepthRangeParameters|PointParameters|MaterialParameters|LightSourceParameters|LightModelParameters|LightModelProducts|LightProducts|FogParameters)\\b" - statement: "\\b(const|attribute|varying|uniform|in|out|inout|if|else|return|discard|while|for|do)\\b" - statement: "\\b(break|continue)\\b" - constant.bool: "\\b(true|false)\\b" - symbol.operator: "[-+/*=<>?:!~%&|^]" - constant.number: "\\b([0-9]+|0x[0-9a-fA-F]*)\\b" - comment: start: "//" end: "$" rules: - todo: "TODO:?" - comment: start: "/\\*" end: "\\*/" rules: - todo: "TODO:?" micro-2.0.14/runtime/syntax/gnuplot.yaml0000664000175000017510000000053314663411671017637 0ustar nileshnileshfiletype: gnuplot detect: filename: "\\.(gnu|gpi|plt|gp)$" rules: - statement: "\\b(set|unset|plot|splot|replot|if|else|do|for|while|fit)\\b" - symbol.operator: "[-+/*=<>?:!~%&|^$]" - constant.number: "\\b([0-9]+|0x[0-9a-fA-F]*)\\b" - comment: start: "#" end: "$" rules: - todo: "TODO:?" micro-2.0.14/runtime/syntax/go.yaml0000664000175000017510000000344614663411671016562 0ustar nileshnileshfiletype: go detect: filename: "\\.go$" rules: # Conditionals and control flow - special: "\\b(break|case|continue|default|go|goto|range|return|println|fallthrough)\\b" - statement: "\\b(else|for|if|switch|select)\\b" - preproc: "\\b(package|import|const|var|type|struct|func|defer|iota|make|new|copy|len|cap|panic|append|close|delete|print|recover)\\b" - symbol.operator: "[-+/*=<>!~%&|^]|:=" # Types - symbol: "(,|\\.)" - type: "\\b(u?int(8|16|32|64)?|float(32|64)|complex(64|128))\\b" - type: "\\b(uintptr|byte|rune|string|interface|bool|map|chan|error)\\b" - type.keyword: "\\b(struct)\\b" - constant.bool: "\\b(true|false|nil)\\b" # Brackets - symbol.brackets: "(\\{|\\})" - symbol.brackets: "(\\(|\\))" - symbol.brackets: "(\\[|\\])" # Numbers and strings - constant.number: "\\b([0-9]+|0x[0-9a-fA-F]*)\\b|'.'" - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "%." - constant.specialChar: "\\\\[abfnrtv'\\\"\\\\]" - constant.specialChar: "\\\\([0-7]{3}|x[A-Fa-f0-9]{2}|u[A-Fa-f0-9]{4}|U[A-Fa-f0-9]{8})" - constant.string: start: "'" end: "'" skip: "\\\\." rules: - error: "..+" - constant.specialChar: "%." - constant.specialChar: "\\\\[abfnrtv'\\\"\\\\]" - constant.specialChar: "\\\\([0-7]{3}|x[A-Fa-f0-9]{2}|u[A-Fa-f0-9]{4}|U[A-Fa-f0-9]{8})" - constant.string: start: "`" end: "`" rules: [] - comment: start: "//" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" - comment: start: "/\\*" end: "\\*/" rules: - todo: "(TODO|XXX|FIXME):?" micro-2.0.14/runtime/syntax/godoc.yaml0000664000175000017510000000040514663411671017240 0ustar nileshnilesh# godoc # example: go doc -all | micro filetype: godoc detect: filename: "\\.godoc$" header: package.*import rules: - preproc: "^[^ ].*" - comment: start: "//" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" micro-2.0.14/runtime/syntax/golo.yaml0000664000175000017510000001001614663411671017104 0ustar nileshnileshfiletype: golo detect: filename: "\\.golo$" rules: - type: "\\b(function|fun|)\\b" - type: "\\b(struct|DynamicObject|union|AdapterFabric|Adapter|DynamicVariable|Observable)\\b" - type: "\\b(list|set|array|vector|tuple|map)\\b" - type: "\\b(Ok|Error|Empty|None|Some|Option|Result|Result.ok|Result.fail|Result.error|Result.empty|Optional.empty|Optional.of)\\b" - identifier.class: "\\b(augment|pimp)\\b" - identifier.class: "\\b(interfaces|implements|extends|overrides|maker|newInstance)\\b" - identifier.class: "\\b(isEmpty|isNone|isPresent|isSome|iterator|flattened|toList|flatMap|`and|orElseGet|`or|toResult|apply|either)\\b" - identifier.class: "\\b(result|option|trying|raising|nullify|catching)\\b" - identifier.class: "\\b(promise|setFuture|failedFuture|all|any)\\b" - identifier.class: "\\b(initialize|initializeWithinThread|start|future|fallbackTo|onSet|onFail|cancel|enqueue)\\b" - identifier.class: "\\b(println|print|raise|readln|readPassword|secureReadPassword|requireNotNull|require|newTypedArray|range|reversedRange|mapEntry|asInterfaceInstance|asFunctionalInterface|isClosure|fileToText|textToFile|fileExists|currentDir|sleep|uuid|isArray|arrayTypeOf|charValue|intValue|longValue|doubleValue|floatValue|removeByIndex|box)\\b" - identifier.class: "\\b(likelySupported|reset|bold|underscore|blink|reverse_video|concealed|fg_black|fg_red|fg_green|fg_yellow|fg_blue|fg_magenta|fg_cyan|fg_white|bg_black|bg_red|bg_green|bg_yellow|bg_blue|bg_magenta|bg_cyan|bg_white|cursor_position|cursor_save_position|cursor_restore_position|cursor_up|cursor_down|cursor_forward|cursor_backward|erase_display|erase_line)\\b" - identifier.class: "\\b(emptyList|cons|lazyList|fromIter|generator|repeat|iterate)\\b" - identifier.class: "\\b(asLazyList|foldl|foldr|take|takeWhile|drop|dropWhile|subList)\\b" - identifier.class: "\\b(import)\\b" - identifier.class: "\\b(module)\\b" - identifier.class: "\\b(JSON)\\b" - identifier.class: "\\b(stringify|parse|toJSON|toDynamicObject|updateFromJSON)\\b" - identifier.class: "\\b(newInstance|define|getKey|getValue|properties|fallback)\\b" - identifier.class: "\\b(times|upTo|downTo)\\b" - identifier.class: "\\b(format|toInt|toInteger|toDouble|toFloat|toLong)\\b" - identifier.class: "\\b(head|tail|isEmpty|reduce|each|count|exists)\\b" - identifier.class: "\\b(newWithSameType|destruct|append|add|addIfAbsent|prepend|insert|last|unmodifiableView|find|filter|map|join|reverse|reversed|order|ordered|removeAt|include|exclude|remove|delete|has|contains|getOrElse|toArray)\\b" - identifier.class: "\\b(add|addTo|succ|pred|mul|neg|sub|rsub|div|rdiv|mod|rmod|pow|rpow|str|lt|gt|eq|ne|ge|le|`and|`or|`not|xor|even|odd|contains|isEmpty|`is|`isnt|`oftype|`orIfNull|fst|snd|getitem|setitem|getter|id|const|False|True|Null|curry|uncurry|unary|spreader|varargs|swapArgs|swapCurry|swapCouple|swap|invokeWith|pipe|compose|io|andThen|until|recur|cond)\\b" - identifier.class: "\\b(toUpperCase|equals|startsWith)\\b" - statement: "\\b(if|else|then|when|case|match|otherwise)\\b" - special: "\\b(with|break|continue|return)\\b" - error: "\\b(try|catch|finally|throw)\\b" - identifier: "\\b(super|this|let|var|local)\\b" - symbol.brackets: "[(){}]|\\[|\\]" - statement: "\\b(for|while|foreach|in)\\b" - constant: "\\b(and|in|is|not|or|isnt|orIfNull)\\b" - constant.bool: "\\b(true|false)\\b" - constant: "\\b(null|undefined)\\b" - symbol.operator: "[\\-+/*=<>!~%&|^]|:=" - constant.number: "\\b([0-9]+|0x[0-9a-fA-F]*)\\b|'.'" - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\." - constant.string: start: "'" end: "'" skip: "\\\\." rules: - constant.specialChar: "\\\\." - comment: start: "#" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" - comment: start: "----" end: "----" rules: - todo: "(TODO|XXX|FIXME):?" micro-2.0.14/runtime/syntax/gomod.yaml0000664000175000017510000000127314663411671017256 0ustar nileshnileshfiletype: gomod detect: filename: "go.mod" rules: # URL - type: "(^|[ \\t])+\\b([a-zA-Z0-9-]+\\.?)+(/[a-zA-Z0-9-_\\.]+)*\\b" # Keywords - special: "(^|[ \\t])+\\b(module|go)\\b" - preproc: "(^|[ \\t])+\\b(toolchain|require|exclude|replace|retract)\\b" - symbol.operator: "=>" # Brackets - type: "(\\(|\\))" # Go version - type: "(^|[ \\t])+([0-9]+\\.?)+" # Version - constant.string: "(^|[ \\t])+v([0-9]+\\.?){3}.*" - constant.number: "(^|[ \\t])+v([0-9]+\\.?){3}" - comment: start: "//" end: "$" rules: - todo: "(indirect):?" # (^|[ \\t])+ means after start of string or space or tab character micro-2.0.14/runtime/syntax/graphql.yaml0000664000175000017510000000223114663411671017602 0ustar nileshnileshfiletype: graphql detect: filename: "\\.(gql|graphql)$" rules: - type: "\\b(?:(query|mutation|subscription|type|input|scalar|fragment|schema|union|on|extends?))\\b" # scalar types - statement: "\\b(ID|Int|Float|Boolean|String|Datetime|Null)\\b" # introspection types - statement: "(__\\w+)" # parameters - statement: "((\\w+)(?:\\:([\\s]*)?)(?:\\$))" # directive locations - statement: "\\b(QUERY|MUTATION|SUBSCRIPTION|FIELD|FRAGMENT_DEFINITION|FRAGMENT_SPREAD|INLINE_FRAGMENT|SCHEMA|SCALAR|OBJECT|FIELD_DEFINITION|ARGUMENT_DEFINITION|INTERFACE|UNION|ENUM|ENUM_VALUE|INPUT_OBJECT|INPUT_FIELD_DEFINITION)\\b" # directives - constant: "(@\\w+)" # root types - constant: "\\b(Query|Mutation|Subscription|Schema|Root)\\b" # variables - special: "(\\$\\w+)" # required symbol - special: "(!)" - symbol: "(:|=|\\||\\(|\\)|\\{|\\}|\\[|\\])" - constant.bool: "\\b(true|false)\\b" - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\." - comment: start: "#" end: "$" rules: [] micro-2.0.14/runtime/syntax/groff.yaml0000664000175000017510000000140014663411671017244 0ustar nileshnileshfiletype: groff detect: filename: "\\.m[ems]$|\\.rof|\\.tmac$|^tmac." rules: - statement: "^\\.(ds|nr) [^[[:space:]]]*" - constant.specialChar: "\\\\." - constant.specialChar: "\\\\f.|\\\\f\\(..|\\\\s(\\+|\\-)?[0-9]" - constant: "(\\\\|\\\\\\\\)n(.|\\(..)" - constant: start: "(\\\\|\\\\\\\\)n\\[" end: "]" rules: [] - type: "^\\.[[:space:]]*[^[[:space:]]]*" - comment: "^\\.\\\\\".*$" - constant.string: "(\\\\|\\\\\\\\)\\*(.|\\(..)" - constant.string: start: "(\\\\|\\\\\\\\)\\*\\[" end: "]" rules: [] - constant.specialChar: "\\\\\\(.." - constant.specialChar: start: "\\\\\\[" end: "]" rules: [] - identifier.macro: "\\\\\\\\\\$[1-9]" micro-2.0.14/runtime/syntax/groovy.yaml0000664000175000017510000001001114663411671017464 0ustar nileshnileshfiletype: groovy detect: filename: "(\\.(groovy|gy|gvy|gsh|gradle)$|^[Jj]enkinsfile$)" header: "^#!.*/(env +)?groovy *$" rules: # And the style guide for constants is CONSTANT_CASE - identifier: "\\b[A-Z_$]+\\b" # The style guide for JVM languages is PascalCase for classes and interfaces - identifier.class: "\\b[A-Z][a-zA-Z0-9$]+\\b" # Primitive types - type: "\\b(byte|short|int|long|float|double|char|boolean|void)\\b" # Type-related keywords - type.keyword: "\\b(private|public|protected|static|final|var|def)\\b" # Keywords - statement: "\\b(for|while|do|if|else|switch|case|default|try|catch|finally)\\b" - statement: "\\b(break|continue|return|throw|assert)\\b" - statement: "\\b(package|import|class|interface|trait|enum|extends|implements|throws)\\b" - statement: "\\b(this|super)\\b" # Unsused, but reserved keywords - statement: "\\b(goto|const)\\b" # Operators and punctuation - symbol.operator: "[-+*/%=<>^~&|!?:;,.@]|\\b(in|is|as|instanceof|new)\\b" - symbol.brackets: "[(){}]|\\[|\\]" # Decimal integer literal - constant.number: "(?i)\\b[1-9]([_0-9]*[0-9])?[GLIDF]?\\b" # Binary integer literal - constant.number: "(?i)\\b0b[01]([01_]*[01])?[GLIDF]?\\b" # Octal integer literal - constant.number: "(?i)\\b0[0-7]([0-7_]*[0-7])?[GLIDF]?\\b" # Hexadecimal integer literal - constant.number: "(?i)\\b0x[0-9a-fA-F]([0-9a-f_]*[0-9a-fA-F])?[GLIDF]?\\b" # Floating-point literal - constant.number: "(?i)\\b[0-9]([0-9_]*[0-9])?([.][0-9]([0-9_]*[0-9])?)?(e[+-]?[0-9]([0-9_]*[0-9])?)?[DF]?\\b" - constant.bool: "\\b(true|false|null)\\b" # Annotations - identifier: "@[A-Za-z_$][A-Za-z0-9_$]*\\b" # Single-quoted strings - constant.string: start: "'" end: "'" skip: "\\\\." rules: - constant.specialChar: "\\\\([\"'bfnrst\\x24\\\\]|u[a-fA-F0-9]{4})" # This also matches the Triple-double-quoted strings region, but I can't really find a way to mitigate it, all the while still matching "" as a string correctly # Also, nesting ${} are never going to be matched correctly with just regex either, so highlighting will break if one is to nest interpolation # These two problems combined mean slight mistakes in highlighing that the user is just going to have to deal with # Double-quoted strings - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\([\"'bfnrst\\x24\\\\]|u[a-fA-F0-9]{4})" - identifier.var: "\\x24[\\w\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0100-\uFFFE]+([.][a-zA-Z0-9_\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0100-\uFFFE]+)*" - identifier: "\\x24[{].*[}]" # Triple-double-quoted strings - constant.string: start: "\"\"\"" end: "\"\"\"" skip: "\\\\." rules: - constant.specialChar: "\\\\([\"'bfnrst\\x24\\\\]|u[a-fA-F0-9]{4})" - identifier.var: "\\x24[\\w\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0100-\uFFFE]+([.][a-zA-Z0-9_\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0100-\uFFFE]+)*" - identifier: start: "[$][{]" end: "[}]" rules: [] # Triple-single-quoted strings - constant.string: start: "'''" end: "'''" skip: "\\\\." rules: - constant.specialChar: "\\\\([\"'bfnrst\\x24\\\\]|u[a-fA-F0-9]{4})" # Slashy strings are left out, because they match in unwanted places pretty much all the time # Dollar-slashy strings - constant.string: start: "[$]/" end: "/[$]" rules: [] # Single-line comments - comment: start: "//" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" # Multiline comments - comment: start: "/[*]" end: "[*]/" rules: - todo: "(TODO|XXX|FIXME):?" # Groovydoc comments - comment: start: "/[*][*]@?" end: "[*]/" rules: [] micro-2.0.14/runtime/syntax/haml.yaml0000664000175000017510000000114314663411671017066 0ustar nileshnileshfiletype: haml detect: filename: "\\.haml$" rules: - symbol: "-|=" - default: "->|=>" - constant: "([ ]|^)%[0-9A-Za-z_]+>" - special: ":[0-9A-Za-z_]+>" - type: "\\.[A-Za-z_]+>" - constant.string: "\"([^\"]|(\\\\\"))*\"|%[QW]?\\{[^}]*\\}|%[QW]?\\([^)]*\\)|%[QW]?<[^>]*>|%[QW]?\\$[^$]*\\$|%[QW]?\\^[^^]*\\^|%[QW]?![^!]*!" - constant.string: "'([^']|(\\\\'))*'|%[qw]\\{[^}]*\\}|%[qw]\\([^)]*\\)|%[qw]<[^>]*>|%[qw]\\[[^]]*\\]|%[qw]\\$[^$]*\\$|%[qw]\\^[^^]*\\^|%[qw]![^!]*!" - identifier: "#\\{[^}]*\\}" - identifier.var: "(@|@@)[0-9A-Z_a-z]+" - comment: "#[^{].*$|#$" micro-2.0.14/runtime/syntax/hare.yaml0000664000175000017510000000333614663411671017072 0ustar nileshnileshfiletype: hare detect: filename: "\\.ha$" rules: - identifier: "\\b[A-Z_][0-9A-Z_]+\\b" - type: "\\b(bool|char|str|rune|void)\\b" - type: "\\b(f32|f64|uint|int|u8|u16|u32|u64|i8|i16|i32|i64|uintptr)\\b" - statement: "\\b(case|else|for|if|switch)\\b" - statement: "\\b(continue|break|return)\\b" - special: "\\b(as|const|def|defer|enum|export|fn|is|let|match|static|struct|type|union|yield|_)\\b" - preproc: "\\b(abort|alloc|append|assert|delete|free|insert|len|nullable|offset|size)\\b" - preproc: "^use .+;" - preproc: "\\@([a-zA-Z_][0-9a-zA-Z_]+)\\b" - constant: "\\b(false|null|true)\\b" - constant.number: "\\b(0x[0-9A-Fa-f]+(i(8|16|32|64)?|u(8|16|32|64)?|z)?)\\b" - constant.number: "\\b(0o[0-7]+(i(8|16|32|64)?|u(8|16|32|64)?|z)?)\\b" - constant.number: "\\b(0b[01]+(i(8|16|32|64)?|u(8|16|32|64)?|z)?)\\b" - constant.specialChar: "\\\".*\\\"" - constant.specialChar: "`.*`" - constant.specialChar: "'([^'\\\\]|\\\\(0|a|b|f|n|r|t|v|\\\\|'|\\\"|x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8}))'" - symbol.operator: "([.:;,+*|=!\\%]|<|>|/|-|&)" - symbol.brackets: "[(){}]|\\[|\\]" - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\([\"'abfnrtv\\\\]|[0-3]?[0-7]{1,2}|x[0-9A-Fa-f]{1,2}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})" - constant.string: start: "'" end: "'" skip: "\\\\." rules: - error: "..+" - constant.specialChar: "\\\\([\"'abfnrtv\\\\]|[0-3]?[0-7]{1,2}|x[0-9A-Fa-f]{1,2}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})" - comment: start: "//" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" micro-2.0.14/runtime/syntax/haskell.yaml0000664000175000017510000000236014663411671017572 0ustar nileshnileshfiletype: haskell detect: filename: "\\.hs$" rules: # Keywords - statement: "\\b(as|case|of|class|data|default|deriving|do|forall|foreign|hiding|if|then|else|import|infix|infixl|infixr|instance|let|in|mdo|module|newtype|qualified|type|where)\\b" # Various symbols - symbol: "(\\||@|!|:|_|~|=|\\\\|;|\\(\\)|,|\\[|\\]|\\{|\\})" # Operators - symbol.operator: "(==|/=|&&|\\|\\||<|>|<=|>=)" # Various symbols - special: "(->|<-)" - symbol: "\\.|\\$" # Data constructors - constant.bool: "\\b(True|False)\\b" - constant: "\\b(Nothing|Just|Left|Right|LT|EQ|GT)\\b" # Data classes - identifier.class: "\\b(Read|Show|Enum|Eq|Ord|Data|Bounded|Typeable|Num|Real|Fractional|Integral|RealFrac|Floating|RealFloat|Monad|MonadPlus|Functor|Foldable|Additive|Zip)[ ]" # Strings - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\." # Comments - comment: start: "--" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" - comment: start: "\\{-" end: "-\\}" rules: - todo: "(TODO|XXX|FIXME):?" - identifier.micro: "undefined" micro-2.0.14/runtime/syntax/hc.yaml0000664000175000017510000000354114663411671016543 0ustar nileshnileshfiletype: hc detect: filename: "(\\.(hc|HC)$|\\.(hh|HH)$|\\.ii?$|\\.(def)$)" rules: - identifier: "\\b[A-Z_][0-9A-Z_]+\\b" - type: "\\b(F64|I8|U8|I16|U16|I32|U32|I64|U64|sizeof|enum|U0|static|extern|struct|union|class|intern|public|argc|argv|asm)\\b" - statement: "\\b(for|if|while|do|else|case|default|switch)\\b" - statement: "\\b(try|catch|throw|goto|continue|break|return)\\b" - preproc: "^[[:space:]]*#[[:space:]]*(define|pragma|include|(un|ifn?)def|endif|el(if|se)|if|help_index|ifjit|ifaot|exe)" # Operator Color - symbol.operator: "([.:;,+*|=!\\%]|<|>|/|-|&)" - symbol.brackets: "[(){}]|\\[|\\]" # Integer Constants - constant.number: "(\\b([1-9][0-9]*|0[0-7]*|0[Xx][0-9A-Fa-f]+|0[Bb][01]+)([Uu]?[Ll][Ll]?|[Ll][Ll]?[Uu]?)?\\b)" # Decimal Floating Constants - constant.number: "(\\b(([0-9]*[.][0-9]+|[0-9]+[.][0-9]*)([Ee][+-]?[0-9]+)?|[0-9]+[Ee][+-]?[0-9]+)[FfLl]?\\b)" # Hexadecimal Floating Constants - constant.number: "(\\b0[Xx]([0-9A-Za-z]*[.][0-9A-Za-z]+|[0-9A-Za-z]+[.][0-9A-Za-z]*)[Pp][+-]?[0-9]+[FfLl]?\\b)" - constant.number: "NULL" - constant.number: "TRUE" - constant.number: "FALSE" - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\([\"'abfnrtv\\\\]|[0-3]?[0-7]{1,2}|x[0-9A-Fa-f]{1,2}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})" - constant.string: start: "'" end: "'" skip: "\\\\." rules: - error: "..+" - constant.specialChar: "\\\\([\"'abfnrtv\\\\]|[0-3]?[0-7]{1,2}|x[0-9A-Fa-f]{1,2}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})" - comment: start: "//" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" - comment: start: "/\\*" end: "\\*/" rules: - todo: "(TODO|XXX|FIXME):?" micro-2.0.14/runtime/syntax/html.yaml0000664000175000017510000000670014663411671017115 0ustar nileshnileshfiletype: html detect: filename: "\\.htm[l]?$" rules: # Doctype is case-insensitive - preproc: "" # Opening tag - symbol.tag: start: "<(a|abbr|acronym|address|applet|area|article|aside|audio|b|base|bdi|bdo|big|blockquote|body|br|button|canvas|caption|center|cite|code|col|colgroup|command|datalist|dd|del|details|dfn|dialog|dir|div|dl|dt|em|embed|fieldset|figcaption|figure|font|footer|form|frame|frameset|h[1-6]|head|header|hgroup|hr|html|i|iframe|img|input|ins|kbd|keygen|label|legend|li|link|map|main|mark|menu|meta|meter|nav|noframes|noscript|object|ol|optgroup|option|output|p|param|pre|progress|q|rp|rt|ruby|s|samp|section|select|small|source|span|strike|strong|sub|summary|sup|table|tbody|td|textarea|tfoot|th|thead|time|title|tr|track|tt|u|ul|var|video|wbr)\\b" end: ">" rules: - identifier: "\\b(placeholder|style|alt|bgcolor|height|href|id|(aria|data)\\-.+|label|longdesc|name|on(click|focus|load|mouseover)|size|span|src|target|type|value|width|class|charset|content|rel|integrity|crossorigin|for|onsubmit|lang|role)\\b" - special: "\\b(required)\\b" # Match double-quote strings - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\." - constant.string.url: "((ftp(s)?|http(s)?|git|chrome)://[^\\s]+)" # Match single-quote strings - constant.string: start: "'" end: "'" skip: "\\\\." rules: - constant.specialChar: "\\\\." - constant.string.url: "((ftp(s)?|http(s)?|git|chrome)://[^\\s]+)" # Highlight the equals and any colon between words - symbol: "\\b(=|:\\b)" # Closing tag - symbol.tag: start: "" rules: # Anything in the closing tag is an error - error: "." # Reserved entities like a   and Ī - special: "(([a-zA-Z]&#[0-9]+|&[a-zA-Z]+|&#[a-zA-Z0-9]+);)" # TODO: Add `limit-rules` to both the `default` rules below once it's implemented into Micro - default: start: "" end: "" limit-group: symbol.tag rules: - include: "javascript" - default: start: "" end: "" limit-group: symbol.tag rules: - include: "css" # This weird empty comment thing is technically valid - comment: "" - comment.block: start: "" rules: - todo: "(FIXME|NOTE|TODO):?" # While technically not a "true" error, these are recommended to not be used inside a comment - error: "(\\-\\-|>)" micro-2.0.14/runtime/syntax/html4.yaml0000664000175000017510000000262314663411671017201 0ustar nileshnileshfiletype: html4 detect: filename: "\\.htm[l]?4$" header: "" rules: - error: "<[^!].*?>" - symbol.tag: "(?i)<[/]?(a(bbr|cronym|ddress|pplet|rea|rticle|side|udio)?|b(ase(font)?|d(i|o)|ig|lockquote|r)?|ca(nvas|ption)|center|cite|co(de|l|lgroup)|d(ata(list)?|d|el|etails|fn|ialog|ir|l|t)|em(bed)?|fieldset|fig(caption|ure)|font|form|(i)?frame|frameset|h[1-6]|hr|i|img|in(put|s)|kbd|keygen|label|legend|li(nk)?|ma(in|p|rk)|menu(item)?|met(a|er)|nav|no(frames|script)|o(l|pt(group|ion)|utput)|p(aram|icture|re|rogress)?|q|r(p|t|uby)|s(trike)?|samp|se(ction|lect)|small|source|span|strong|su(b|p|mmary)|textarea|time|track|u(l)?|var|video|wbr)( .*|>)*?>" - symbol.tag.extended: "(?i)<[/]?(body|div|html|head(er)?|footer|title|table|t(body|d|h(ead)?|r|foot))( .*)*?>" - preproc: "(?i)<[/]?(script|style)( .*)*?>" - special: "&[^;[[:space:]]]*;" - symbol: "[:=]" - identifier: "(alt|bgcolor|height|href|id|label|longdesc|name|on(click|focus|load|mouseover)|size|span|src|style|target|type|value|width)=" - constant.string: "\"[^\"]*\"" - constant.number: "(?i)#[0-9a-fA-F]{6,6}" - default: start: ">" end: "<" rules: [] - symbol.tag: "<|>" - constant.string.url: "(ftp(s)?|http(s)?|git|chrome)://[^ ]+" - comment: "" - preproc: "" micro-2.0.14/runtime/syntax/html5.yaml0000664000175000017510000000243514663411671017203 0ustar nileshnileshfiletype: html5 detect: filename: "\\.htm[l]?5$" header: "" rules: - error: "<[^!].*?>" - symbol.tag: "(?i)<[/]?(a|a(bbr|ddress|rea|rticle|side|udio)|b|b(ase|d(i|o)|lockquote|r|utton)|ca(nvas|ption)|center|cite|co(de|l|lgroup)|d(ata|atalist|d|el|etails|fn|ialog|l|t)|em|embed|fieldset|fig(caption|ure)|form|iframe|h[1-6]|hr|i|img|in(put|s)|kbd|keygen|label|legend|li|link|ma(in|p|rk)|menu|menuitem|met(a|er)|nav|noscript|o(bject|l|pt(group|ion)|utput)|p|param|picture|pre|progress|q|r(p|t|uby)|s|samp|se(ction|lect)|small|source|span|strong|su(b|p|mmary)|textarea|time|track|u|ul|var|video|wbr)( .*)*?>" - symbol.tag.extended: "(?i)<[/]?(body|div|html|head(er)?|footer|title|table|t(body|d|h(ead)?|r|foot))( .*)*?>" - preproc: "(?i)<[/]?(script|style)( .*)*?>" - special: "&[^;[[:space:]]]*;" - symbol: "[:=]" - identifier: "(alt|bgcolor|height|href|id|label|longdesc|name|on(click|focus|load|mouseover)|size|span|src|style|target|type|value|width)=" - constant.string: "\"[^\"]*\"" - constant.number: "(?i)#[0-9a-fA-F]{6,6}" - default: start: ">" end: "<" rules: [] - symbol.tag: "<|>" - constant.string.url: "(ftp(s)?|http(s)?|git|chrome)://[^ ]+" - comment: "" - preproc: "" micro-2.0.14/runtime/syntax/ini.yaml0000664000175000017510000000115514663411671016727 0ustar nileshnileshfiletype: ini detect: filename: "\\.(ini|desktop|lfl|override|tscn|tres)$|(mimeapps\\.list|pinforc|setup\\.cfg|project\\.godot)$|weechat/.+\\.conf$" rules: - constant.bool.true: "\\btrue\\b" - constant.bool.false: "\\bfalse\\b" - identifier: "^[[:space:]]*[^=]*=" - special: "^[[:space:]]*\\[.*\\]$" - statement: "[=;]" - constant.string: "\"(\\\\.|[^\"])*\"|'(\\\\.|[^'])*'" - comment: start: "#" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" - comment: start: ";" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" micro-2.0.14/runtime/syntax/inputrc.yaml0000664000175000017510000000061714663411671017636 0ustar nileshnileshfiletype: inputrc detect: filename: "inputrc$" rules: - constant.bool.false: "\\b(off|none)\\b" - constant.bool.true: "\\bon\\b" - preproc: "\\bset|\\$include\\b" - constant.string: "\"(\\\\.|[^\"])*\"|'(\\\\.|[^'])*'" - constant.specialChar: "\\\\.?" - comment: "(^|[[:space:]])#([^{].*)?$" - indent-char.whitespace: "[[:space:]]+$" - indent-char: " + +| + +" micro-2.0.14/runtime/syntax/java.yaml0000664000175000017510000000173114663411671017071 0ustar nileshnileshfiletype: java detect: filename: "\\.java$" rules: - type: "\\b(boolean|byte|char|double|float|int|long|new|short|this|transient|void)\\b" - statement: "\\b(break|case|catch|continue|default|do|else|finally|for|if|return|switch|throw|try|while)\\b" - type: "\\b(abstract|class|extends|final|implements|import|instanceof|interface|native|package|private|protected|public|static|strictfp|super|synchronized|throws|volatile)\\b" - constant: "\\b(true|false|null)\\b" - constant.number: "\\b[0-9]+\\b" - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\." - constant.string: start: "'" end: "'" skip: "\\\\." rules: - preproc: "..+" - constant.specialChar: "\\\\." - comment: start: "//" end: "$" rules: [] - comment: start: "/\\*" end: "\\*/" rules: [] micro-2.0.14/runtime/syntax/javascript.yaml0000664000175000017510000000544614663411671020325 0ustar nileshnileshfiletype: javascript detect: filename: "(\\.js$|\\.es[5678]?$|\\.mjs$)" header: "^#!.*/(env +)?node( |$)" rules: - constant.number: "\\b[-+]?([1-9][0-9]*|0[0-7]*|0x[0-9a-fA-F]+)([uU][lL]?|[lL][uU]?)?\\b" - constant.number: "\\b[-+]?([0-9]+\\.[0-9]*|[0-9]*\\.[0-9]+)([EePp][+-]?[0-9]+)?[fFlL]?" - constant.number: "\\b[-+]?([0-9]+[EePp][+-]?[0-9]+)[fFlL]?" #- identifier: "[A-Za-z_][A-Za-z0-9_]*[[:space:]]*[(]" # ^ this is not correct usage of the identifier color - symbol.brackets: "[(){}]|\\[|\\]" - symbol.operator: "([-+/*=<>!~%?:&|]|[.]{3})" - statement: "\\b(async|await|break|case|catch|const|continue|debugger|default)\\b" - statement: "\\b(delete|do|else|export|finally|for|function\\*?|class|extends)\\b" - statement: "\\b(get|if|import|from|in|of|instanceof|let|new|reject|resolve|return)\\b" - statement: "\\b(set|static|super|switch|this|throw|try|typeof|var|void|while|with|yield)\\b" # reserved but unassigned - error: "\\b(enum|implements|interface|package|private|protected|public)\\b" - constant: "\\b(globalThis|Infinity|null|undefined|NaN)\\b" - constant: "\\b(null|undefined|NaN)\\b" - constant: "\\b(true|false)\\b" - type: "\\b(Array|Boolean|Date|Enumerator|Error|Function|Generator|Map|Math)\\b" - type: "\\b(Number|Object|Promise|Proxy|Reflect|RegExp|Set|String|Symbol|WeakMap|WeakSet)\\b" - type: "\\b(BigInt64Array|BigUint64Array|Float32Array|Float64Array|Int16Array)\\b" # - constant: "/[^*]([^/]|(\\\\/))*[^\\\\]/[gim]*" - constant: "\\\\[0-7][0-7]?[0-7]?|\\\\x[0-9a-fA-F]+|\\\\[bfnrt'\"\\?\\\\]" - comment: "^#!.*/(env +)?node( |$)" - identifier: "\\b(alert|decodeURI|decodeURIComponent|document|encodeURI|encodeURIComponent|escape|eval|isFinite|isNaN|parseFloat|parseInt|unescape|uneval|window)\\b" - identifier: "\\b(Intl|WebAssembly)\\b" - identifier: "\\b(Arguments)\\b" - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\." - constant.string: start: "'" end: "'" skip: "\\\\." rules: - constant.specialChar: "\\\\." - constant.string: start: "`" end: "`" rules: - constant.specialChar: "\\\\." - identifier: "\\x24\\{.*?\\}" - constant.bool: "\\b(true|false)\\b" - constant.bool.false: "\\b(false)\\b" - constant.bool.true: "\\b(true)\\b" - comment: start: "//" end: "$" rules: - todo: "(TODO|XXX|FIXME)" - comment: start: "/\\*" end: "\\*/" skip: "\\\\." rules: - constant.specialChar: "\\\\." # function documentation - identifier: "\\s\\*\\s.*" - todo: "(TODO|XXX|FIXME)" micro-2.0.14/runtime/syntax/jinja2.yaml0000664000175000017510000000210714663411671017323 0ustar nileshnileshfiletype: jinja2 rules: - include: "html" - special: "({{|}}|{%-?|-?%})" - default: start: "({%-?|{{)" end: "(-?%}|}})" limit-group: special rules: - include: "python" - statement: "\\b(ignore missing|with(out)? context|block|call|endblock|endcall|endfilter|endfor|endmacro|endraw|endset|extends|filter|for|include|macro|raw|recursive|scoped|set)\\b" - identifier.builtinfunc: "\\b(attr|batch|capitalize|center|count|d|default|dictsort|e|escape|filesizeformat|first|forceescape|groupby|indent|join|last|length|lower|pprint|random|reject|rejectattr|replace|reverse|safe|select|selectattr|striptags|title|tojson|trim|truncate|unique|upper|urlencode|urlize|wordcount|wordwrap|xmlattr)\\b" - identifier.builtintest: "\\b(callable|defined|divisibleby|eq|equalto|escaped|even|ge|gt|iterable|le|lower|lt|mapping|ne|none|number|odd|sameas|sequence|string|undefined|upper)\\b" - identifier.defaultglobal: "\\b(lipsum|cycler|joiner|namespace)\\b" - comment: start: "{#" end: "#}" rules: [] micro-2.0.14/runtime/syntax/json.yaml0000664000175000017510000000203414663411671017116 0ustar nileshnileshfiletype: json detect: filename: "\\.json$" header: "^\\{$" rules: - constant.number: "\\b[-+]?([1-9][0-9]*|0[0-7]*|0x[0-9a-fA-F]+)([uU][lL]?|[lL][uU]?)?\\b" - constant.number: "\\b[-+]?([0-9]+\\.[0-9]*|[0-9]*\\.[0-9]+)([EePp][+-]?[0-9]+)?[fFlL]?" - constant.number: "\\b[-+]?([0-9]+[EePp][+-]?[0-9]+)[fFlL]?" - constant: "\\b(null)\\b" - constant: "\\b(true|false)\\b" - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\." - constant.string: start: "'" end: "'" skip: "\\\\." rules: - constant.specialChar: "\\\\." - statement: "\\\"(\\\\\"|[^\"])*\\\"[[:space:]]*:\" \"'(\\'|[^'])*'[[:space:]]*:" - constant: "\\\\u[0-9a-fA-F]{4}|\\\\[bfnrt'\"/\\\\]" - comment: start: "//" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" - comment: start: "/\\*" end: "\\*/" rules: - todo: "(TODO|XXX|FIXME):?" micro-2.0.14/runtime/syntax/jsonnet.yaml0000664000175000017510000000627514663411671017640 0ustar nileshnileshfiletype: jsonnet detect: filename: "\\.jsonnet$" # Spec: https://jsonnet.org/ref/spec.html rules: # built-in objects # FIXME: $ won't match - constant: "\\b(self|\\$|super)\\b" # boolean constants - constant.bool: "\\b(null|true|false)\\b" # the standard library - identifier: "\\bstd\\.(extVar|thisFile|type|length|objectHas|objectFields|objectHasAll|objectFieldsAll|prune|mapWithKey|abs|sign|max|min|pow|exp|log|exponent|mantissa|floor|ceil|sqrt|sin|cos|tan|asin|acos|atan|mod|assertEqual|toString|codepoint|char|substr|findSubstr|startsWith|endsWith|split|splitLimit|strReplace|asciiUpper|asciiLower|stringChars|format|escapeStringDollars|escapeStringPython|parseInt|parseOctal|parseHex|parseJson|encodeUTF8|decodeUTF8|manifestIni|manifestPython|manifestPythonVars|manifestJsonEx|manifestYamlDoc|manifestYamlStream|manifestXmlJsonml|makeArray|count|find|map|mapWithIndex|filterMap|filter|foldl|foldr|range|join|lines|flattenArrays|sort|uniq|set|setInter|setUnion|setDiff|setMember|base64|base64DecodeBytes|base64Decode|md5|mergePatch|trace)\\b" # unquoted object keys - type: "[_a-zA-Z][_a-zA-Z0-9]*\\s*:" # object key separator - statement: ":" # keywords - statement: "\\b(assert|else|error|for|function|if|import|importstr|in|local|tailstrict|then)\\b" # operators - symbol.operator: "([.;,+*|=!\\%]|<|>|/|-|&)" # parentheses - symbol.brackets: "([(){}]|\\[|\\])" # numbers - constant.number: "\\b(0|([1-9][0-9]*))(\\.[0-9]+)?([eE][\\+-]?[0-9]+)?\\b" # double-quoted string - constant.string: start: "\"" end: "\"" skip: "\\\\\"" rules: - constant.specialChar: "\\\\u[0-9a-fA-F]{4}|\\\\[bfnrt'\"/\\\\]" # single-quoted string - constant.string: start: "'" end: "'" skip: "\\\\'" rules: - constant.specialChar: "\\\\u[0-9a-fA-F]{4}|\\\\[bfnrt'\"/\\\\]" # double-quoted verbatim string - constant.string: start: "@\"" end: "\"" skip: "\\\\\"" rules: - constant.specialChar: "\\\\\"" # single-quoted verbatim string - constant.string: start: "@'" end: "'" skip: "\\\\'" rules: - constant.specialChar: "\\\\'" # block string - constant.string: # FIXME: # This isn't quite right. # The spec says this: # beginning with |||, followed by optional whitespace and a new-line. # The next non-blank line must be prefixed with some non-zero length # whitespace W. The block ends at the first subsequent line that does # not begin with W, and it is an error if this line does not contain # some optional whitespace followed by |||. # We need to match ^(\s+) on the first non-blank line after ||| # Then we need to skip ^\1.*$ start: "\\|\\|\\| *$" end: "^ *\\|\\|\\|" rules: [] # multi-line comment - comment: start: "/\\*" end: "\\*/" rules: - todo: "(TODO|XXX|FIXME):?" # single-line comment - comment: start: "#|(//)" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" micro-2.0.14/runtime/syntax/julia.yaml0000664000175000017510000000334714663411671017261 0ustar nileshnileshfiletype: julia detect: filename: "\\.jl$" header: "^#!.*/(env +)?julia( |$)" rules: # built-in objects - constant.bool: "\\b(true|false)\\b" - constant: "\\b(nothing|missing)\\b" # built-in attributes - constant: "__[A-Za-z0-9_]+__" # definitions - identifier: "[A-Za-z_][A-Za-z0-9_]*[[:space:]]*[(]" # keywords - statement: "\\b(baremodule|begin|break|catch|const|continue|do|else|elseif|end|export|finally|for|function|global|if|import|let|local|macro|module|public|quote|return|struct|try|using|while)\\b" - statement: "\\b(abstract\\s+type|primitive\\s+type|mutable\\s+struct)\\b" # decorators - identifier.macro: "@[A-Za-z0-9_]+" # operators - symbol.operator: "[:+*|=!%~<>/\\-?&\\\\÷∈∉∘]|\\b(in|isa|where)\\b" # for some reason having ^ in the same regex with the other operators broke things - symbol.operator: "\\^" # parentheses - symbol.brackets: "([(){}]|\\[|\\])" # numbers - constant.number: "\\b([0-9]+(_[0-9]+)*|0x[0-9a-fA-F]+(_[0-9a-fA-F]+)*|0b[01]+(_[01]+)*|0o[0-7]+(_[0-7]+)*|Inf(16|32|64)?|NaN(16|32|64)?)\\b" - constant.string: start: "\"\"\"" end: "\"\"\"" rules: [] - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\([\"'abfnrtv\\\\]|[0-3]?[0-7]{1,2}|x[0-9A-Fa-f]{1,2}|u[0-9A-Fa-f]{1,4}|U[0-9A-Fa-f]{1,8})" # Lifted from Rust's syntax highlighting - constant.string: "'(\\\\.|.)'" - constant.string: start: "'\"" end: "'" rules: [] - comment: start: "#=" end: "=#" rules: [] - comment: start: "#" end: "$" rules: [] micro-2.0.14/runtime/syntax/justfile.yaml0000664000175000017510000000323414663411671017775 0ustar nileshnilesh# For more information, see https://github.com/casey/just filetype: 'justfile' detect: filename: "(^\\.?[Jj]ustfile|\\.just)$" header: "^#!.*/(env +)?[bg]?just --justfile" rules: - preproc: "\\<(ifeq|ifdef|ifneq|ifndef|else|endif)\\>" - statement: "^(export|include|override)\\>" - symbol.operator: "^[^:= ]+:" - symbol.operator: "([=,%]|\\+=|\\?=|:=|&&|\\|\\|)" - statement: "\\$\\((abspath|addprefix|addsuffix|and|basename|call|dir)[[:space:]]" - statement: "\\$\\((error|eval|filter|filter-out|findstring|firstword)[[:space:]]" - statement: "\\$\\((flavor|foreach|if|info|join|lastword|notdir|or)[[:space:]]" - statement: "\\$\\((origin|patsubst|realpath|shell|sort|strip|suffix)[[:space:]]" - statement: "\\$\\((value|warning|wildcard|word|wordlist|words)[[:space:]]" # default functions - probably shouldn't be overwritten by assignment - statement: "\\b(arch|os|os_family|env_var|invocation_directory|justfile|justfile_directory|just_executable|lowercase|quote|replace|trim|trim_end|trim_end|trim_end_match|trim_end_matches|trim_start|trim_start_match|trim_start_matches|uppercase)\\b" - identifier: "^.+:" - identifier: "[()$]" - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\." - constant.string: start: "'" end: "'" skip: "\\\\." rules: - constant.specialChar: "\\\\." - identifier: "\\$+(\\{[^} ]+\\}|\\([^) ]+\\))" - identifier: "\\$[@^<*?%|+]|\\$\\([@^<*?%+-][DF]\\)" - identifier: "\\$\\$|\\\\.?" - comment: start: "#" end: "$" rules: [] micro-2.0.14/runtime/syntax/keymap.yaml0000664000175000017510000000114414663411671017434 0ustar nileshnileshfiletype: keymap detect: filename: "\\.(k|key)?map$|Xmodmap$" rules: - statement: "\\b(add|clear|compose|keycode|keymaps|keysym|remove|string)\\b" - statement: "\\b(control|alt|shift)\\b" - constant.number: "\\b[0-9]+\\b" - special: "=" - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\." - constant.string: start: "'" end: "'" skip: "\\\\." rules: - constant.specialChar: "\\\\." - comment: start: "^!" end: "$" rules: [] micro-2.0.14/runtime/syntax/kickstart.yaml0000664000175000017510000000117114663411671020145 0ustar nileshnileshfiletype: kickstart detect: filename: "\\.ks$|\\.kickstart$" rules: - special: "%[a-z]+" - statement: "^[[:space:]]*(install|cdrom|text|graphical|volgroup|logvol|reboot|timezone|lang|keyboard|authconfig|firstboot|rootpw|user|firewall|selinux|repo|part|partition|clearpart|bootloader)" - constant: "--(name|mirrorlist|baseurl|utc)(=|\\>)" - statement: "\\$(releasever|basearch)\\>" - brightblack: "^@[A-Za-z][A-Za-z-]*" - brightred: "^-@[a-zA-Z0-9*-]+" - red: "^-[a-zA-Z0-9*-]+" - comment: "(^|[[:space:]])#([^{].*)?$" - indent-char.whitespace: "[[:space:]]+$" - indent-char: " + +| + +" micro-2.0.14/runtime/syntax/kotlin.yaml0000664000175000017510000000342314663411671017450 0ustar nileshnileshfiletype: kotlin detect: filename: "\\.kts?$" rules: # Operators - symbol.operator: ([.:;,+*|=!?\\%]|<|>|/|-|&) # Statements Keywords - statement: \b(as|by|class|constructor|companion|const|fun|import|in|infix|interface|inline|is|out|operator|package|return|suspend|super|this|when|val|var)\b - statement.properties: \b(get|set)\b - statement.control: \b(break|continue|else|do|if|try|catch|finally|for|while)\b - statement.class: \b(abstract|annotation|data|enum|final|open|sealed)\b - statement.member: \b(override|lateinit|init)\b - statement.access: \b(internal|private|protected|public)\b - statement.parameter: \b(crossinline|noinline|reified|vararg)\b # Expression and types - type: \b(dynamic|object|throw|typealias)\b # Meta - statement.meta: \@(\bfile|delegate|field|get|property|receiver|set|setparam|param|)\b # Constant - constant: \b(true|false|null) - constant.number: ([0-9]+) # Storage Types - type.storage: \b(Byte|UByte|Char|Double|Float|Int|UInt|Long|ULong|Short|UShort|Boolean|Unit|Nothing)\b # Collections - type.collections: \b(Array)\b # String - constant.string: start: \" end: \" skip: \\. rules: - constant.specialChar: (\\0|\\\\|\\t|\\n|\\r|\\"|\\') - constant.unicode: \\u\{[[:xdigit:]]+} # Shebang Line - comment.shebang: ^(#!).* # Line Comment - comment.line: "//.*" # Block Comment - comment.block: start: "/\\*" end: "\\*/" rules: - todo: "(TODO|XXX|FIXME):?" # Doc Block Comment - comment.block: start: "/\\*\\*" end: "\\*/" rules: - todo: "(TODO|XXX|FIXME):?" # Todo - todo: "(TODO|XXX|FIXME):?" micro-2.0.14/runtime/syntax/kvlang.yaml0000664000175000017510000000376414663411671017442 0ustar nileshnileshfiletype: "kvlang" detect: filename: "\\.kv$" rules: # layouts - special: "\\b[a-z].+" - identifier: "\\b(self|app|root)\\b" - type: "\\b[A-Z].+" - type: "\\b(AnchorLayout|BoxLayout|FloatLayout|RelativeLayout|GridLayout|PageLayout|StackLayout)\\b" - type: "\\b(canvas)\\b" # functions - identifier.function: "[a-zA-Z_0-9]+\\(" # built-in functions - type: "\\b(abs|all|any|ascii|bin|bool|breakpoint|bytearray|bytes)\\b" - type: "\\b(callable|chr|classmethod|compile|copyright|credits|oct)\\b" - type: "\\b(delattr|dict|dir|display|divmod|enumerate|eval|filter)\\b" - type: "\\b(float|format|frozenset|get_ipython|getattr|globals|type)\\b" - type: "\\b(hash|help|hex|id|input|int|isinstance|issubclass|iter|len)\\b" - type: "\\b(license|list|locals|map|max|memoryview|min|next|object)\\b" - type: "\\b(open|ord|pow|print|property|range|repr|reversed|round|set)\\b" - type: "\\b(setattr|slice|sorted|staticmethod|hasattr|super|tuple|str)\\b" - type: "\\b(vars|zip|exec|sum|complex)\\b" # keywords - statement.built_in: "\\b(and|as|assert|async|await|break|class|continue|def)\\b" - statement.built_in: "\\b(del|elif|else|except|finally|for|from|global|if)\\b" - statement.built_in: "\\b(import|in|is|lambda|nonlocal|not|or|pass|raise)\\b" - statement.built_in: "\\b(return|try|while|with|yield|match|case)\\b" # operators - symbol.operator: "([~^.:;,+*|=!\\%]|<|>|/|-|&)" # parentheses - symbol.brackets: "([(){}]|\\[|\\])" # numbers - constant.number: "\\b[0-9](_?[0-9])*(\\.([0-9](_?[0-9])*)?)?(e[0-9](_?[0-9])*)?\\b" # decimal - constant.number: "\\b0b(_?[01])+\\b" # bin - constant.number: "\\b0o(_?[0-7])+\\b" # oct - constant.number: "\\b0x(_?[0-9a-f])+\\b" # hex - constant.bool.none: "\\b(None)\\b" - constant.bool.true: "\\b(True)\\b" - constant.bool.false: "\\b(False)\\b" # strings - constant.string: start: "\"" end: "(\"|$)" skip: "\\\\." rules: [] - constant.string: start: "'" end: "('|$)" skip: "\\\\." rules: [] - comment: start: "#" end: "$" rules: [] micro-2.0.14/runtime/syntax/ledger.yaml0000664000175000017510000000074614663411671017417 0ustar nileshnileshfiletype: ledger detect: filename: "(^|\\.|/)(ledger|ldgr|beancount|bnct)$" rules: - special: "^([0-9]{4}(/|-)[0-9]{2}(/|-)[0-9]{2}|[=~]) .*" - constant: "^[0-9]{4}(/|-)[0-9]{2}(/|-)[0-9]{2}" - statement: "^~ .*" - identifier.var: "^= .*" - identifier: "^[[:space:]]+(![[:space:]]+)?\\(?[A-Za-z ]+(:[A-Za-z ]+)*\\)?" - identifier: "^[[:space:]]+(![[:space:]]+)?\\(?[A-Za-z_\\-]+(:[A-Za-z_\\-]+)*\\)?" - symbol: "[*!]" - comment: "^[[:space:]]*;.*" micro-2.0.14/runtime/syntax/lfe.yaml0000664000175000017510000000103314663411671016711 0ustar nileshnileshfiletype: lfe detect: filename: "lfe$|\\.lfe$" rules: - symbol.brackets: "\\(|\\)" - type: "defun|define-syntax|define|defmacro|defmodule|export" - constant: "\\ [A-Za-z][A-Za-z0-9_-]+\\ " - symbol.operator: "\\(([\\-+*/<>]|<=|>=)|'" - constant.number: "\\b[0-9]+\\b" - constant.string: "\\\"(\\\\.|[^\"])*\\\"" - special: "['|`][A-Za-z][A-Za-z0-9_\\-]+" - constant.specialChar: "\\\\.?" - comment: "(^|[[:space:]]);.*" - indent-char.whitespace: "[[:space:]]+$" - indent-char: " + +| + +" micro-2.0.14/runtime/syntax/lilypond.yaml0000664000175000017510000000737314663411671020012 0ustar nileshnileshfiletype: lilypond detect: filename: "\\.ly$|\\.ily$|\\.lly$" rules: - constant.number: "\\d+" - identifier: "\\b(staff|spacing|signature|routine|notes|handler|corrected|beams|arpeggios|Volta_engraver|Voice|Vertical_align_engraver|Vaticana_ligature_engraver|VaticanaVoice|VaticanaStaff|Tweak_engraver|Tuplet_engraver|Trill_spanner_engraver|Timing_translator|Time_signature_performer|Time_signature_engraver|Tie_performer|Tie_engraver|Text_spanner_engraver|Text_engraver|Tempo_performer|Tab_tie_follow_engraver|Tab_staff_symbol_engraver|Tab_note_heads_engraver|TabVoice|TabStaff|System_start_delimiter_engraver|Stem_engraver|Stanza_number_engraver|Stanza_number_align_engraver|Staff_symbol_engraver|Staff_performer|Staff_collecting_engraver|StaffGroup|Staff|Spanner_break_forbid_engraver|Span_bar_stub_engraver|Span_bar_engraver|Span_arpeggio_engraver|Spacing_engraver|Slur_performer|Slur_engraver|Slash_repeat_engraver|Separating_line_group_engraver|Script_row_engraver|Script_engraver|Script_column_engraver|Score|Rhythmic_column_engraver|RhythmicStaff|Rest_engraver|Rest_collision_engraver|Repeat_tie_engraver|Repeat_acknowledge_engraver|Pure_from_neighbor_engraver|Pitched_trill_engraver|Pitch_squash_engraver|Piano_pedal_performer|Piano_pedal_engraver|Piano_pedal_align_engraver|PianoStaff|Phrasing_slur_engraver|PetrucciVoice|PetrucciStaff|Percent_repeat_engraver|Part_combine_engraver|Parenthesis_engraver|Paper_column_engraver|Output_property_engraver|Ottava_spanner_engraver|OneStaff|NullVoice|Note_spacing_engraver|Note_performer|Note_name_engraver|Note_heads_engraver|Note_head_line_engraver|NoteName\\|NoteHead|New_fingering_engraver|Multi_measure_rest_engraver|Midi_control_function_performer|Metronome_mark_engraver|Mensural_ligature_engraver|MensuralVoice|MensuralStaff|Mark_engraver|Lyrics|Lyric_performer|Lyric_engraver|Ligature_bracket_engraver|Ledger_line_engraver|Laissez_vibrer_engraver|Kievan_ligature_engraver|KievanVoice|KievanStaff|Key_performer|Key_engraver|Keep_alive_together_engraver|Instrument_switch_engraver|Instrument_name_engraver|Hyphen_engraver|Grob_pq_engraver|GregorianTranscriptionVoice|GregorianTranscriptionStaff|GrandStaff|Grace_spacing_engraver|Grace_engraver|Grace_beam_engraver|Grace_auto_beam_engraver|Global|Glissando_engraver|Fretboard_engraver|FretBoards|Forbid_line_break_engraver|Footnote_engraver|Font_size_engraver|Fingering_engraver|Fingering_column_engraver|Figured_bass_position_engraver|Figured_bass_engraver|FiguredBass|Extender_engraver|Episema_engraver|Dynamics|Dynamic_performer|Dynamic_engraver|Dynamic_align_engraver|Drum_notes_engraver|Drum_note_performer|DrumVoice|DrumStaff|Double_percent_repeat_engraver|Dots_engraver|Dot_column_engraver|Devnull|Default_bar_line_engraver|Custos_engraver|Cue_clef_engraver|CueVoice|Control_track_performer|Concurrent_hairpin_engraver|Collision_engraver|Cluster_spanner_engraver|Clef_engraver|Chord_tremolo_engraver|Chord_name_engraver|ChordNames|ChoirStaff|Breathing_sign_engraver|Break_align_engraver|Bend_engraver|Beam_performer|Beam_engraver|Beam_collision_engraver|Bar_number_engraver|Bar_engraver|Axis_group_engraver|Auto_beam_engraver|Arpeggio_engraver|Accidental_engraver|Score)\\b" - statement: "[-_^]?\\\\[-A-Za-z_]+" - preproc: "\\b(((gisis|gis|geses|ges|g|fisis|fis|feses|fes|f|eisis|eis|eeses|ees|e|disis|dis|deses|des|d|cisis|cis|ceses|ces|c|bisis|bis|beses|bes|b|aisis|ais|aeses|aes|a)[,']*[?!]?)|s|r|R|q)(128|64|32|16|8|4|2|1|\\\\breve|\\\\longa|\\\\maxima)?([^\\\\\\w]|_|\\b)" - special: "[(){}<>]|\\[|\\]" - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\." - comment: start: "%\\{" end: "%\\}" rules: [] - comment: start: "%" end: "$" rules: [] micro-2.0.14/runtime/syntax/lisp.yaml0000664000175000017510000000076314663411671017123 0ustar nileshnileshfiletype: lisp detect: filename: "(emacs|zile)$|\\.(el|li?sp|scm|ss|rkt)$" rules: - default: "\\([a-z-]+" - symbol: "\\(([\\-+*/<>]|<=|>=)|'" - constant.number: "\\b[0-9]+b>" - special: "\\bnil\\b" - preproc: "\\b[tT]b>" - constant.string: "\\\"(\\\\.|[^\"])*\\\"" - constant.specialChar: "'[A-Za-z][A-Za-z0-9_-]+" - constant.specialChar: "\\\\.?" - comment: "(^|[[:space:]]);.*" - indent-char.whitespace: "[[:space:]]+$" - indent-char: " + +| + +" micro-2.0.14/runtime/syntax/log.yaml0000664000175000017510000000737314663411671016741 0ustar nileshnileshfiletype: log detect: filename: "(\\.log|log\\.txt)$" rules: - diff-modified: "\\b(WARN(ING)?|[Ww]arn(ing)?|w(r)?n|w|W/)\\b" - diff-modified: "\\b(CRITICAL|[Cc]ritical)\\b" - constant: "\\b(INFO(RMATION)?|[Ii]nfo(rmation)?|[Ii]n(f)?|i|I/)\\b" - constant: "\\b(DEBUG|[Dd]ebug|dbug|dbg|de|d|D/)\\b" - constant: "\\b(VERBOSE|[Vv]erbose|V/)\\b" - constant: "\\b(ALERT|[Aa]lert)\\b" - preproc: "\\b(TRACE|Trace|NOTICE|VERBOSE|verb|vrb|vb|v)\\b" - gutter-error: "\\b(ERROR|[Ee]rr(or)?|[Ee]r(or)?|e|E\\x2F)\\b" - gutter-error: "\\b(FATAL|[Ff]atal)\\b" - gutter-error: "\\b(EMERGENCY|[Ee]mergency)\\b" - gutter-error: "\\b(FAIL(URE)?|[Ff]ail(ure)?)\\b" # constants - constant.bool.true: "\\b(YES|yes|Y|y|ON|on|TRUE|True|true)\\b" - constant.bool.false: "\\b(NO|no|N|n|OFF|off|FALSE|False|false)\\b" - constant.bool.false: "\\b(None|null|nil)\\b" # numbers - constant.number: "\\b[0-9](_?[0-9])*(\\.([0-9](_?[0-9])*)?)?(e[0-9](_?[0-9])*)?\\b" # decimal - constant.number: "\\b0b(_?[01])+\\b" # bin - constant.number: "\\b0o(_?[0-7])+\\b" # oct - constant.number: "\\b0x(_?[0-9a-f])+\\b" # hex # operators - symbol.operator: "([~^.:;,+*|=!\\%]|<|>|/|-|&)" # parentheses - symbol.brackets: "([(){}]|\\[|\\])" # string - constant.string: start: "\"" end: "(\"|$)" skip: "\\\\." rules: - constant.specialChar: "\\\\." - constant.string: start: "'" end: "('|$)" skip: "\\\\." rules: - constant.specialChar: "\\\\." # file - preproc: "\\b(FILE|File|file)\\b" # time - identifier: "\\b((([Mm]on|[Tt]ues|[Ww]ed(nes)?|[Tt]hur(s)?|[Ff]ri|[Ss]at(ur)?|[Ss]un)(day)?\\s)?([Jj]an(uary)?|[Ff]eb(ruary)?|[Mm]ar(ch)?|[Aa]pr(il)?|[Mm]ay|[Jj]un(e)?|[Jj]ul(y)?|[Aa]ug(ust)?|[Aa]go|[Ss]ep(tember)?|[Oo]ct(ober)?|[Nn]ov(ember)?|[Dd]ec(ember)?)\\s\\d{1,2},?(\\s\\d{4})?)\\b" # date - identifier: "\\b(\\d{2,4}[-/\\.]?\\d{2,3}[-/\\.]?\\d{2,4})\\b" # date - identifier: "\\b(\\d{2}:\\d{2}(:\\d{2})?([\\.,]?\\d{1,8}[\\.\\+,]?\\d{1,8}?)?([\\.\\+,]?\\d{1,8}[\\.\\+,]?\\d{1,8}?)?([\\.\\+,]?\\d{1,8}?)?(\\s-\\d{0,4})?)\\b" # time - identifier: "^([0-2][0-9][0-2][0-9][-/]?[0-9][0-9][-/]?[0-9][0-9])" # - identifier: "^([0-2][0-9][0-2][0-9][-/]?[0-9][0-9][-/]?[0-9][0-9]\\s[0-9][0-9]:[0-9][0-9](:[0-9][0-9])?(\\.?[0-9][0-9][0-9])?)" - identifier: "^(\\d{4}[-/]?\\d{2}[-/]?\\d{2}\\s\\d{2}:\\d{2}(:\\d{2})?(\\.?\\d{2,8})?)" - identifier: "^([0-2][0-9]|[0-2]-?[0-9][0-9]-?[0-9][0-9])\\-([0-1][0-9])\\-([0-3][0-9]) ([0-2][0-9])\\:([0-5][0-9])\\:([0-5][0-9]),([0-9][0-9][0-9])" # Complete precision: - identifier: "^(\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d\\.\\d+([+-][0-2]\\d:[0-5]\\d|Z))" # No milliseconds: - identifier: "^(\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d([+-][0-2]\\d:[0-5]\\d|Z))" # No Seconds: - identifier: "^(\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d([+-][0-2]\\d:[0-5]\\d|Z))" # Putting it all together: - identifier: "^(\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d\\.\\d+([+-][0-2]\\d:[0-5]\\d|Z))|(\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d([+-][0-2]\\d:[0-5]\\d|Z))|(\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d([+-][0-2]\\d:[0-5]\\d|Z))" # Complete precision: - identifier: "^(\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d\\.\\d+)" # No milliseconds - identifier: "^(\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d)" # No Seconds - identifier: "^(\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d)" # Putting it all together - identifier: "^(\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d\\.\\d+)|(\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d)|(\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d)" # link - constant.string.url: start: "https?://" end: "\\s" rules: [] # path # - constant.string.url: "\\b(.+)/([^/]+)\\b" # linux # - constant.string.url: "\\b(^[a-zA-Z]:)\\b" # windowns - diff-modified: "([Cc]ommit:)\\s\\w+\\[\\w+]" micro-2.0.14/runtime/syntax/lua.yaml0000664000175000017510000001031414663411671016726 0ustar nileshnileshfiletype: lua detect: filename: "\\.lua$" rules: - statement: "\\b(do|end|while|break|repeat|until|if|elseif|then|else|for|in|function|local|return|goto)\\b" - statement: "\\b(not|and|or)\\b" - statement: "\\b(debug|string|math|table|io|coroutine|os|utf8|bit32)\\b\\." - statement: "\\b(_ENV|_G|_VERSION|assert|collectgarbage|dofile|error|getfenv|getmetatable|ipairs|load|loadfile|module|next|pairs|pcall|print|rawequal|rawget|rawlen|rawset|require|select|setfenv|setmetatable|tonumber|tostring|type|unpack|xpcall)\\s*\\(" - identifier: "io\\.\\b(close|flush|input|lines|open|output|popen|read|tmpfile|type|write)\\b" - identifier: "math\\.\\b(abs|acos|asin|atan2|atan|ceil|cosh|cos|deg|exp|floor|fmod|frexp|huge|ldexp|log10|log|max|maxinteger|min|mininteger|modf|pi|pow|rad|random|randomseed|sin|sqrt|tan|tointeger|type|ult)\\b" - identifier: "os\\.\\b(clock|date|difftime|execute|exit|getenv|remove|rename|setlocale|time|tmpname)\\b" - identifier: "package\\.\\b(config|cpath|loaded|loadlib|path|preload|seeall|searchers|searchpath)\\b" - identifier: "string\\.\\b(byte|char|dump|find|format|gmatch|gsub|len|lower|match|pack|packsize|rep|reverse|sub|unpack|upper)\\b" - identifier: "table\\.\\b(concat|insert|maxn|move|pack|remove|sort|unpack)\\b" - identifier: "utf8\\.\\b(char|charpattern|codes|codepoint|len|offset)\\b" - identifier: "coroutine\\.\\b(create|isyieldable|resume|running|status|wrap|yield)\\b" - identifier: "debug\\.\\b(debug|getfenv|gethook|getinfo|getlocal|getmetatable|getregistry|getupvalue|getuservalue|setfenv|sethook|setlocal|setmetatable|setupvalue|setuservalue|traceback|upvalueid|upvaluejoin)\\b" - identifier: "bit32\\.\\b(arshift|band|bnot|bor|btest|bxor|extract|replace|lrotate|lshift|rrotate|rshift)\\b" - identifier: "\\:\\b(close|flush|lines|read|seek|setvbuf|write|byte|char|dump|find|format|gmatch|gsub|len|lower|match|pack|packsize|rep|reverse|sub|unpack|upper)\\b" - identifier: "\\b(self|arg)\\b" - constant: "\\b(false|nil|true)\\b" - statement: "(\\b(dofile|require|include)|%q|%!|%Q|%r|%x)\\b" - symbol.brackets: "[(){}\\[\\]]" - symbol: "(\\*|//|/|%|\\+|-|\\^|>|>=|<|<=|~=|=|[\\.]{2,3}|#)" - constant.number: "\\b((0[xX](([0-9A-Fa-f]+\\.[0-9A-Fa-f]*)|(\\.?[0-9A-Fa-f]+))([pP][-+]?[0-9]+)?)|((([0-9]+\\.[0-9]*)|(\\.?[0-9]+))([eE][-+]?[0-9]+)?))" - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\([abfnrtvz\\'\"]|[0-9]{1,3}|x[0-9a-fA-F][0-9a-fA-F]|u\\{[0-9a-fA-F]+\\})" - constant.string: start: "'" end: "'" skip: "\\\\." rules: - constant.specialChar: "\\\\([abfnrtvz\\'\"]|[0-9]{1,3}|x[0-9a-fA-F][0-9a-fA-F]|u\\{[0-9a-fA-F]+\\})" - constant.string: start: "\\[\\[" end: "\\]\\]" rules: [] # support first few lengths of "long brackets" explicitly # brackets longer than that will give false positives - constant.string: start: "\\[=\\[" end: "\\]=\\]" rules: [] - constant.string: start: "\\[==\\[" end: "\\]==\\]" rules: [] - constant.string: start: "\\[===\\[" end: "\\]===\\]" rules: [] - constant.string: start: "\\[====+\\[" end: "\\]====+\\]" rules: [] - comment.block: start: "\\-\\-\\[\\[" end: "\\]\\]" rules: - todo: "(TODO|NOTE|FIXME):?" # support long brackets, same as with multiline strings - comment.block: start: "\\-\\-\\[=\\[" end: "\\]=\\]" rules: - todo: "(TODO|NOTE|FIXME):?" - comment.block: start: "\\-\\-\\[==\\[" end: "\\]==\\]" rules: - todo: "(TODO|NOTE|FIXME):?" - comment.block: start: "\\-\\-\\[===\\[" end: "\\]===\\]" rules: - todo: "(TODO|NOTE|FIXME):?" - comment.block: start: "\\-\\-\\[====+\\[" end: "\\]====+\\]" rules: - todo: "(TODO|NOTE|FIXME):?" # this has to go after block comment or block comment does not work - comment: start: "\\-\\-" end: "$" rules: - todo: "(TODO|NOTE|FIXME):?" micro-2.0.14/runtime/syntax/mail.yaml0000664000175000017510000000112614663411671017070 0ustar nileshnileshfiletype: mail detect: filename: "(.*/mutt-.*|\\.eml)$" header: "^From .* \\d+:\\d+:\\d+ \\d+" rules: - type: "^From .*" - identifier: "^[^[:space:]]+:" - preproc: "^List-(Id|Archive|Subscribe|Unsubscribe|Post|Help):" - constant: "^(To|From):" - constant.string: start: "^Subject:.*" end: "$" rules: - constant.specialChar: "\\\\." - statement: "?" - default: start: "^\\n\\n" end: ".*" rules: [] - comment: start: "^>.*" end: "$" rules: [] micro-2.0.14/runtime/syntax/make_headers.go0000664000175000017510000000300114663411671020213 0ustar nileshnilesh//go:build ignore // +build ignore package main import ( "bytes" "fmt" "io/ioutil" "os" "strings" "time" yaml "gopkg.in/yaml.v2" ) type HeaderYaml struct { FileType string `yaml:"filetype"` Detect struct { FNameRgx string `yaml:"filename"` HeaderRgx string `yaml:"header"` SignatureRgx string `yaml:"signature"` } `yaml:"detect"` } type Header struct { FileType string FNameRgx string HeaderRgx string SignatureRgx string } func main() { if len(os.Args) > 1 { os.Chdir(os.Args[1]) } files, _ := ioutil.ReadDir(".") for _, f := range files { fname := f.Name() if strings.HasSuffix(fname, ".yaml") { convert(fname[:len(fname)-5]) } } } func convert(name string) { filename := name + ".yaml" var hdr HeaderYaml source, err := ioutil.ReadFile(filename) if err != nil { panic(err) } err = yaml.Unmarshal(source, &hdr) if err != nil { panic(err) } encode(name, hdr) } func encode(name string, c HeaderYaml) { f, _ := os.Create(name + ".hdr") f.WriteString(c.FileType + "\n") f.WriteString(c.Detect.FNameRgx + "\n") f.WriteString(c.Detect.HeaderRgx + "\n") f.WriteString(c.Detect.SignatureRgx + "\n") f.Close() } func decode(name string) Header { start := time.Now() data, _ := ioutil.ReadFile(name + ".hdr") strs := bytes.Split(data, []byte{'\n'}) var hdr Header hdr.FileType = string(strs[0]) hdr.FNameRgx = string(strs[1]) hdr.HeaderRgx = string(strs[2]) hdr.SignatureRgx = string(strs[3]) fmt.Printf("took %v\n", time.Since(start)) return hdr } micro-2.0.14/runtime/syntax/makefile.yaml0000664000175000017510000000241614663411671017726 0ustar nileshnileshfiletype: makefile detect: filename: "([Mm]akefile|\\.ma?k)$" header: "^#!.*/(env +)?[bg]?make( |$)" rules: - preproc: "\\<(ifeq|ifdef|ifneq|ifndef|else|endif)\\>" - statement: "^(export|include|override)\\>" - symbol.operator: "^[^:= ]+:" - symbol.operator: "([=,%]|\\+=|\\?=|:=|&&|\\|\\|)" - statement: "\\$\\((abspath|addprefix|addsuffix|and|basename|call|dir)[[:space:]]" - statement: "\\$\\((error|eval|filter|filter-out|findstring|firstword)[[:space:]]" - statement: "\\$\\((flavor|foreach|if|info|join|lastword|notdir|or)[[:space:]]" - statement: "\\$\\((origin|patsubst|realpath|shell|sort|strip|suffix)[[:space:]]" - statement: "\\$\\((value|warning|wildcard|word|wordlist|words)[[:space:]]" - identifier: "^.+:" - identifier: "[()$]" - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\." - constant.string: start: "'" end: "'" skip: "\\\\." rules: - constant.specialChar: "\\\\." - identifier: "\\$+(\\{[^} ]+\\}|\\([^) ]+\\))" - identifier: "\\$[@^<*?%|+]|\\$\\([@^<*?%+-][DF]\\)" - identifier: "\\$\\$|\\\\.?" - comment: start: "#" end: "$" rules: [] micro-2.0.14/runtime/syntax/man.yaml0000664000175000017510000000041614663411671016722 0ustar nileshnileshfiletype: man detect: filename: "\\.[1-9]x?$" rules: - green: "\\.(S|T)H.*$" - brightgreen: "\\.(S|T)H|\\.TP" - brightred: "\\.(BR?|I[PR]?).*$" - brightblue: "\\.(BR?|I[PR]?|PP)" - brightwhite: "\\\\f[BIPR]" - yellow: "\\.(br|DS|RS|RE|PD)" micro-2.0.14/runtime/syntax/markdown.yaml0000664000175000017510000000204014663411671017764 0ustar nileshnileshfiletype: markdown detect: filename: "\\.(livemd|md|mkd|mkdn|markdown)$" rules: # Tables (Github extension) - type: ".*[ :]\\|[ :].*" # quotes - statement: "^>.*" # Emphasis - type: "(^|[[:space:]])(_[^ ][^_]*_|\\*[^ ][^*]*\\*)" # Strong emphasis - type: "(^|[[:space:]])(__[^ ][^_]*__|\\*\\*[^ ][^*]*\\*\\*)" # strike-through - type: "(^|[[:space:]])~~[^ ][^~]*~~" # horizontal rules - special: "^(---+|===+|___+|\\*\\*\\*+)\\s*$" # headlines - special: "^#{1,6}.*" # lists - identifier: "^[[:space:]]*[\\*+-] |^[[:space:]]*[0-9]+\\. " # misc - preproc: "(\\(([CcRr]|[Tt][Mm])\\)|\\.{3}|(^|[[:space:]])\\-\\-($|[[:space:]]))" # links - constant: "\\[[^]]+\\]" - constant: "\\[([^][]|\\[[^]]*\\])*\\]\\([^)]+\\)" # images - underlined: "!\\[[^][]*\\](\\([^)]+\\)|\\[[^]]+\\])" # urls - underlined: "https?://[^ )>]+" - special: "^```$" - special: start: "`" end: "`" rules: [] micro-2.0.14/runtime/syntax/mc.yaml0000664000175000017510000000063014663411671016544 0ustar nileshnilesh# sendmail config files filetype: mc detect: filename: "\\.mc$" rules: - statement: "^(divert|VERSIONID|OSTYPE|DOMAIN|FEATURE|define)" - statement: "^(DAEMON_OPTIONS|MAILER)" - comment: start: "#" end: "$" rules: [] - comment: start: "dnl" end: "$" rules: [] - constant.string: start: "`" end: "'" rules: [] micro-2.0.14/runtime/syntax/micro.yaml0000664000175000017510000000243214663411671017260 0ustar nileshnileshfiletype: micro detect: filename: "\\.(micro)$" rules: - statement: "\\b(syntax|color(-link)?)\\b" - statement: "\\b(start=|end=)\\b" # Simple one-liners - identifier: "\\b(default|number|statement|underlined|error|todo|statusline|indent-char|cursor\\-line|color\\-column|ignore|divider|tabbar)\\b" # Separate identifiers to keep "complex" regex clean - identifier: "\\b(special(Char)?)\\b" - identifier: "\\b((current\\-)?line\\-number)\\b" - identifier: "\\b(gutter\\-(info|error|warning){1})\\b" - identifier: "\\b(comment(\\.bright)?)\\b" - identifier: "\\b(symbol(\\.(brackets|operator|tag))?)\\b" - identifier: "\\b(identifier(\\.(class|macro|var))?)\\b" - identifier: "\\b(constant(\\.(bool(\\.(true|false){1})?|number|specialChar|string(\\.url)?){1})?)\\b" - identifier: "\\b(preproc(\\.shebang)?)\\b" - identifier: "\\b(type(\\.keyword)?)\\b" - constant.number: "\\b(|h|A|0x)+[0-9]+(|h|A)+\\b" - constant.number: "\\b0x[0-9 a-f A-F]+\\b" - comment: start: "#" end: "$" rules: - todo: "(FIXME|TODO|NOTE):?" - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\." - constant.number: "#[0-9 A-F a-f]+" micro-2.0.14/runtime/syntax/mpdconf.yaml0000664000175000017510000000073114663411671017575 0ustar nileshnileshfiletype: mpd detect: filename: "mpd\\.conf$" rules: - statement: "\\b(user|group|bind_to_address|host|port|plugin|name|type)\\b" - statement: "\\b((music|playlist)_directory|(db|log|state|pid|sticker)_file)\\b" - special: "^(input|audio_output|decoder)[[:space:]]*\\{|\\}" - constant.string: "\"(\\\\.|[^\"])*\"|'(\\\\.|[^'])*'" - comment: "(^|[[:space:]])#([^{].*)?$" - indent-char.whitespace: "[[:space:]]+$" - indent-char: " + +| + +" micro-2.0.14/runtime/syntax/msbuild.yaml0000664000175000017510000000015014663411671017601 0ustar nileshnileshfiletype: msbuild detect: filename: "\\.(.*proj|props|targets|tasks)$" rules: - include: "xml" micro-2.0.14/runtime/syntax/nanorc.yaml0000664000175000017510000000212014663411671017421 0ustar nileshnileshfiletype: nanorc detect: filename: "\\.?nanorc$" rules: - default: "(?i)^[[:space:]]*((un)?set|include|syntax|i?color).*$" - type: "(?i)^[[:space:]]*(set|unset)[[:space:]]+(autoindent|backup|backupdir|backwards|boldtext|brackets|casesensitive|const|cut|fill|historylog|matchbrackets|morespace|mouse|multibuffer|noconvert|nofollow|nohelp|nonewlines|nowrap|operatingdir|preserve|punct)\\>|^[[:space:]]*(set|unset)[[:space:]]+(quickblank|quotestr|rebinddelete|rebindkeypad|regexp|smarthome|smooth|speller|suspend|tabsize|tabstospaces|tempfile|undo|view|whitespace|wordbounds)\\b" - preproc: "(?i)^[[:space:]]*(set|unset|include|syntax|header)\\b" - constant.bool.true: "(?i)(set)\\b" - constant.bool.false: "(?i)(unset)\\b" - identifier: "(?i)^[[:space:]]*(i)?color[[:space:]]*(bright)?(white|black|red|blue|green|yellow|magenta|cyan)?(,(white|black|red|blue|green|yellow|magenta|cyan))?\\b" - special: "(?i)^[[:space:]]*(i)?color\\b|\\b(start|end)=" - constant.string: "\"(\\\\.|[^\"])*\"" - comment: "^[[:space:]]*#.*$" - comment.bright: "^[[:space:]]*##.*$" micro-2.0.14/runtime/syntax/nginx.yaml0000664000175000017510000001163314663411671017275 0ustar nileshnileshfiletype: nginx detect: filename: "nginx.*\\.conf$|\\.nginx$" header: "^(server|upstream)[a-z ]*\\{$" rules: - preproc: "\\b(events|server|http|location|upstream)[[:space:]]*\\{" - statement: "(^|[[:space:]{;])(access_log|add_after_body|add_before_body|add_header|addition_types|aio|alias|allow|ancient_browser|ancient_browser_value|auth_basic|auth_basic_user_file|autoindex|autoindex_exact_size|autoindex_localtime|break|charset|charset_map|charset_types|chunked_transfer_encoding|client_body_buffer_size|client_body_in_file_only|client_body_in_single_buffer|client_body_temp_path|client_body_timeout|client_header_buffer_size|client_header_timeout|client_max_body_size|connection_pool_size|create_full_put_path|daemon|dav_access|dav_methods|default_type|deny|directio|directio_alignment|disable_symlinks|empty_gif|env|error_log|error_page|expires|fastcgi_buffer_size|fastcgi_buffers|fastcgi_busy_buffers_size|fastcgi_cache|fastcgi_cache_bypass|fastcgi_cache_key|fastcgi_cache_lock|fastcgi_cache_lock_timeout|fastcgi_cache_min_uses|fastcgi_cache_path|fastcgi_cache_use_stale|fastcgi_cache_valid|fastcgi_connect_timeout|fastcgi_hide_header|fastcgi_ignore_client_abort|fastcgi_ignore_headers|fastcgi_index|fastcgi_intercept_errors|fastcgi_keep_conn|fastcgi_max_temp_file_size|fastcgi_next_upstream|fastcgi_no_cache|fastcgi_param|fastcgi_pass|fastcgi_pass_header|fastcgi_read_timeout|fastcgi_send_timeout|fastcgi_split_path_info|fastcgi_store|fastcgi_store_access|fastcgi_temp_file_write_size|fastcgi_temp_path|flv|geo|geoip_city|geoip_country|gzip|gzip_buffers|gzip_comp_level|gzip_disable|gzip_http_version|gzip_min_length|gzip_proxied|gzip_static|gzip_types|gzip_vary|if|if_modified_since|ignore_invalid_headers|image_filter|image_filter_buffer|image_filter_jpeg_quality|image_filter_sharpen|image_filter_transparency|include|index|internal|ip_hash|keepalive|keepalive_disable|keepalive_requests|keepalive_timeout|large_client_header_buffers|limit_conn|limit_conn_log_level|limit_conn_zone|limit_except|limit_rate|limit_rate_after|limit_req|limit_req_log_level|limit_req_zone|limit_zone|lingering_close|lingering_time|lingering_timeout|listen|location|log_format|log_not_found|log_subrequest|map|map_hash_bucket_size|map_hash_max_size|master_process|max_ranges|memcached_buffer_size|memcached_connect_timeout|memcached_next_upstream|memcached_pass|memcached_read_timeout|memcached_send_timeout|merge_slashes|min_delete_depth|modern_browser|modern_browser_value|mp4|mp4_buffer_size|mp4_max_buffer_size|msie_padding|msie_refresh|open_file_cache|open_file_cache_errors|open_file_cache_min_uses|open_file_cache_valid|open_log_file_cache|optimize_server_names|override_charset|pcre_jit|perl|perl_modules|perl_require|perl_set|pid|port_in_redirect|postpone_output|proxy_buffer_size|proxy_buffering|proxy_buffers|proxy_busy_buffers_size|proxy_cache|proxy_cache_bypass|proxy_cache_key|proxy_cache_lock|proxy_cache_lock_timeout|proxy_cache_min_uses|proxy_cache_path|proxy_cache_use_stale|proxy_cache_valid|proxy_connect_timeout|proxy_cookie_domain|proxy_cookie_path|proxy_hide_header|proxy_http_version|proxy_ignore_client_abort|proxy_ignore_headers|proxy_intercept_errors|proxy_max_temp_file_size|proxy_next_upstream|proxy_no_cache|proxy_pass|proxy_pass_header|proxy_read_timeout|proxy_redirect|proxy_send_timeout|proxy_set_header|proxy_ssl_session_reuse|proxy_store|proxy_store_access|proxy_temp_file_write_size|proxy_temp_path|random_index|read_ahead|real_ip_header|recursive_error_pages|request_pool_size|reset_timedout_connection|resolver|resolver_timeout|return|rewrite|root|satisfy|satisfy_any|secure_link_secret|send_lowat|send_timeout|sendfile|sendfile_max_chunk|server|server|server_name|server_name_in_redirect|server_names_hash_bucket_size|server_names_hash_max_size|server_tokens|set|set_real_ip_from|source_charset|split_clients|ssi|ssi_silent_errors|ssi_types|ssl|ssl_certificate|ssl_certificate_key|ssl_ciphers|ssl_client_certificate|ssl_crl|ssl_dhparam|ssl_engine|ssl_prefer_server_ciphers|ssl_protocols|ssl_session_cache|ssl_session_timeout|ssl_verify_client|ssl_verify_depth|sub_filter|sub_filter_once|sub_filter_types|tcp_nodelay|tcp_nopush|timer_resolution|try_files|types|types_hash_bucket_size|types_hash_max_size|underscores_in_headers|uninitialized_variable_warn|upstream|user|userid|userid_domain|userid_expires|userid_name|userid_p3p|userid_path|userid_service|valid_referers|variables_hash_bucket_size|variables_hash_max_size|worker_priority|worker_processes|worker_rlimit_core|worker_rlimit_nofile|working_directory|xml_entities|xslt_stylesheet|xslt_types)([[:space:]]|$)" - constant.bool.true: "\\b(on)\\b" - constant.bool.false: "\\b(off)\\b" - identifier: "\\$[A-Za-z][A-Za-z0-9_]*" - symbol: "[*]" - constant-string: "\"(\\\\.|[^\"])*\"|'(\\\\.|[^'])*'" - constant.string: start: "'$" end: "';$" rules: [] - comment: "(^|[[:space:]])#([^{].*)?$" - indent-char.whitespace: "[[:space:]]+$" - indent-char: " + +| + +" micro-2.0.14/runtime/syntax/nim.yaml0000664000175000017510000000451614663411671016737 0ustar nileshnileshfiletype: nim detect: filename: "\\.nims?$|nim.cfg" rules: - preproc: "[\\{\\|]\\b(atom|lit|sym|ident|call|lvalue|sideeffect|nosideeffect|param|genericparam|module|type|let|var|const|result|proc|method|iterator|converter|macro|template|field|enumfield|forvar|label|nk[a-zA-Z]+|alias|noalias)\\b[\\}\\|]" - statement: "\\b(addr|and|as|asm|atomic|bind|block|break|case|cast|concept|const|continue|converter|defer|discard|distinct|div|do|elif|else|end|enum|except|export|finally|for|from|func|generic|if|import|in|include|interface|is|isnot|iterator|let|macro|method|mixin|mod|nil|not|notin|object|of|or|out|proc|ptr|raise|ref|return|shl|shr|static|template|try|tuple|type|using|var|when|while|with|without|xor|yield)\\b" - statement: "\\b(deprecated|noSideEffect|constructor|destructor|override|procvar|compileTime|noReturn|acyclic|final|shallow|pure|asmNoStackFrame|error|fatal|warning|hint|line|linearScanEnd|computedGoto|unroll|immediate|checks|boundsChecks|overflowChecks|nilChecks|assertations|warnings|hints|optimization|patterns|callconv|push|pop|global|pragma|experimental|bitsize|volatile|noDecl|header|incompleteStruct|compile|link|passC|passL|emit|importc|importcpp|importobjc|codegenDecl|injectStmt|intdefine|strdefine|varargs|exportc|extern|bycopy|byref|union|packed|unchecked|dynlib|cdecl|thread|gcsafe|threadvar|guard|locks|compileTime)\\b" - symbol.operator: "[=\\+\\-\\*/<>@\\$~&%\\|!\\?\\^\\.:\\\\]+" - special: "\\{\\.|\\.\\}|\\[\\.|\\.\\]|\\(\\.|\\.\\)|;|,|`" - statement: "\\.\\." - type: "\\b(int|cint|int8|int16|int32|int64|uint|uint8|uint16|uint32|uint64|float|float32|float64|bool|char|enum|string|cstring|cstringArray|cdouble|csize_t|pointer|array|openarray|seq|varargs|tuple|object|set|void|auto|cshort|clong|range|nil|T|untyped|typedesc)\\b" - type: "'[iI](8|16|32|64)?\\b|'[uU](8|16|32|64)?\\b|'[fF](32|64|128)?\\b|'[dD]\\b" - constant.number: "\\b[0-9]+\\b" - constant.number: "\\b0[xX][0-9A-Fa-f][0-9_A-Fa-f]+\\b" - constant.number: "\\b0[ocC][0-7][0-7_]+\\b" - constant.number: "\\b0[bB][01][01_]+\\b" - constant.number: "\\b[0-9_]((\\.?)[0-9_]+)?[eE][+\\-][0-9][0-9_]+\\b" - constant.string: "\"(\\\\.|[^\"])*\"|'(\\\\.|[^'])*'" - comment: "[[:space:]]*(?:[^\\\\]|^)#.*$" - comment: start: "\\#\\[" end: "\\]\\#" rules: [] - todo: "(TODO|FIXME|XXX):?" micro-2.0.14/runtime/syntax/nix.yaml0000664000175000017510000000124214663411671016743 0ustar nileshnileshfiletype: nix detect: filename: "\\.nix$" rules: - special: "\\b(Ellipsis|null|self|super|true|false|abort)\\b" - statement: "\\b(let|in|with|import|rec|inherit)\\b" - symbol.operator: "([~^.:;,+*|=!\\%@]|<|>|/|-|&)" - symbol.brackets: "([(){}]|\\[|\\])" - constant.number: "\\b[0-9](_?[0-9])*(\\.([0-9](_?[0-9])*)?)?(e[0-9](_?[0-9])*)?\\b" - constant.string: start: "\"" end: "\"" rules: [] - constant.string: start: "''" end: "''" rules: [] - comment: start: "#" end: "$" rules: [] - comment: start: "/\\*" end: "\\*/" rules: [] micro-2.0.14/runtime/syntax/nu.yaml0000664000175000017510000001552314663411671016576 0ustar nileshnileshfiletype: nu detect: filename: "\\.nu$" rules: - symbol: "[-+/*=<>!~%?:&|]" # https://www.nushell.sh/book/command_reference.html - statement: "\\b(agg-groups|agg|alias|all-false|all-true|all?|ansi gradient)\\b" - statement: "\\b(ansi strip|ansi|any?|append|append|arg-max)\\b" - statement: "\\b(arg-min|arg-sort|arg-true|arg-unique|as-date)\\b" - statement: "\\b(as-datetime|as|benchmark|build-string|cache|cal|cd)\\b" - statement: "\\b(char|clear|col|collect|columns|compact|complete)\\b" - statement: "\\b(concatenate|config|config env|config nu|contains)\\b" - statement: "\\b(count|count-null|cp|cumulative|date format|date humanize)\\b" - statement: "\\b(date list-timezone|date now|date to-record|date to-table)\\b" - statement: "\\b(date to-timezone|date|db and|db as|db col|db collect)\\b" - statement: "\\b(db describe|db fn|db from|db group-by|db join|db limit)\\b" - statement: "\\b(db open|db or|db order-by|db over|db query|db schema)\\b" - statement: "\\b(db select|db testing|db where|db|debug|decode)\\b" - statement: "\\b(def-env|default|def|describe|describe|detect columns)\\b" - statement: "\\b(df-not|do|drop|drop|drop column|drop nth|drop-duplicates)\\b" - statement: "\\b(drop-nulls|dtypes|du|each while|each|echo|empty?)\\b" - statement: "\\b(enter|env|error make|every|exec|exit|explode)\\b" - statement: "\\b(export alias|export def|export def-env|export env)\\b" - statement: "\\b(export extern|export|expr-not|extern|fetch|fill-na)\\b" - statement: "\\b(fill-null|filter-with|find|first|flatten)\\b" - statement: "\\b(fmt|format filesize|format|for|from csv|from eml)\\b" - statement: "\\b(from ics|from ini|from json|from nuon|from ods|from ssv)\\b" - statement: "\\b(from toml|from tsv|from url|from vcf|from xlsx|from xml)\\b" - statement: "\\b(from yaml|from yml|from|get-day|get-hour|get-minute)\\b" - statement: "\\b(get-month|get-nanosecond|get-ordinal|get-second|get-week)\\b" - statement: "\\b(get-weekday|get-year|get|glob|grid|group-by)\\b" - statement: "\\b(group|gstat|g|hash base64|hash md5|hash sha256|hash)\\b" - statement: "\\b(headers|help|hide|histogram|history|if|ignore)\\b" - statement: "\\b(inc|input|insert|into binary|into bool|into datetime|into decimal)\\b" - statement: "\\b(into duration|into filesize|into int|into string|into)\\b" - statement: "\\b(is-admin|is-duplicated|is-in|is-not-null)\\b" - statement: "\\b(is-null|is-unique|join|keep|keep until)\\b" - statement: "\\b(keep while|keybindings default|keybindings listen|keybindings list)\\b" - statement: "\\b(keybindings|kill|last|length|let-env|let)\\b" - statement: "\\b(lines|list|lit|load-env|ls|ls-df|match|math abs)\\b" - statement: "\\b(math avg|math ceil|math eval|math floor|math max)\\b" - statement: "\\b(math median|math min|math mode|math product|math round)\\b" - statement: "\\b(math sqrt|math stddev|math sum|math variance|math|max)\\b" - statement: "\\b(mean|median|melt|merge|metadata)\\b" - statement: "\\b(min|mkdir|module|move|mv|n|n-unique|n-unique)\\b" - statement: "\\b(nth|nu-highlight|open|open-df|otherwise|overlay)\\b" - statement: "\\b(overlay add|overlay list|overlay new|overlay remove|p)\\b" - statement: "\\b(par-each|parse|path basename|path dirname|path exists)\\b" - statement: "\\b(path expand|path join|path parse|path relative-to|path split)\\b" - statement: "\\b(path type|path|pivot|post|prepend|print|ps|quantile)\\b" - statement: "\\b(quantile|query json|query web|query xml|query|random bool)\\b" - statement: "\\b(random chars|random decimal|random dice|random integer)\\b" - statement: "\\b(random uuid|random|range|reduce|register|reject|rename)\\b" - statement: "\\b(replace|replace-all|reverse|reverse|rm|roll down)\\b" - statement: "\\b(roll left|roll right|roll up|rolling|roll|rotate)\\b" - statement: "\\b(run-external|sample|save|select|select|seq|seq char)\\b" - statement: "\\b(seq date|set|set-with-idx|shape|shells|shift|shuffle)\\b" - statement: "\\b(size|skip until|skip while|skip|sleep|slice|sort)\\b" - statement: "\\b(sort-by|source|split chars|split column|split row)\\b" - statement: "\\b(split-by|split|std|std|str camel-case|str capitalize)\\b" - statement: "\\b(str collect|str contains|str downcase|str ends-with|str find-replace)\\b" - statement: "\\b(str index-of|str kebab-case|str length|str lpad|str pascal-case)\\b" - statement: "\\b(str replace|str reverse|str rpad|str screaming-snake-case)\\b" - statement: "\\b(str snake-case|str starts-with|str substring|str title-case)\\b" - statement: "\\b(str to-datetime|str to-decimal|str to-int|str trim|str upcase)\\b" - statement: "\\b(str-lengths|str-slice|strftime|str|sum|sys|table)\\b" - statement: "\\b(take until|take while|take|term size|to csv)\\b" - statement: "\\b(to html|to json|to md|to nuon|to text|to toml|to tsv)\\b" - statement: "\\b(to url|to xml|to yaml|to-csv|to-df|to-dummies|to-lazy)\\b" - statement: "\\b(to-lowercase|to-nu|to-parquet|to-uppercase|touch|to)\\b" - statement: "\\b(transpose|tutor|unalias|uniq|unique|update|update cells)\\b" - statement: "\\b(upsert|url host|url path|url query|url scheme|url)\\b" - statement: "\\b(use|value-counts|var|version|view-source|watch)\\b" - statement: "\\b(when|where|which|window|with-column|with-env|wrap)\\b" # https://www.nushell.sh/book/types_of_data.html#booleans - constant: "\\b(false|true)\\b" - constant.number: "\\b[-+]?([1-9][0-9])*\\b" # https://www.nushell.sh/book/types_of_data.html#binary-data - constant.number: "\\b[-+]?(0(x|b|o)\\[[0-9a-fA-F ]+\\])" # https://www.nushell.sh/book/types_of_data.html#file-sizes - constant.number: "\\b[-+]?([0-9]+[BbMmGgTtPp][i]?[Bb]?)?\\b" # https://www.nushell.sh/book/types_of_data.html#duration - constant.number: "\\b[-+]?([0-9]+[num]?[s])?\\b" - constant.number: "\\b[-+]?([0-9]+(sec|min|hr|day|wk))?\\b" # https://www.nushell.sh/book/types_of_data.html#dates - constant.number: "\\b([0-9]+[-][0-9]+[-][0-9]+([T][0-9]+[:][0-9]+[:][0-9]+)?([\\+][0-9]+[:][0-9]+)?)\\b" # https://www.nushell.sh/book/types_of_data.html#ranges - constant.number: "([0-9]+(\\.\\.)[0-9]+)?" # https://www.nushell.sh/book/types_of_data.html#open-ended-ranges - constant.number: "((\\.\\.)[0-9]+)?" - constant.number: "([0-9]+(\\.\\.))?" - comment: start: "#" end: "$" rules: [] - comment: start: "/\\*" end: "\\*/" rules: - todo: "(FIXME|TODO|NOTE):?" - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\." - constant.string: start: "'" end: "'" skip: "\\\\." rules: - constant.specialChar: "\\\\." micro-2.0.14/runtime/syntax/objc.yaml0000664000175000017510000000501614663411671017065 0ustar nileshnileshfiletype: objective-c detect: filename: "\\.(m|mm|h)$" signature: "(obj|objective)-c|#import|@(encode|end|interface|implementation|selector|protocol|synchronized|try|catch|finally|property|optional|required|import|autoreleasepool)" rules: - type: "\\b(float|double|CGFloat|id|bool|BOOL|Boolean|char|int|short|long|sizeof|enum|void|static|const|struct|union|typedef|extern|(un)?signed|inline|Class|SEL|IMP|NS(U)?Integer)\\b" - type: "\\b((s?size)|((u_?)?int(8|16|32|64|ptr)))_t\\b" - type: "\\b[A-Z][A-Z][[:alnum:]]*\\b" - type: "\\b[A-Za-z0-9_]*_t\\b" - type: "\\bdispatch_[a-zA-Z0-9_]*_t\\b" - statement: "(__attribute__[[:space:]]*\\(\\([^)]*\\)\\)|__(aligned|asm|builtin|hidden|inline|packed|restrict|section|typeof|weak)__|__unused|_Nonnull|_Nullable|__block|__builtin.*)" - statement: "\\b(class|namespace|template|public|protected|private|typename|this|friend|virtual|using|mutable|volatile|register|explicit)\\b" - statement: "\\b(for|if|while|do|else|case|default|switch)\\b" - statement: "\\b(try|throw|catch|operator|new|delete)\\b" - statement: "\\b(goto|continue|break|return)\\b" - statement: "\\b(nonatomic|atomic|readonly|readwrite|strong|weak|assign)\\b" - statement: "@(encode|end|interface|implementation|class|selector|protocol|synchronized|try|catch|finally|property|optional|required|import|autoreleasepool)" - preproc: "^[[:space:]]*#[[:space:]]*(define|include|import|(un|ifn?)def|endif|el(if|se)|if|warning|error|pragma).*$" - preproc: "__[A-Z0-9_]*__" - special: "^[[:space:]]*[#|@][[:space:]]*(import|include)[[:space:]]*[\"|<].*\\/?[>|\"][[:space:]]*$" - statement: "([.:;,+*|=!\\%\\[\\]]|<|>|/|-|&)" - constant.number: "(\\b(-?)?[0-9]+\\b|\\b\\[0-9]+\\.[0-9]+\\b|\\b0x[0-9a-fA-F]+\\b)" - constant: "(@\\[(\\\\.|[^\\]])*\\]|@\\{(\\\\.|[^\\}])*\\}|@\\((\\\\.|[^\\)])*\\))" - constant: "\\b<(\\\\.[^\\>])*\\>\\b" - constant: "\\b(nil|NULL|YES|NO|TRUE|true|FALSE|false|self)\\b" - constant: "\\bk[[:alnum]]*\\b" - constant.string: "'.'" - constant.string: start: "@\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\." - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\." - comment: start: "//" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" - comment: start: "/\\*" end: "\\*/" rules: - todo: "(TODO|XXX|FIXME):?" micro-2.0.14/runtime/syntax/ocaml.yaml0000664000175000017510000000277014663411671017247 0ustar nileshnileshfiletype: ocaml detect: filename: "\\.mli?$" rules: - identifier: "\\b[A-Z][0-9a-z_]{2,}\\b" #declarations - statement: "\\b(let|val|method|in|and|rec|private|virtual|constraint)\\b" #structure items - type: "\\b(type|open|class|module|exception|external)\\b" #patterns - statement: "\\b(fun|function|functor|match|try|with)\\b" #patterns-modifiers - statement: "\\b(as|when|of)\\b" #conditions - statement: "\\b(if|then|else)\\b" #blocs - type: "\\b(begin|end|object|struct|sig|for|while|do|done|to|downto)\\b" - type: "'[0-9A-Za-z_]+" #constantes - constant.bool: "\\b(true|false)\\b" #modules/classes - special: "\\b(include|inherit|initializer)\\b" #expr modifiers - special: "\\b(new|ref|mutable|lazy|assert|raise)\\b" #character literal - constant.string: "'(\\\\[0-7]{3}|\\\\x[A-Fa-f0-9]{2}|\\\\u[A-Fa-f0-9]{4}|\\\\U[A-Fa-f0-9]{8}|\\\\[abfnrtv'\\\"\\\\]|.)'" - constant.specialChar: "\\\\[abfnrtv'\\\"\\\\]" - constant.specialChar: "\\\\([0-7]{3}|x[A-Fa-f0-9]{2}|u[A-Fa-f0-9]{4}|U[A-Fa-f0-9]{8})" #string literal - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "%." - constant.specialChar: "\\\\[abfnrtv'\\\"\\\\]" - constant.specialChar: "\\\\([0-7]{3}|x[A-Fa-f0-9]{2}|u[A-Fa-f0-9]{4}|U[A-Fa-f0-9]{8})" - comment: start: "\\(\\*" end: "\\*\\)" rules: [] micro-2.0.14/runtime/syntax/octave.yaml0000664000175000017510000000503214663411671017427 0ustar nileshnilesh# References # https://github.com/zyedidia/micro/blob/master/runtime/syntax/go.yaml # https://github.com/vim-scripts/octave.vim--/blob/master/syntax/octave.vim # # TODO # include only needed operators # ... highlighting # built-in function highlighting? # highlight eps/pi/e etc. as functions when followed by () # what are skip and error fields in strings? # multiline comments not working filetype: octave detect: filename: "\\.m$" rules: # Statements https://www.gnu.org/software/octave/doc/v4.0.0/Statements.html - statement: "\\b(function|endfunction|return|end|global|persistent)\\b" - statement: "\\b(if|elseif|else|endif|switch|case|otherwise|endswitch)\\b" - statement: "\\b(while|endwhile|do|until|for|endfor|parfor|endparfor|break|continue)\\b" - statement: "\\b(unwind_protect|unwind_protect_cleanup|end_unwind_protect|try|catch|end_try_catch)\\b" # Operators - symbol.operator: "[-+/*=<>!~%&|^]|:=" # Brackets - symbol.brackets: "(\\{|\\})" - symbol.brackets: "(\\(|\\))" - symbol.brackets: "(\\[|\\])" # Commas - symbol: "," # Numbers https://www.gnu.org/software/octave/doc/v4.0.1/Mathematical-Constants.html - constant.number: "\\b([0-9]+|0x[0-9a-fA-F]*)\\b|'.'" - constant.number: "\\b(pi|e|I|Inf|NaN|eps|realmax|realmin)\\b|" # Boolean - constant.bool: "\\b(true|false)\\b" # Strings https://www.gnu.org/software/octave/doc/v4.0.1/Strings.html - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "%" - constant.specialChar: "\\\\[abfnrtv'\\\"\\\\]" - constant.specialChar: "\\\\([0-7]{3}|x[A-Fa-f0-9]{2}|u[A-Fa-f0-9]{4}|U[A-Fa-f0-9]{8})" - constant.string: start: "'" end: "'" skip: "\\\\." rules: - error: "..+" - constant.specialChar: "%" - constant.specialChar: "\\\\[abfnrtv'\\\"\\\\]" - constant.specialChar: "\\\\([0-7]{3}|x[A-Fa-f0-9]{2}|u[A-Fa-f0-9]{4}|U[A-Fa-f0-9]{8})" # Comments https://www.gnu.org/software/octave/doc/v4.2.1/Comments.html - comment: start: "%" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" - comment: start: "#" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" - comment: start: "%{" end: "%}" rules: - todo: "(TODO|XXX|FIXME):?" - comment: start: "#{" end: "#}" rules: - todo: "(TODO|XXX|FIXME):?" micro-2.0.14/runtime/syntax/odin.yaml0000664000175000017510000000441114663411671017077 0ustar nileshnileshfiletype: odin detect: filename: "\\.odin$" rules: # Conditionals and control flow - special: "\\b(asm|auto_cast|break|case|cast|context|continue|do|dynamic|fallthrough|return|transmute|using|where)\\b" - statement: "\\b(else|for|if|switch|in|not_in|or_else|or_return|when)\\b" - preproc: "\\b(assert|package|foreign|import|proc|defer|make|new|free|delete|copy|len|cap|append|raw_data)\\b" - preproc: "\\b((size|align|offset|type|type_info|typeid)_of|offset_of_by_string)\\b" - preproc: "\\b(swizzle|complex|quaternion|real|imag|jmag|kmag|conj|expand_to_tuple|min|max|abs|clamp|soa_zip|soa_unzip|transpose|outer_product|hadamard_product|matrix_flatten)\\b" - symbol.operator: "[-+/*=<>!~%&|^@]|:\\s*=|:\\s*:|\\?" # Types - symbol: "(,|\\.)" - type: "\\b(b(8|16|32|64)|(i|u)(8|(16|32|64|128)(le|be)?)|f(16|32|64)(le|be)?|complex(32|64|128)|quaternion(64|128|256))\\b" - type: "\\b(any|bool|byte|rune|u?int|uintptr|rawptr|c?string|map|matrix|typeid)\\b" - type.keyword: "\\b(distinct|struct|enum|union|bit_set)\\b" - constant.bool: "\\b(true|false|nil)\\b" # Brackets - symbol.brackets: "(\\{|\\})" - symbol.brackets: "(\\(|\\))" - symbol.brackets: "(\\[|\\])" # Numbers and strings - constant.number: "\\b(0b[01]*|0o[0-7]*|0x[0-9a-fA-F]*|[0-9_]+|0d[0-9]*|0z[0-9abAB]*)\\b|'.'" - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "%." - constant.specialChar: "\\\\[abfnrtv'\\\"\\\\]" - constant.specialChar: "\\\\([0-7]{1,3}|x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8})" - constant.string: start: "'" end: "'" skip: "\\\\." rules: - error: "..+" - constant.specialChar: "%." - constant.specialChar: "\\\\[abfnrtv'\\\"\\\\]" - constant.specialChar: "\\\\([0-7]{1,3}|x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8})" - constant.string: start: "`" end: "`" rules: [] - comment: start: "//" end: "$" rules: - todo: "TODO:?|NOTE(\\(.*\\))?:?" - comment: start: "/\\*" end: "\\*/" rules: - todo: "TODO:?|NOTE(\\(.*\\))?:?" micro-2.0.14/runtime/syntax/pascal.yaml0000664000175000017510000000347714663411671017424 0ustar nileshnileshfiletype: pascal detect: filename: "\\.pas$" rules: - type: "\\b(?i:(string|ansistring|widestring|shortstring|char|ansichar|widechar|boolean|byte|shortint|word|smallint|longword|cardinal|longint|integer|int64|single|currency|double|extended))\\b" - statement: "\\b(?i:(and|asm|array|begin|break|case|const|constructor|continue|destructor|div|do|downto|else|end|file|for|function|goto|if|implementation|in|inline|interface|label|mod|not|object|of|on|operator|or|packed|procedure|program|record|repeat|resourcestring|set|shl|shr|then|to|type|unit|until|uses|var|while|with|xor))\\b" - statement: "\\b(?i:(as|class|dispose|except|exit|exports|finalization|finally|inherited|initialization|is|library|new|on|out|property|raise|self|threadvar|try))\\b" - statement: "\\b(?i:(absolute|abstract|alias|assembler|cdecl|cppdecl|default|export|external|forward|generic|index|local|name|nostackframe|oldfpccall|override|pascal|private|protected|public|published|read|register|reintroduce|safecall|softfloat|specialize|stdcall|virtual|write))\\b" - constant: "\\b(?i:(false|true|nil))\\b" - special: start: "asm" end: "end" rules: [] - constant.number: "\\$[0-9A-Fa-f]+" - constant.number: "\\b[+-]?[0-9]+([.]?[0-9]+)?(?i:e[+-]?[0-9]+)?" - constant.string: start: "#[0-9]{1,}" end: "$" rules: - constant.specialChar: "\\\\." - constant.string: start: "'" end: "'" skip: "\\\\." rules: - constant.specialChar: "\\\\." - preproc: start: "{\\$" end: "}" rules: [] - comment: start: "//" end: "$" rules: [] - comment: start: "\\(\\*" end: "\\*\\)" rules: [] - comment: start: "({)(?:[^$])" end: "}" rules: [] micro-2.0.14/runtime/syntax/patch.yaml0000664000175000017510000000041014663411671017240 0ustar nileshnileshfiletype: patch detect: filename: "\\.(patch|diff)$" header: "^diff" rules: - brightgreen: "^\\+.*" - green: "^\\+\\+\\+.*" - brightblue: "^ .*" - brightred: "^-.*" - red: "^---.*" - brightyellow: "^@@.*" - magenta: "^diff.*" micro-2.0.14/runtime/syntax/peg.yaml0000664000175000017510000000074714663411671016731 0ustar nileshnileshfiletype: peg detect: filename: "\\.l?peg$" rules: - identifier: "^[[:space:]]*[A-Za-z][A-Za-z0-9_]*[[:space:]]*<-" - constant.number: "\\^[+-]?[0-9]+" - symbol.operator: "[-+*?^/!&]|->|<-|=>" - identifier.var: "%[A-Za-z][A-Za-z0-9_]*" - special: "\\[[^]]*\\]" - constant.string: "\"(\\\\.|[^\"])*\"|'(\\\\.|[^'])*'" - comment: "(^|[[:space:]])\\-\\-.*$" - todo: "TODO:?" - indent-char.whitespace: "[[:space:]]+$" - indent-char: " + +| + +" micro-2.0.14/runtime/syntax/perl.yaml0000664000175000017510000000446414663411671017120 0ustar nileshnileshfiletype: perl detect: filename: "\\.p[lmp]$" header: "^#!.*/(env +)?perl( |$)" rules: - type: "\\b(accept|alarm|atan2|bin(d|mode)|c(aller|homp|h(dir|mod|op|own|root)|lose(dir)?|onnect|os|rypt)|d(bm(close|open)|efined|elete|ie|o|ump)|e(ach|of|val|x(ec|ists|it|p))|f(cntl|ileno|lock|ork))\\b|\\b(get(c|login|peername|pgrp|ppid|priority|pwnam|(host|net|proto|serv)byname|pwuid|grgid|(host|net)byaddr|protobynumber|servbyport)|([gs]et|end)(pw|gr|host|net|proto|serv)ent|getsock(name|opt)|gmtime|goto|grep|hex|index|int|ioctl|join)\\b|\\b(keys|kill|last|length|link|listen|local(time)?|log|lstat|m|mkdir|msg(ctl|get|snd|rcv)|next|oct|open(dir)?|ord|pack|pipe|pop|printf?|push|q|qq|qx|rand|re(ad(dir|link)?|cv|say|do|name|quire|set|turn|verse|winddir)|rindex|rmdir|s|scalar|seek(dir)?)\\b|\\b(se(lect|mctl|mget|mop|nd|tpgrp|tpriority|tsockopt)|shift|shm(ctl|get|read|write)|shutdown|sin|sleep|socket(pair)?|sort|spli(ce|t)|sprintf|sqrt|srand|stat|study|substr|symlink|sys(call|read|tem|write)|tell(dir)?|time|tr(y)?|truncate|umask)\\b|\\b(un(def|link|pack|shift)|utime|values|vec|wait(pid)?|wantarray|warn|write)\\b" - statement: "\\b(continue|else|elsif|do|for|foreach|if|unless|until|while|eq|ne|lt|gt|le|ge|cmp|x|my|sub|use|package|can|isa)\\b" - special: "\\-\\>" - symbol: "(,|\\.)" #regexes - identifier.macro: "m?\\/.*?\\/[a-z]*" - identifier.macro: "m?\\|.*?\\|[a-z]*" - identifier.macro: "\\bs/.*?/.*?/[a-z]*" - identifier.macro: "\\bs\\|.*?\\|.*?\\|[a-z]*" - constant.string: start: '"' end: '"' skip: '\\"' rules: - identifier.var: '[\\$@%].[a-zA-Z0-9_]*' - constant.string: start: "'" end: "'" skip: "\\\\'" rules: [] - comment: start: "#" end: "$" rules: [] - constant.string: "\"\\(.*\\)\"|qq?\\|.*\\||qq?\\{.*\\}|qq?\\/.*\\/" - constant.number: "\\b([0-9]*[.])?[0-9]+" - constant.number: "\\b[0-9]+" - constant.number: "\\b0x[a-f0-9]+" - constant.string.url: "`(.+?)`" - identifier.var: '[\\$@%].[a-zA-Z0-9_]*' - preproc: start: "(^use| = new)" end: ";" rules: [] - comment: start: "^=" end: "^=cut" rules: [] - identifier.macro: start: "<< 'STOP'" end: "STOP" rules: [] micro-2.0.14/runtime/syntax/php.yaml0000664000175000017510000000575214663411671016746 0ustar nileshnileshfiletype: php detect: filename: "\\.php[2345s~]?$" rules: - symbol.operator: "<|>" - error: "<[^!].*?>" - symbol.tag: "(?i)<[/]?(a(bbr|cronym|ddress|pplet|rea|rticle|side|udio)?|b(ase(font)?|d(i|o)|ig|lockquote|r)?|ca(nvas|ption)|center|cite|co(de|l|lgroup)|d(ata(list)?|d|el|etails|fn|ialog|ir|l|t)|em(bed)?|fieldset|fig(caption|ure)|font|form|(i)?frame|frameset|h[1-6]|hr|i|img|in(put|s)|kbd|keygen|label|legend|li(nk)?|ma(in|p|rk)|menu(item)?|met(a|er)|nav|no(frames|script)|o(l|pt(group|ion)|utput)|p(aram|icture|re|rogress)?|q|r(p|t|uby)|s(trike)?|samp|se(ction|lect)|small|source|span|strong|su(b|p|mmary)|textarea|time|track|u(l)?|var|video|wbr)( .*|>)*?>" - symbol.tag.extended: "(?i)<[/]?(body|div|html|head(er)?|footer|title|table|t(body|d|h(ead)?|r|foot))( .*|>)*?>" - preproc: "(?i)<[/]?(script|style)( .*|>)*?>" - preproc: "<\\?(php|=)?" - preproc: "\\?>" - preproc: "" - special: "&[^;[[:space:]]]*;" - symbol: "[:=]" - identifier: "(alt|bgcolor|height|href|label|longdesc|name|onclick|onfocus|onload|onmouseover|size|span|src|style|target|type|value|width)=" - constant.number: "(?i)#[0-9a-fA-F]{6,6}" - constant.string.url: "(ftp(s)?|http(s)?|git|chrome)://[^ ]+" - comment: "" - default: "<\\?(php|=)\" end=\"\\?>" - identifier.class: "([a-zA-Z0-9_-]+)\\(" - type: "\\b(array|bool|callable|float|int|iterable|object|mixed|string|void)\\b" - identifier.class: "[a-zA-Z\\\\]+::" - identifier: "\\b([A-Z][a-zA-Z0-9_]+)\\b" - identifier: "([A-Z0-9_]+)[;|\\s|\\)|,]" - type.keyword: "\\b(global|final|public|private|protected|static|const|var)\\b" - statement: "\\b(abstract|catch|class|declare|do|else(if)?|end(declare|for(each)?|if|switch|while)|enum|finally|for(each)|function|if|interface|namespace|switch|trait|try|while)\\b" - identifier: "\\bnew\\s+([a-zA-Z0-9\\\\]+)" - special: "\\b(as|and|break|case|clone|continue|default|die|fn|echo|empty|eval|exit|extends|goto|or|include(_once)?|implements|instanceof|insteadof|isset|list|match|new|print|return|require(_once)?|unset|use|throw|xor|yield(\\s+from))\\b" - constant.bool: "\\b(true|false|null|TRUE|FALSE|NULL)\\b" - constant: "[\\s|=|\\s|\\(|/|+|-|\\*|\\[]" - constant.number: "[0-9]" - identifier: "(\\$this|parent|self|\\$this->)" - symbol.operator: "(=>|===|!==|==|!=|&&|\\|\\||::|=|->|\\!)" - identifier.var: "(\\$[a-zA-Z0-9\\-_]+)" - symbol.operator: "[\\(|\\)|/|+|\\-|\\*|\\[|.|,|;]" - symbol.brackets: "(\\[|\\]|\\{|\\}|[()])" - comment: start: "(^|[[:space:]])*(//|#)" end: "$" rules: [] - comment: start: "/\\*" end: "\\*/" rules: [] - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\[abfnrtv'\\\"\\\\]" - constant.string: start: "\'" end: "\'" skip: "\\\\." rules: - constant.specialChar: "\\\\[abfnrtv'\\\"\\\\]" micro-2.0.14/runtime/syntax/pkg-config.yaml0000664000175000017510000000051014663411671020166 0ustar nileshnileshfiletype: pc detect: filename: "\\.pc$" rules: - preproc: "^(Name|Description|URL|Version|Conflicts|Cflags):" - preproc: "^(Requires|Libs)(\\.private)?:" - symbol.operator: "=" - identifier.var: "\\$\\{[A-Za-z_][A-Za-z0-9_]*\\}" - indent-char.whitespace: "[[:space:]]+$" - indent-char: " + +| + +" micro-2.0.14/runtime/syntax/po.yaml0000664000175000017510000000045214663411671016565 0ustar nileshnileshfiletype: po detect: filename: "\\.pot?$" rules: - preproc: "\\b(msgid|msgstr)\\b" - constant.string: "\"(\\\\.|[^\"])*\"|'(\\\\.|[^'])*'" - special: "\\\\.?" - comment: "(^|[[:space:]])#([^{].*)?$" - indent-char.whitespace: "[[:space:]]+$" - indent-char: " + +| + +" micro-2.0.14/runtime/syntax/pony.yaml0000664000175000017510000000241414663411671017134 0ustar nileshnileshfiletype: pony detect: filename: "\\.pony$" rules: - statement: "\\b(type|interface|trait|primitive|class|struct|actor)\\b" - statement: "\\b(compiler_intrinsic)\\b" - statement: "\\b(use)\\b" - statement: "\\b(var|let|embed)\\b" - statement: "\\b(new|be|fun)\\b" - statement: "\\b(iso|trn|ref|val|box|tag|consume)\\b" - statement: "\\b(break|continue|return|error)\\b" - statement: "\\b(if|then|elseif|else|end|match|where|try|with|as|recover|object|lambda|as|digestof|ifdef)\\b" - statement: "\\b(while|do|repeat|until|for|in)\\b" - statement: "(\\?|=>)" - statement: "(\\||\\&|\\,|\\^)" - symbol.operator: "(\\-|\\+|\\*|/|\\!|%|<<|>>)" - symbol.operator: "(==|!=|<=|>=|<|>)" - statement: "\\b(is|isnt|not|and|or|xor)\\b" - type: "\\b(_*[A-Z][_a-zA-Z0-9\\']*)\\b" - constant: "\\b(this)\\b" - constant.bool: "\\b(true|false)\\b" - constant.number: "\\b((0b[0-1_]*)|(0o[0-7_]*)|(0x[0-9a-fA-F_]*)|([0-9_]+(\\.[0-9_]+)?((e|E)(\\\\+|-)?[0-9_]+)?))\\b" - constant.string: "\"(\\\\.|[^\"])*\"" - comment: start: "\"\"\"[^\"]*" end: "\"\"\"" rules: [] - comment: "(^|[[:space:]])//.*" - comment: start: "/\\*" end: "\\*/" rules: [] - todo: "TODO:?" micro-2.0.14/runtime/syntax/pov.yaml0000664000175000017510000000131514663411671016752 0ustar nileshnileshfiletype: pov detect: filename: "\\.(pov|POV|povray|POVRAY)$" rules: - preproc: "^[[:space:]]*#[[:space:]]*(declare)" - statement: "\\b(sphere|cylinder|translate|matrix|rotate|scale)\\b" - statement: "\\b(orthographic|location|up|right|direction|clipped_by)\\b" - statement: "\\b(fog_type|fog_offset|fog_alt|rgb|distance|transform)\\b" - identifier: "^\\b(texture)\\b" - identifier: "\\b(light_source|background)\\b" - identifier: "\\b(fog|object|camera)\\b" - symbol.operator: "(\\{|\\}|\\(|\\)|\\;|\\]|\\[|`|\\\\|\\$|<|>|!|=|&|\\|)" - special: "\\b(union|group|subgroup)\\b" - comment: "//.*" - comment: start: "/\\*" end: "\\*/" rules: [] micro-2.0.14/runtime/syntax/privoxy-action.yaml0000664000175000017510000000315114663411671021141 0ustar nileshnileshfiletype: privoxy-action detect: filename: "\\.action$" rules: - constant.bool.false: "[{[:space:]]\\-block([[:space:]{}]|$)" - constant.bool.true: "[{[:space:]]\\+block([[:space:]{}]|$)" - constant.bool.false: "-(add-header|change-x-forwarded-for|client-header-filter|client-header-tagger|content-type-overwrite|crunch-client-header|crunch-if-none-match|crunch-incoming-cookies|crunch-outgoing-cookies|crunch-server-header|deanimate-gifs|downgrade-http-version|fast-redirects|filter|force-text-mode|forward-override|handle-as-empty-document|handle-as-image|hide-accept-language|hide-content-disposition|hide-from-header|hide-if-modified-since|hide-referrer|hide-user-agent|limit-connect|overwrite-last-modified|prevent-compression|redirect|server-header-filter|server-header-tagger|session-cookies-only|set-image-blocker)" - constant.bool.true: "\\+(add-header|change-x-forwarded-for|client-header-filter|client-header-tagger|content-type-overwrite|crunch-client-header|crunch-if-none-match|crunch-incoming-cookies|crunch-outgoing-cookies|crunch-server-header|deanimate-gifs|downgrade-http-version|fast-redirects|filter|force-text-mode|forward-override|handle-as-empty-document|handle-as-image|hide-accept-language|hide-content-disposition|hide-from-header|hide-if-modified-since|hide-referrer|hide-user-agent|limit-connect|overwrite-last-modified|prevent-compression|redirect|server-header-filter|server-header-tagger|session-cookies-only|set-image-blocker)" - constant.specialChar: "\\\\.?" - comment: "(^|[[:space:]])#([^{].*)?$" - indent-char.whitespace: "[[:space:]]+$" - indent-char: " + +| + +" micro-2.0.14/runtime/syntax/privoxy-config.yaml0000664000175000017510000000153714663411671021137 0ustar nileshnileshfiletype: privoxy-config detect: filename: "privoxy/config$" rules: - statement: "(accept-intercepted-requests|actionsfile|admin-address|allow-cgi-request-crunching|buffer-limit|compression-level|confdir|connection-sharing|debug|default-server-timeout|deny-access|enable-compression|enable-edit-actions|enable-remote-http-toggle|enable-remote-toggle|enforce-blocks|filterfile|forward|forwarded-connect-retries|forward-socks4|forward-socks4a|forward-socks5|handle-as-empty-doc-returns-ok|hostname|keep-alive-timeout|listen-address|logdir|logfile|max-client-connections|permit-access|proxy-info-url|single-threaded|socket-timeout|split-large-forms|templdir|toggle|tolerate-pipelining|trustfile|trust-info-url|user-manual)[[:space:]]" - comment: "(^|[[:space:]])#([^{].*)?$" - indent-char.whitespace: "[[:space:]]+$" - indent-char: " + +| + +" micro-2.0.14/runtime/syntax/privoxy-filter.yaml0000664000175000017510000000072014663411671021150 0ustar nileshnileshfiletype: privoxy-filter detect: filename: "\\.filter$" rules: - statement: "^(FILTER|CLIENT-HEADER-FILTER|CLIENT-HEADER-TAGGER|SERVER-HEADER-FILTER|SERVER-HEADER-TAGGER): [a-z-]+" - identifier: "^(FILTER|CLIENT-HEADER-FILTER|CLIENT-HEADER-TAGGER|SERVER-HEADER-FILTER|SERVER-HEADER-TAGGER):" - constant.specialChar: "\\\\.?" - comment: "(^|[[:space:]])#([^{].*)?$" - indent-char.whitespace: "[[:space:]]+$" - indent-char: " + +| + +" micro-2.0.14/runtime/syntax/proto.yaml0000664000175000017510000000201114663411671017303 0ustar nileshnileshfiletype: proto detect: filename: "(\\.(proto)$$)" rules: - identifier: "\\b[A-Z_][0-9A-Z_]+\\b" - type: "\\b(int(8|16|32|64))|string|bytes|repeated|bool|required|map|optional|oneof|union\\b" - statement: "\\b(import|service|enum|syntax|package|option|message|rpc|returns|extensions|to)\\b" - constant: "'\\\\(([0-3]?[0-7]{1,2}))'" - constant: "'\\\\x[0-9A-Fa-f]{1,2}'" - symbol.brackets: "[(){}]|\\[|\\]" - constant.number: "(\\b[0-9]+\\b|\\b0x[0-9A-Fa-f]+\\b)" - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\." - constant.string: start: "'" end: "'" skip: "\\\\." rules: - preproc: "..+" - constant.specialChar: "\\\\." - comment: start: "//" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" - comment: start: "/\\*" end: "\\*/" rules: - todo: "(TODO|XXX|FIXME):?" micro-2.0.14/runtime/syntax/puppet.yaml0000664000175000017510000000277514663411671017476 0ustar nileshnileshfiletype: puppet detect: filename: "\\.pp$" rules: - default: "^[[:space:]]([a-z][a-z0-9_]+)" - identifier.var: "\\$[a-z:][a-z0-9_:]+" - type: "\\b(augeas|computer|cron|exec|file|filebucket|group|host|interface|k5login|macauthorization|mailalias|maillist|mcx|mount|nagios_command|nagios_contact|nagios_contactgroup|nagios_host|nagios_hostdependency|nagios_hostescalation|nagios_hostextinfo|nagios_hostgroup|nagios_service|nagios_servicedependency|nagios_serviceescalation|nagios_serviceextinfo|nagios_servicegroup|nagios_timeperiod|notify|package|resources|router|schedule|scheduled_task|selboolean|selmodule|service|ssh_authorized_key|sshkey|stage|tidy|user|vlan|yumrepo|zfs|zone|zpool|anchor)\\b" - statement: "\\b(class|define|if|else|undef|inherits)\\b" - symbol: "(=|-|~|>)" - identifier.var: "(\\$|@|@@)?\\b[A-Z]+[0-9A-Z_a-z]*" - symbol: "([ ]|^):[0-9A-Z_]+\\b" - constant: "/([^/]|(\\\\/))*/[iomx]*|%r\\{([^}]|(\\\\}))*\\}[iomx]*" - constant.string: "`[^`]*`|%x\\{[^}]*\\}" - constant.string: "\"([^\"]|(\\\\\"))*\"|%[QW]?\\{[^}]*\\}|%[QW]?\\([^)]*\\)|%[QW]?<[^>]*>|%[QW]?\\[[^]]*\\]|%[QW]?\\$[^$]*\\$|%[QW]?\\^[^^]*\\^|%[QW]?![^!]*!" - special: "\\$\\{[^}]*\\}" - constant.string: "'([^']|(\\\\'))*'|%[qw]\\{[^}]*\\}|%[qw]\\([^)]*\\)|%[qw]<[^>]*>|%[qw]\\[[^]]*\\]|%[qw]\\$[^$]*\\$|%[qw]\\^[^^]*\\^|%[qw]![^!]*!" - comment: "#[^{].*$|#$" - comment.bright: "##[^{].*$|##$" - todo: "(XXX|TODO|FIXME|\\?\\?\\?)" - indent-char.whitespace: "[[:space:]]+$" micro-2.0.14/runtime/syntax/python2.yaml0000664000175000017510000000500114663411671017545 0ustar nileshnileshfiletype: python2 detect: filename: "\\.py2$" header: "^#!.*/(env +)?python2$" rules: # built-in objects - constant: "\\b(None|self|True|False)\\b" # built-in attributes - constant: "\\b(__bases__|__builtin__|__class__|__debug__|__dict__|__doc__|__file__|__members__|__methods__|__name__|__self__)\\b" # built-in functions - identifier: "\\b(abs|apply|callable|chr|cmp|compile|delattr|dir|divmod|eval|exec|execfile|filter|format|getattr|globals|hasattr|hash|help|hex|id|input|intern|isinstance|issubclass|len|locals|max|min|next|oct|open|ord|pow|range|raw_input|reduce|reload|repr|round|setattr|unichr|vars|zip|__import__)\\b" # special method names - identifier: "\\b(__abs__|__add__|__and__|__call__|__cmp__|__coerce__|__complex__|__concat__|__contains__|__del__|__delattr__|__delitem__|__dict__|__delslice__|__div__|__divmod__|__float__|__getattr__|__getitem__|__getslice__|__hash__|__hex__|__init__|__int__|__inv__|__invert__|__len__|__long__|__lshift__|__mod__|__mul__|__neg__|__nonzero__|__oct__|__or__|__pos__|__pow__|__radd__|__rand__|__rcmp__|__rdiv__|__rdivmod__|__repeat__|__repr__|__rlshift__|__rmod__|__rmul__|__ror__|__rpow__|__rrshift__|__rshift__|__rsub__|__rxor__|__setattr__|__setitem__|__setslice__|__str__|__sub__|__xor__)\\b" # types - type: "\\b(basestring|bool|buffer|bytearray|bytes|classmethod|complex|dict|enumerate|file|float|frozenset|int|list|long|map|memoryview|object|property|reversed|set|slice|staticmethod|str|super|tuple|type|unicode|xrange)\\b" # definitions - identifier: "def [a-zA-Z_0-9]+" # keywords - statement: "\\b(and|as|assert|break|class|continue|def|del|elif|else|except|finally|for|from|global|if|import|in|is|lambda|not|or|pass|print|raise|return|try|while|with|yield)\\b" # decorators - preproc: "^\\s*@[^(]*" # operators - symbol.operator: "([.:;,+*|=!\\%@]|<|>|/|-|&)" # parentheses - symbol.brackets: "([(){}]|\\[|\\])" # numbers - constant.number: "\\b[0-9]+\\b" - constant.string: start: "\"\"\"" end: "\"\"\"" rules: [] - constant.string: start: "'''" end: "'''" rules: [] - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\." - constant.string: start: "'" end: "'" skip: "\\\\." rules: - constant.specialChar: "\\\\." - comment: start: "#" end: "$" rules: [] micro-2.0.14/runtime/syntax/python3.yaml0000664000175000017510000000545214663411671017560 0ustar nileshnileshfiletype: python detect: filename: "\\.py(3|w)?$" header: "^#!.*/(env +)?python(3)?$" rules: # built-in objects - constant: "\\b(Ellipsis|None|self|cls|True|False)\\b" # built-in attributes - constant: "\\b(__bases__|__builtin__|__class__|__debug__|__dict__|__doc__|__file__|__members__|__methods__|__name__|__self__)\\b" # built-in functions - identifier: "\\b(abs|all|any|ascii|bin|bool|breakpoint|bytearray|bytes|callable|chr|classmethod|compile|complex|delattr|dir|divmod|eval|exec|format|getattr|globals|hasattr|hash|help|hex|id|input|isinstance|issubclass|iter|len|locals|max|min|next|nonlocal|oct|open|ord|pow|print|repr|round|setattr|sorted|sum|vars|__import__)\\b" # special method names - identifier: "\\b__(abs|add|and|call|cmp|coerce|complex|concat|contains|delattr|delitem|delslice|del|dict|divmod|div|float|getattr|getitem|getslice|hash|hex|iadd|iand|iconcat|ifloordiv|ilshift|imatmul|imod|imul|init|int|invert|inv|ior|ipow|irshift|isub|iter|itruediv|ixor|len|long|lshift|mod|mul|neg|next|nonzero|oct|or|pos|pow|radd|rand|rcmp|rdivmod|rdiv|repeat|repr|rlshift|rmod|rmul|ror|rpow|rrshift|rshift|rsub|rxor|setattr|setitem|setslice|str|sub|xor)__\\b" # types - type: "\\b(bool|bytearray|bytes|classmethod|complex|dict|enumerate|filter|float|frozenset|int|list|map|memoryview|object|property|range|reversed|set|slice|staticmethod|str|super|tuple|type|zip)\\b" # definitions - identifier: "def [a-zA-Z_0-9]+" # keywords - statement: "\\b(and|as|assert|async|await|break|case|class|continue|def|del|elif|else|except|finally|for|from|global|if|import|in|is|lambda|match|nonlocal|not|or|pass|raise|return|try|while|with|yield)\\b" # decorators - preproc: "^\\s*@[^(]*" # operators - symbol.operator: "([~^.:;,+*|=!\\%@]|<|>|/|-|&)" # parentheses - symbol.brackets: "([(){}]|\\[|\\])" # numbers - constant.number: "\\b[0-9](_?[0-9])*(\\.([0-9](_?[0-9])*)?)?(e[0-9](_?[0-9])*)?\\b" # decimal - constant.number: "\\b0b(_?[01])+\\b" # bin - constant.number: "\\b0o(_?[0-7])+\\b" # oct - constant.number: "\\b0x(_?[0-9a-fA-F])+\\b" # hex - constant.string: start: "\"\"\"" end: "\"\"\"" rules: [] - constant.string: start: "'''" end: "'''" rules: [] - constant.string: start: "\"" end: "(\"|$)" skip: "\\\\." rules: - constant.specialChar: "\\\\." - constant.string: start: "'" end: "('|$)" skip: "\\\\." rules: - constant.specialChar: "\\\\." - comment: start: "#" end: "$" rules: # AKA Code tags (PEP 350) - todo: "(TODO|FIXME|HACK|BUG|NOTE|FAQ|MNEMONIC|REQ|RFE|IDEA|PORT|\\?\\?\\?|!!!|GLOSS|SEE|TODOC|STAT|RVD|CRED):?" micro-2.0.14/runtime/syntax/r.yaml0000664000175000017510000000146214663411671016412 0ustar nileshnileshfiletype: r detect: filename: "\\.(r|R)$" rules: - statement: "\\b(library|require|break|else|for|function|if|ifelse|in|next|names|switch|repeat|print|try|tryCatch|isTRUE|return|while)\\b" - constant: "\\b(T|TRUE|F|FALSE|NULL|Inf|NaN|NA|NA_integer_|NA_real_|NA_complex_|NA_character_)\\b" - constant.number: "(\\b[0-9]+\\b|\\b0x[0-9A-Fa-f]+\\b)" - symbol.operator: "([.:;,+*|=!\\%]|<|>|/|-|&|\\^|\\$)" - comment: start: "#" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\." - constant.string: start: "'" end: "'" skip: "\\\\." rules: - constant.specialChar: "\\\\." micro-2.0.14/runtime/syntax/raku.yaml0000664000175000017510000000401314663411671017106 0ustar nileshnileshfiletype: raku detect: filename: "(\\.p6$|\\.pl6$|\\.pm6$|\\.raku$|\\.rakumod$|\\.rakudoc$)" rules: - type: "\\b(accept|alarm|atan2|bin(d|mode)|c(aller|h(dir|mod|op|own|root)|lose(dir)?|onnect|os|rypt)|d(bm(close|open)|efined|elete|ie|o|ump)|e(ach|of|val|x(ec|ists|it|p))|f(cntl|ileno|lock|ork)|get(c|login|peername|pgrp|ppid|priority|pwnam|(host|net|proto|serv)byname|pwuid|grgid|(host|net)byaddr|protobynumber|servbyport)|([gs]et|end)(pw|gr|host|net|proto|serv)ent|getsock(name|opt)|gmtime|goto|grep|hex|index|int|ioctl|join|keys|kill|last|length|link|listen|local(time)?|log|lstat|m|mkdir|msg(ctl|get|snd|rcv)|next|oct|open(dir)?|ord|pack|pipe|pop|printf?|push|q|qq|qx|rand|re(ad(dir|link)?|cv|do|name|quire|set|turn|verse|winddir)|rindex|rmdir|s|scalar|seek|seekdir|se(lect|mctl|mget|mop|nd|tpgrp|tpriority|tsockopt)|shift|shm(ctl|get|read|write)|shutdown|sin|sleep|socket(pair)?|sort|spli(ce|t)|sprintf|sqrt|srand|stat|study|substr|symlink|sys(call|read|tem|write)|tell(dir)?|time|tr|y|truncate|umask|un(def|link|pack|shift)|utime|values|vec|wait(pid)?|wantarray|warn|write)\\b" - statement: "\\b(continue|else|elsif|do|for|foreach|if|unless|until|while|eq|ne|lt|gt|le|ge|cmp|x|my|sub|use|package|can|isa)\\b" - special: "\\b(has|is|class|role|given|when|BUILD|multi|returns|method|submethod|slurp|say|sub)\\b" - identifier: "[$@%&](\\.|!|\\^)?([[:alpha:]]|_)" - identifier: "[$@%&](\\.|!|^)?([[:alpha:]]|_)([[:alnum:]]|-|_)*([[:alnum:]]|_)" - identifier: "[$@%&](\\?|\\*)([A-Z])([A-Z]|-)*([A-Z])" - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\." - constant.string: start: "'" end: "'" skip: "\\\\." rules: - constant.specialChar: "\\\\." - preproc: start: "(^use| = new)" end: ";" rules: [] - identifier.macro: start: "<" - special: "^%(build$|changelog|check$|clean$|description)" - special: "^%(files|install$|package|prep$)" - special: "^%(pre|preun|pretrans|post|postun|posttrans)" - special: "^%(trigger|triggerin|triggerpostun|triggerun|verifyscript)" - comment: "(^|[[:space:]])#([^{].*)?$" - constant: "^\\*.*$" - indent-char.whitespace: "[[:space:]]+$" - indent-char: " + +| + +" - todo: "TODO:?" micro-2.0.14/runtime/syntax/ruby.yaml0000664000175000017510000000457714663411671017144 0ustar nileshnileshfiletype: ruby detect: filename: "\\.(rb|rake|gemspec)$|^(.*[\\/])?(Gemfile|config.ru|Rakefile|Capfile|Vagrantfile|Guardfile|Appfile|Fastfile|Pluginfile|Podfile|\\.?[Bb]rewfile)$" header: "^#!.*/(env +)?ruby( |$)" rules: - comment.bright: start: "##" end: "$" rules: - todo: "(XXX|TODO|FIXME|BUG|\\?\\?\\?)" - comment: start: "#" end: "$" rules: - todo: "(XXX|TODO|FIXME|BUG|\\?\\?\\?)" - statement: "\\b(BEGIN|END|alias|and|begin|break|case|class|def|defined\\?|do|else|elsif|end|ensure|for|if|in|module|next|nil|not|or|private|protected|public|redo|rescue|retry|return|self|super|then|undef|unless|until|when|while|yield)\\b" - constant: "(\\$|@|@@)?\\b[A-Z]+[0-9A-Z_a-z]*" - constant.number: "(?i)\\b0x[0-9a-fA-F][0-9a-f_]*\\b" - constant.number: "(?i)\\b0b[01][01_]*\\b" - constant.number: "(?i)\\b[0-9][0-9_]*(['.'][0-9_]+)?(e[\\-]?[0-9_]+)?\\b" # Ruby "Symbols" - constant: "(i?)([ ]|^):[0-9A-Z_]+\\b" - constant: "\\b(__FILE__|__LINE__)\\b" - constant: "/([^/]|(\\\\/))*/[iomx]*|%r\\{([^}]|(\\\\}))*\\}[iomx]*" - constant.string: start: "'" end: "'" skip: "\\\\." rules: [] - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - symbol.brackets: start: "#\\{" end: "\\}" rules: - default: ".*" - constant.string.exec: start: "`" end: "`" skip: "\\\\." rules: - symbol.brackets: start: "#\\{" end: "\\}" rules: - default: ".*" - constant.string: "%[QW]?\\{[^}]*\\}|%[QW]?\\([^)]*\\)|%[QW]?<[^>]*>|%[QW]?\\[[^]]*\\]|%[QW]?\\$[^$]*\\$|%[QW]?\\^[^^]*\\^|%[QW]?![^!]*!" - constant.string: "%[qw]\\{[^}]*\\}|%[qw]\\([^)]*\\)|%[qw]<[^>]*>|%[qw]\\[[^]]*\\]|%[qw]\\$[^$]*\\$|%[qw]\\^[^^]*\\^|%[qw]![^!]*!" - constant.string.exec: "%[x]\\{[^}]*\\}|%[x]\\([^)]*\\)|%[x]<[^>]*>|%[x]\\[[^]]*\\]|%[x]\\$[^$]*\\$|%[x]\\^[^^]*\\^|%[x]![^!]*!" - constant.bool: "\\b(true|false|nil|TRUE|FALSE|NIL)\\b" - symbol.operator: "[-+/*=<>!~%&|^]|\\b:" - symbol.brackets: "([(){}]|\\[|\\])" - constant.macro: start: "<<-?'?EOT'?" end: "^EOT" rules: [] - preproc.shebang: "^#!.+?( |$)" micro-2.0.14/runtime/syntax/rust.yaml0000664000175000017510000000407314663411671017147 0ustar nileshnileshfiletype: rust detect: filename: "\\.rs$" rules: # function definition - identifier: "fn [a-z0-9_]+" # Reserved words - statement: "\\b(abstract|alignof|as|async|await|become|box|break|const|continue|crate|do|dyn|else|enum|extern|false|final|fn|for|if|impl|in|let|loop|macro|match|mod|move|mut|offsetof|override|priv|pub|pure|ref|return|sizeof|static|self|struct|super|true|trait|type|typeof|unsafe|unsized|use|virtual|where|while|yield)\\b" # macros - special: "[a-z_]+!" # Constants - constant: "\\b[A-Z][A-Z_0-9]+\\b" # Numbers - constant.number: "\\b[0-9]+\\b" # Booleans - constant: "\\b(true|false)\\b" # Traits/Enums/Structs/Types/etc. - type: "\\b[A-Z]+[a-zA-Z_0-9]*[a-z]+[a-zA-Z_0-9]*\\b" # Builtin types that start with lowercase. - type: "\\b(bool|str|isize|usize|((i|u)(8|16|32|64))|f32|f64)\\b" - constant.string: start: "\"" end: "\"" skip: '\\.' rules: - constant.specialChar: '\\.' - constant.string: start: "r#\"" end: "\"#" rules: [] - constant.string: start: "r##\"" end: "\"##" rules: [] - constant.string: start: "r###\"" end: "\"###" rules: [] - constant.string: start: "r####+\"" end: "\"####+" rules: [] # Character literals # NOTE: This is an ugly hack to work around the fact that rust uses # single quotes both for character literals and lifetimes. # Match all character literals. - constant.string: "'(\\\\.|.)'" # Match the '"' literal which would otherwise match # as a double quoted string and destroy the highlighting. - constant.string: start: "'\"" end: "'" rules: [] - comment: start: "//" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" - comment: start: "/\\*" end: "\\*/" rules: - todo: "(TODO|XXX|FIXME):?" - special: start: "#!\\[" end: "\\]" rules: [] micro-2.0.14/runtime/syntax/sage.yaml0000664000175000017510000000474314663411671017075 0ustar nileshnileshfiletype: sage detect: filename: "\\.sage$" header: "^#!.*/(env +)?sage( |$)" rules: # built-in objects - constant: "\\b(None|self|True|False)\\b" # built-in attributes - constant: "\\b(__bases__|__builtin__|__class__|__debug__|__dict__|__doc__|__file__|__members__|__methods__|__name__|__self__)\\b" # built-in functions - identifier: "\\b(abs|apply|callable|chr|cmp|compile|delattr|dir|divmod|eval|exec|execfile|filter|format|getattr|globals|hasattr|hash|help|hex|id|input|intern|isinstance|issubclass|len|locals|max|min|next|oct|open|ord|pow|range|raw_input|reduce|reload|repr|round|setattr|unichr|vars|zip|__import__)\\b" # special method names - identifier: "\\b(__abs__|__add__|__and__|__call__|__cmp__|__coerce__|__complex__|__concat__|__contains__|__del__|__delattr__|__delitem__|__dict__|__delslice__|__div__|__divmod__|__float__|__getattr__|__getitem__|__getslice__|__hash__|__hex__|__init__|__int__|__inv__|__invert__|__len__|__long__|__lshift__|__mod__|__mul__|__neg__|__nonzero__|__oct__|__or__|__pos__|__pow__|__radd__|__rand__|__rcmp__|__rdiv__|__rdivmod__|__repeat__|__repr__|__rlshift__|__rmod__|__rmul__|__ror__|__rpow__|__rrshift__|__rshift__|__rsub__|__rxor__|__setattr__|__setitem__|__setslice__|__str__|__sub__|__xor__)\\b" # types - type: "\\b(basestring|bool|buffer|bytearray|bytes|classmethod|complex|dict|enumerate|file|float|frozenset|int|list|long|map|memoryview|object|property|reversed|set|slice|staticmethod|str|super|tuple|type|unicode|xrange)\\b" # definitions - identifier: "def [a-zA-Z_0-9]+" # keywords - statement: "\\b(and|as|assert|break|class|continue|def|del|elif|else|except|finally|for|from|global|if|import|in|is|lambda|not|or|pass|print|raise|return|try|while|with|yield)\\b" # decorators - brightgreen: "@.*[(]" # operators - statement: "([.:;,+*|=!\\%@]|<|>|/|-|&)" # parentheses - statement: "([(){}]|\\[|\\])" # numbers - constant.number: "\\b[0-9]+\\b" - comment: start: "\"\"\"" end: "\"\"\"" rules: [] - comment: start: "'''" end: "'''" rules: [] - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\." - constant.string: start: "'" end: "'" skip: "\\\\." rules: - constant.specialChar: "\\\\." - comment: start: "#" end: "$" rules: [] micro-2.0.14/runtime/syntax/scala.yaml0000664000175000017510000000200414663411671017225 0ustar nileshnileshfiletype: scala detect: filename: "\\.sc(ala)?$|\\.sbt$" rules: - type: "\\b(boolean|byte|char|double|float|int|long|new|short|this|transient|void)\\b" - statement: "\\b(match|val|var|break|case|catch|continue|default|do|else|finally|for|if|return|switch|throw|try|while)\\b" - statement: "\\b(def|object|case|trait|lazy|implicit|abstract|class|extends|with|final|implements|override|import|instanceof|interface|native|package|private|protected|public|static|strictfp|super|synchronized|throws|volatile|sealed)\\b" - constant.string: start: "\"\"\"" end: "\"\"\"" rules: [] - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\." - constant: "\\b(true|false|null)\\b" - comment: start: "//" end: "$" rules: [] - comment: start: "/\\*" end: "\\*/" rules: [] - comment: start: "/\\*\\*" end: "\\*/" rules: [] micro-2.0.14/runtime/syntax/sed.yaml0000664000175000017510000000051414663411671016721 0ustar nileshnileshfiletype: sed detect: filename: "\\.sed$" header: "^#!.*bin/(env +)?sed( |$)" rules: - symbol.operator: "[|^$.*+]" - constant.number: "\\{[0-9]+,?[0-9]*\\}" - constant.specialChar: "\\\\." - comment: "(^|[[:space:]])#([^{].*)?$" - indent-char.whitespace: "[[:space:]]+$" - indent-char: " + +| + +" micro-2.0.14/runtime/syntax/sh.yaml0000664000175000017510000000521414663411671016562 0ustar nileshnileshfiletype: shell # Detection based on filename is rather complicated as there are many # different file extensions and special filenames in use. # This expressions aims to capture them all while not matching # filenames that coincidentally contain the same substring. # # File extensions: # * .sh # * .bash # * .ash # * .ebuild (Gentoo ebuild format) # # Special filenames: # * .bashrc, .bash_aliases, .bash_functions .bash_profile # * profile, .profile (/etc/profile or ~/.profile) # * Pkgfile # * pkgmk.conf # * rc.conf # * PKGBUILD (Arch Linux build scripts) # * APKBUILD # # Fix command (fc) files: # * bash-fc. (followed by a random string) detect: filename: "(\\.(sh|bash|ash|ebuild)$|(\\.bash(rc|_aliases|_functions|_profile)|\\.?profile|Pkgfile|pkgmk\\.conf|rc\\.conf|PKGBUILD|APKBUILD)$|bash-fc\\.)" header: "^#!.*/(env +)?(ba)?(a)?(mk)?sh( |$)" rules: # Numbers - constant.number: "\\b[0-9]+\\b" # Conditionals and control flow - statement: "\\b(break|case|continue|do|done|elif|else|esac|exec|exit|fi|for|function|if|in|return|select|then|trap|until|wait|while)\\b" - special: "[`$<>!=&~^\\{\\}\\(\\)\\;\\]\\[]+" # Shell commands - type: "\\b(cd|command|echo|eval|export|getopts|let|local|read|set|shift|time|umask|unset)\\b" # Common linux commands - type: "\\b((g|ig)?awk|bash|dash|find|getopt|\\w{0,4}grep|kill|killall|\\w{0,4}less|make|pkill|sed|sh|tar)\\b" # Coreutils commands - type: "\\b(base64|basename|cat|chcon|chgrp|chmod|chown|chroot|cksum|comm|cp|csplit|cut|date|dd|df|dir|dircolors|dirname|du|env|expand|expr|factor|false|fmt|fold|head|hostid|id|install|join|link|ln|logname|ls|md5sum|mkdir|mkfifo|mknod|mktemp|mv|nice|nl|nohup|nproc|numfmt|od|paste|pathchk|pinky|pr|printenv|printf|ptx|pwd|readlink|realpath|rm|rmdir|runcon|seq|(sha1|sha224|sha256|sha384|sha512)sum|shred|shuf|sleep|sort|split|stat|stdbuf|stty|sum|sync|tac|tail|tee|test|time|timeout|touch|tr|true|truncate|tsort|tty|uname|unexpand|uniq|unlink|users|vdir|wc|who|whoami|yes)\\b" # Conditional flags - statement: "\\s+(-[A-Za-z]+|--[a-z]+)" - identifier: "\\$\\{[0-9A-Za-z_:!%&=+#~@*^$?, .\\-\\/\\[\\]]+\\}" - identifier: "\\$[0-9A-Za-z_:!%&=+#~@*^$?,\\-\\[\\]]+" - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: [] - constant.string: start: "'" end: "'" skip: "\\\\." rules: [] - constant.string: start: "<<[^\\s]+[-~.]*[A-Za-z0-9]+$" end: "^[^\\s]+[A-Za-z0-9]+$" skip: "\\\\." rules: [] - comment: start: "(^|\\s)#" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" micro-2.0.14/runtime/syntax/sls.yaml0000664000175000017510000000066514663411671016756 0ustar nileshnileshfiletype: salt detect: filename: "\\.sls$" rules: - identifier.var: "^[^ -].*:$" - identifier.var: ".*:" - default: "salt:" - constant.number: "/*[0-9]/*" - constant.bool: "\\b(True|False)\\b" - constant.string: "\"(\\\\.|[^\"])*\"|'(\\\\.|[^'])*'" - special: "\\b(grain|grains|compound|pcre|grain_pcre|list|pillar)\\b" - comment: "^#.*" - statement: "\\b(if|elif|else|or|not|and|endif|end)\\b" micro-2.0.14/runtime/syntax/smalltalk.yaml0000664000175000017510000000342014663411671020131 0ustar nileshnileshfiletype: smalltalk detect: filename: "\\.(st|sources|changes)$" rules: - statement: "\\b(self|nil|true|false|ifTrue|ifFalse|whileTrue|whileFalse)\\b" - constant: "(\\$|@|@@)?\\b[A-Z]+[0-9A-Z_a-z]*" - constant.number: "(?i)\\b0x[0-9a-fA-F][0-9a-f_]*\\b" - constant.number: "(?i)\\b0b[01][01_]*\\b" - constant.number: "(?i)\\b[0-9][0-9_]*(['.'][0-9_]+)?(e[\\-]?[0-9_]+)?\\b" # Ruby "Symbols" - constant: "(i?)([ ]|^):[0-9A-Z_]+\\b" - constant: "\\b(__FILE__|__LINE__)\\b" - constant: "/([^/]|(\\\\/))*/[iomx]*|%r\\{([^}]|(\\\\}))*\\}[iomx]*" - constant.string: start: "'" end: "'" skip: "\\\\." rules: [] - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - symbol.brackets: start: "#\\{" end: "\\}" rules: - default: ".*" - constant.string.exec: start: "`" end: "`" skip: "\\\\." rules: - symbol.brackets: start: "#\\{" end: "\\}" rules: - default: ".*" - constant.string: "%[QW]?\\{[^}]*\\}|%[QW]?\\([^)]*\\)|%[QW]?<[^>]*>|%[QW]?\\[[^]]*\\]|%[QW]?\\$[^$]*\\$|%[QW]?\\^[^^]*\\^|%[QW]?![^!]*!" - constant.string: "%[qw]\\{[^}]*\\}|%[qw]\\([^)]*\\)|%[qw]<[^>]*>|%[qw]\\[[^]]*\\]|%[qw]\\$[^$]*\\$|%[qw]\\^[^^]*\\^|%[qw]![^!]*!" - constant.string.exec: "%[x]\\{[^}]*\\}|%[x]\\([^)]*\\)|%[x]<[^>]*>|%[x]\\[[^]]*\\]|%[x]\\$[^$]*\\$|%[x]\\^[^^]*\\^|%[x]![^!]*!" - symbol.operator: "[-+/*=<>!~%&|^]|\\b:" - symbol.brackets: "([(){}]|\\[|\\])" - constant.macro: start: "<<-?'?EOT'?" end: "^EOT" rules: [] - preproc.shebang: "^#!.+?( |$)" micro-2.0.14/runtime/syntax/solidity.yaml0000664000175000017510000000315114663411671020006 0ustar nileshnileshfiletype: solidity detect: filename: "\\.sol$" rules: - preproc: "\\b(contract|library|pragma)\\b" - constant.number: "\\b[-]?([0-9]+|0x[0-9a-fA-F]+)\\b" - identifier: "[a-zA-Z][_a-zA-Z0-9]*[[:space:]]*" - statement: "\\b(assembly|break|continue|do|for|function|if|else|new|return|returns|while)\\b" - special: "\\b(\\.send|throw)\\b" # make sure they are very visible - type.keyword: "\\b(anonymous|constant|indexed|payable|public|private|external|internal)\\b" - constant: "\\b(block(\\.(blockhash|coinbase|difficulty|gaslimit|number|timestamp))?|msg(\\.(data|gas|sender|value))?|now|tx(\\.(gasprice|origin))?)\\b" - constant: "\\b(keccak256|sha3|sha256|ripemd160|ecrecover|addmod|mulmod|this|super|selfdestruct|\\.balance)\\b" - constant: "\\b(true|false)\\b" - constant: "\\b(wei|szabo|finney|ether|seconds|minutes|hours|days|weeks|years)\\b" - type: "\\b(address|bool|mapping|string|var|int(\\d*)|uint(\\d*)|byte(\\d*)|fixed(\\d*)|ufixed(\\d*))\\b" - error: "\\b(abstract|after|case|catch|default|final|in|inline|interface|let|match|null|of|pure|relocatable|static|switch|try|type|typeof|view)\\b" - operator: "[-+/*=<>!~%?:&|]" - comment: start: "//" end: "$" rules: [] - comment: start: "/\\*" end: "\\*/" rules: [] - todo: "TODO:?" - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\." - constant.string: start: "'" end: "'" skip: "\\\\." rules: - constant.specialChar: "\\\\." micro-2.0.14/runtime/syntax/sql.yaml0000664000175000017510000000424414663411671016751 0ustar nileshnileshfiletype: sql detect: filename: "\\.sql$|sqliterc$" rules: - statement: "(?i)\\b(ALL|ASC|AS|ALTER|AND|ADD|AUTO_INCREMENT)\\b" - statement: "(?i)\\b(BETWEEN|BINARY|BOTH|BY|BOOLEAN)\\b" - statement: "(?i)\\b(CHANGE|CHECK|COLUMNS|COLUMN|CROSS|CREATE)\\b" - statement: "(?i)\\b(DATABASES|DATABASE|DATA|DELAYED|DESCRIBE|DESC|DISTINCT|DELETE|DROP|DEFAULT)\\b" - statement: "(?i)\\b(ENCLOSED|ESCAPED|EXISTS|EXPLAIN)\\b" - statement: "(?i)\\b(FIELDS|FIELD|FLUSH|FOR|FOREIGN|FUNCTION|FROM)\\b" - statement: "(?i)\\b(GROUP|GRANT|HAVING)\\b" - statement: "(?i)\\b(IGNORE|INDEX|INFILE|INSERT|INNER|INTO|IDENTIFIED|IN|IS|IF)\\b" - statement: "(?i)\\b(JOIN|KEYS|KILL|KEY)\\b" - statement: "(?i)\\b(LEADING|LIKE|LIMIT|LINES|LOAD|LOCAL|LOCK|LOW_PRIORITY|LEFT|LANGUAGE)\\b" - statement: "(?i)\\b(MODIFY|NATURAL|NOT|NULL|NEXTVAL)\\b" - statement: "(?i)\\b(OPTIMIZE|OPTION|OPTIONALLY|ORDER|OUTFILE|OR|OUTER|ON)\\b" - statement: "(?i)\\b(PROCEDURE|PROCEDURAL|PRIMARY)\\b" - statement: "(?i)\\b(READ|REFERENCES|REGEXP|RENAME|REPLACE|RETURN|REVOKE|RLIKE|RIGHT)\\b" - statement: "(?i)\\b(SHOW|SONAME|STATUS|STRAIGHT_JOIN|SELECT|SETVAL|SET)\\b" - statement: "(?i)\\b(TABLES|TERMINATED|TO|TRAILING|TRUNCATE|TABLE|TEMPORARY|TRIGGER|TRUSTED)\\b" - statement: "(?i)\\b(UNIQUE|UNLOCK|USE|USING|UPDATE|VALUES|VARIABLES|VIEW)\\b" - statement: "(?i)\\b(WITH|WRITE|WHERE|ZEROFILL|TYPE|XOR)\\b" - type: "(?i)\\b(VARCHAR|TINYINT|TEXT|DATE|SMALLINT|MEDIUMINT|INT|INTEGER|BIGINT|FLOAT|DOUBLE|DECIMAL|DATETIME|TIMESTAMP|TIME|YEAR|UNSIGNED|CHAR|TINYBLOB|TINYTEXT|BLOB|MEDIUMBLOB|MEDIUMTEXT|LONGBLOB|LONGTEXT|ENUM|BOOL|BINARY|VARBINARY)\\b" - preproc: "(?i)\\.\\b(databases|dump|echo|exit|explain|header(s)?|help)\\b" - preproc: "(?i)\\.\\b(import|indices|mode|nullvalue|output|prompt|quit|read)\\b" - preproc: "(?i)\\.\\b(schema|separator|show|tables|timeout|width)\\b" - constant.bool: "\\b(ON|OFF)\\b" - constant.number: "\\b([0-9]+)\\b" - constant.string: "\"(\\\\.|[^\"])*\"|'(\\\\.|[^'])*'" - constant.string: "`(\\\\.|[^\\\\`])*`" - comment: "\\-\\-.*$" - indent-char.whitespace: "[[:space:]]+$" - indent-char: " + +| + +" micro-2.0.14/runtime/syntax/stata.yaml0000664000175000017510000004603314663411671017270 0ustar nileshnileshfiletype: stata detect: filename: "\\.a?do$" rules: - constant.string: start: "`\"" end: "\"'" rules: - identifier.macro: start: "`" end: "'" rules: [] - identifier.macro: "\\$\\w+" - constant.specialChar: "\\\\." - constant.string: start: "\"" end: "\"" rules: - identifier.macro: start: "`" end: "'" rules: [] - identifier.macro: "\\$\\w+" - constant.specialChar: "\\\\." # Built-in functions - identifier: "\\b(_caller|abbrev|abs|acos|acosh|asin|asinh|atan|atan2|atanh|autocode|betaden|binomial|binomialp|binomialtail|binormal|bofd|byteorder|c|cauchy|cauchyden|cauchytail|Cdhms|ceil|char|chi2|chi2den|chi2tail|Chms|cholesky|chop|clip|Clock|cloglog|Cmdyhms|Cofc|Cofd|coleqnumb|collatorlocale|collatorversion|colnfreeparms|colnumb|colsof|comb|cond|corr|cos|cosh|date|day|det|dgammapda|dgammapdada|dgammapdadx|dgammapdx|dgammapdxdx|dhms|diag|diag0cnt|digamma|dofb|dofC|dofh|dofm|dofq|dofw|dofy|dow|doy|dunnettprob|e|el|epsdouble|epsfloat|exp|exponential|exponentialden|exponentialtail|F|Fden|fileexists|fileread|filereaderror|filewrite|float|floor|fmtwidth|Ftail|gammaden|gammap|gammaptail|hadamard|halfyear|halfyearly|has_eprop|hh|hhC|hms|hofd|hours|hypergeometric|hypergeometricp|I|ibeta|ibetatail|igaussian|igaussianden|igaussiantail|indexnot|inlist|inrange|int|inv|invbinomial|invbinomialtail|invcauchy|invcauchytail|invchi2|invchi2tail|invcloglog|invdunnettprob|invexponential|invexponentialtail|invF|invFtail|invgammap|invgammaptail|invibeta|invibetatail|invigaussian|invigaussiantail|invlaplace|invlaplacetail|invlogistic|invlogistictail|invlogit|invnbinomial|invnbinomialtail|invnchi2|invnchi2tail|invnF|invnFtail|invnibeta|invnormal|invnt|invnttail|invpoisson|invpoissontail|invsym|invt|invttail|invtukeyprob|invweibull|invweibullph|invweibullphtail|invweibulltail|irecode|issymmetric|J|laplace|laplaceden|laplacetail|ln|lncauchyden|lnfactorial|lngamma|lnigammaden|lnigaussianden|lniwishartden|lnlaplaceden|lnmvnormalden|lnnormal|lnnormalden|lnwishartden|log|log10|logistic|logisticden|logistictail|logit|matmissing|matrix|matuniform|max|maxbyte|maxdouble|maxfloat|maxint|maxlong|mdy|mdyhms|missing|min|minbyte|mindouble|minfloat|minint|minlong|minutes|mm|mmC|mod|mofd|month|monthly|mreldif|msofhours|msofminutes|msofseconds|nbetaden|nbinomial|nbinomialp|nbinomialtail|nchi2|nchi2den|nchi2tail|nF|nFden|nFtail|nibeta|normal|normalden|npnchi2|npnF|npnt|nt|ntden|nttail|nullmat|plural|poisson|poissonp|poissontail|qofd|quarter|quarterly|r|rbeta|rbinomial|rcauchy|rchi2|real|recode|regexm|regexr|regexs|reldif|replay|return|rexponential|rgamma|rhypergeometric|rigaussian|rlaplace|rlogistic|rnbinomial|rnormal|round|roweqnumb|rownfreeparms|rownumb|rowsof|rpoisson|rt|runiform|runiformint|rweibull|rweibullph|s|scalar|seconds|sign|sin|sinh|smallestdouble|soundex|sqrt|ss|ssC|strcat|strdup|strofreal|string|strtrim|stritrim|strltrim|strrtrim|strlen|strupper|strlower|strproper|strmatch|strpos|strrpos|strreverse|strtoname|subinstr|subinword|substr|sum|sweep|t|tan|tanh|tC|td|tden|th|tin|tm|tobytes|tq|trace|trigamma|ttail|tukeyprob|tw|twithin|uchar|udstrlen|udsubstr|uisdigit|uisletter|ustrcompare|ustrsortkey|ustrcompareex|ustrsortkeyex|ustrfix|ustrto|ustrfrom|ustrlen|ustrinvalidcnt|usubstr|ustrleft|ustrright|ustrupper|ustrlower|ustrtitle|ustrtrim|ustrltrim|ustrrtrim|ustrnormalize|ustrpos|ustrrpos|ustrregexm|ustrregexrf|ustrregexra|ustrregexs|ustrreverse|ustrunescape|ustrtohex|ustrtoname|ustrword|ustrwordcount|usubinstr|vec|vecdiag|week|weekly|weibull|weibullden|weibullph|weibullphden|weibullphtail|weibulltail|wofd|word|wordbreaklocale|year|yearly|yh|ym|yofd|yq|yw)\\b" # Built-in commands - statement: "\\b(if|else|else\\s+if|in|foreach|for|forv|forva|forval|forvalu|forvalue|forvalues|by|bys|bysort|quietly|qui|about|ac|ac_7|acprplot|acprplot_7|adjust|ado|adopath|adoupdate|alpha|ameans|an|ano|anov|anova|anova_estat|anova_terms|anovadef|aorder|ap|app|appe|appen|append|arch|arch_dr|arch_estat|arch_p|archlm|areg|areg_p|args|arima|arima_dr|arima_estat|arima_p|as|asmprobit|asmprobit_estat|asmprobit_lf|asmprobit_mfx__dlg|asmprobit_p|ass|asse|asser|assert|avplot|avplot_7|avplots|avplots_7|bcskew0|bgodfrey|binreg|bip0_lf|biplot|bipp_lf|bipr_lf|bipr_p|biprobit|bitest|bitesti|bitowt|blogit|bmemsize|boot|bootsamp|bootstrap|bootstrap_8|boxco_l|boxco_p|boxcox|boxcox_6|boxcox_p|bprobit|br|break|brier|bro|brow|brows|browse|brr|brrstat|bs|bs_7|bsampl_w|bsample|bsample_7|bsqreg|bstat|bstat_7|bstat_8|bstrap|bstrap_7|ca|ca_estat|ca_p|cabiplot|camat|canon|canon_8|canon_8_p|canon_estat|canon_p|cap|caprojection|capt|captu|captur|capture|cat|cc|cchart|cchart_7|cci|cd|censobs_table|centile|cf|char|chdir|checkdlgfiles|checkestimationsample|checkhlpfiles|checksum|chelp|ci|cii|cl|class|classutil|clear|cli|clis|clist|clo|clog|clog_lf|clog_p|clogi|clogi_sw|clogit|clogit_lf|clogit_p|clogitp|clogl_sw|cloglog|clonevar|clslistarray|cluster|cluster_measures|cluster_stop|cluster_tree|cluster_tree_8|clustermat|cmdlog|cnr|cnre|cnreg|cnreg_p|cnreg_sw|cnsreg|codebook|collaps4|collapse|colormult_nb|colormult_nw|compare|compress|conf|confi|confir|confirm|conren|cons|const|constr|constra|constrai|constrain|constraint|continue|contract|copy|copyright|copysource|cor|corc|corr|corr2data|corr_anti|corr_kmo|corr_smc|corre|correl|correla|correlat|correlate|corrgram|cou|coun|count|cox|cox_p|cox_sw|coxbase|coxhaz|coxvar|cprplot|cprplot_7|crc|cret|cretu|cretur|creturn|cross|cs|cscript|cscript_log|csi|ct|ct_is|ctset|ctst_5|ctst_st|cttost|cumsp|cumsp_7|cumul|cusum|cusum_7|cutil|d|datasig|datasign|datasigna|datasignat|datasignatu|datasignatur|datasignature|datetof|db|dbeta|de|dec|deco|decod|decode|deff|des|desc|descr|descri|describ|describe|destring|dfbeta|dfgls|dfuller|di|di_g|dir|dirstats|dis|discard|disp|disp_res|disp_s|displ|displa|display|distinct|do|doe|doed|doedi|doedit|dotplot|dotplot_7|dprobit|drawnorm|drop|ds|ds_util|dstdize|duplicates|durbina|dwstat|dydx|e|ed|edi|edit|egen|eivreg|emdef|end|en|enc|enco|encod|encode|eq|erase|ereg|ereg_lf|ereg_p|ereg_sw|ereghet|ereghet_glf|ereghet_glf_sh|ereghet_gp|ereghet_ilf|ereghet_ilf_sh|ereghet_ip|eret|eretu|eretur|ereturn|err|erro|error|est|est_cfexist|est_cfname|est_clickable|est_expand|est_hold|est_table|est_unhold|est_unholdok|estat|estat_default|estat_summ|estat_vce_only|esti|estimates|etodow|etof|etomdy|ex|exi|exit|expand|expandcl|fac|fact|facto|factor|factor_estat|factor_p|factor_pca_rotated|factor_rotate|factormat|fcast|fcast_compute|fcast_graph|fdades|fdadesc|fdadescr|fdadescri|fdadescrib|fdadescribe|fdasav|fdasave|fdause|fh_st|open|read|close|file|filefilter|fillin|find_hlp_file|findfile|findit|findit_7|fit|fl|fli|flis|flist|for5_0|form|forma|format|fpredict|frac_154|frac_adj|frac_chk|frac_cox|frac_ddp|frac_dis|frac_dv|frac_in|frac_mun|frac_pp|frac_pq|frac_pv|frac_wgt|frac_xo|fracgen|fracplot|fracplot_7|fracpoly|fracpred|fron_ex|fron_hn|fron_p|fron_tn|fron_tn2|frontier|ftodate|ftoe|ftomdy|ftowdate|g|gamhet_glf|gamhet_gp|gamhet_ilf|gamhet_ip|gamma|gamma_d2|gamma_p|gamma_sw|gammahet|gdi_hexagon|gdi_spokes|ge|gen|gene|gener|genera|generat|generate|genrank|genstd|genvmean|gettoken|gl|gladder|gladder_7|glim_l01|glim_l02|glim_l03|glim_l04|glim_l05|glim_l06|glim_l07|glim_l08|glim_l09|glim_l10|glim_l11|glim_l12|glim_lf|glim_mu|glim_nw1|glim_nw2|glim_nw3|glim_p|glim_v1|glim_v2|glim_v3|glim_v4|glim_v5|glim_v6|glim_v7|glm|glm_6|glm_p|glm_sw|glmpred|glo|glob|globa|global|glogit|glogit_8|glogit_p|gmeans|gnbre_lf|gnbreg|gnbreg_5|gnbreg_p|gomp_lf|gompe_sw|gomper_p|gompertz|gompertzhet|gomphet_glf|gomphet_glf_sh|gomphet_gp|gomphet_ilf|gomphet_ilf_sh|gomphet_ip|gphdot|gphpen|gphprint|gprefs|gprobi_p|gprobit|gprobit_8|gr|gr7|gr_copy|gr_current|gr_db|gr_describe|gr_dir|gr_draw|gr_draw_replay|gr_drop|gr_edit|gr_editviewopts|gr_example|gr_example2|gr_export|gr_print|gr_qscheme|gr_query|gr_read|gr_rename|gr_replay|gr_save|gr_set|gr_setscheme|gr_table|gr_undo|gr_use|graph|graph7|grebar|greigen|greigen_7|greigen_8|grmeanby|grmeanby_7|gs_fileinfo|gs_filetype|gs_graphinfo|gs_stat|gsort|gwood|h|hadimvo|hareg|hausman|haver|he|heck_d2|heckma_p|heckman|heckp_lf|heckpr_p|heckprob|hel|help|hereg|hetpr_lf|hetpr_p|hetprob|hettest|hexdump|hilite|hist|hist_7|histogram|hlogit|hlu|hmeans|hotel|hotelling|hprobit|hreg|hsearch|icd9|icd9_ff|icd9p|iis|impute|imtest|inbase|include|inf|infi|infil|infile|infix|inp|inpu|input|ins|insheet|insp|inspe|inspec|inspect|integ|inten|intreg|intreg_7|intreg_p|intrg2_ll|intrg_ll|intrg_ll2|ipolate|iqreg|ir|irf|irf_create|irfm|iri|is_svy|is_svysum|isid|istdize|ivprob_1_lf|ivprob_lf|ivprobit|ivprobit_p|ivreg|ivreg_footnote|ivtob_1_lf|ivtob_lf|ivtobit|ivtobit_p|jackknife|jacknife|jknife|jknife_6|jknife_8|jkstat|joinby|kalarma1|kap|kap_3|kapmeier|kappa|kapwgt|kdensity|kdensity_7|keep|ksm|ksmirnov|ktau|kwallis|l|la|lab|labe|label|labelbook|ladder|levels|levelsof|leverage|lfit|lfit_p|li|lincom|line|linktest|lis|list|lloghet_glf|lloghet_glf_sh|lloghet_gp|lloghet_ilf|lloghet_ilf_sh|lloghet_ip|llogi_sw|llogis_p|llogist|llogistic|llogistichet|lnorm_lf|lnorm_sw|lnorma_p|lnormal|lnormalhet|lnormhet_glf|lnormhet_glf_sh|lnormhet_gp|lnormhet_ilf|lnormhet_ilf_sh|lnormhet_ip|lnskew0|loadingplot|loc|loca|local|log|logi|logis_lf|logistic|logistic_p|logit|logit_estat|logit_p|loglogs|logrank|loneway|lookfor|lookup|lowess|lowess_7|lpredict|lrecomp|lroc|lroc_7|lrtest|ls|lsens|lsens_7|lsens_x|lstat|ltable|ltable_7|ltriang|lv|lvr2plot|lvr2plot_7|m|ma|mac|macr|macro|makecns|man|manova|manova_estat|manova_p|manovatest|mantel|mark|markin|markout|marksample|mat|mat_capp|mat_order|mat_put_rr|mat_rapp|mata|mata_clear|mata_describe|mata_drop|mata_matdescribe|mata_matsave|mata_matuse|mata_memory|mata_mlib|mata_mosave|mata_rename|mata_which|matalabel|matcproc|matlist|matname|matr|matri|matrix|matrix_input__dlg|matstrik|mcc|mcci|md0_|md1_|md1debug_|md2_|md2debug_|mds|mds_estat|mds_p|mdsconfig|mdslong|mdsmat|mdsshepard|mdytoe|mdytof|me_derd|mean|means|median|memory|memsize|meqparse|mer|merg|merge|mfp|mfx|mhelp|mhodds|minbound|mixed_ll|mixed_ll_reparm|mkassert|mkdir|mkmat|mkspline|ml|ml_5|ml_adjs|ml_bhhhs|ml_c_d|ml_check|ml_clear|ml_cnt|ml_debug|ml_defd|ml_e0|ml_e0_bfgs|ml_e0_cycle|ml_e0_dfp|ml_e0i|ml_e1|ml_e1_bfgs|ml_e1_bhhh|ml_e1_cycle|ml_e1_dfp|ml_e2|ml_e2_cycle|ml_ebfg0|ml_ebfr0|ml_ebfr1|ml_ebh0q|ml_ebhh0|ml_ebhr0|ml_ebr0i|ml_ecr0i|ml_edfp0|ml_edfr0|ml_edfr1|ml_edr0i|ml_eds|ml_eer0i|ml_egr0i|ml_elf|ml_elf_bfgs|ml_elf_bhhh|ml_elf_cycle|ml_elf_dfp|ml_elfi|ml_elfs|ml_enr0i|ml_enrr0|ml_erdu0|ml_erdu0_bfgs|ml_erdu0_bhhh|ml_erdu0_bhhhq|ml_erdu0_cycle|ml_erdu0_dfp|ml_erdu0_nrbfgs|ml_exde|ml_footnote|ml_geqnr|ml_grad0|ml_graph|ml_hbhhh|ml_hd0|ml_hold|ml_init|ml_inv|ml_log|ml_max|ml_mlout|ml_mlout_8|ml_model|ml_nb0|ml_opt|ml_p|ml_plot|ml_query|ml_rdgrd|ml_repor|ml_s_e|ml_score|ml_searc|ml_technique|ml_unhold|mleval|mlf_|mlmatbysum|mlmatsum|mlog|mlogi|mlogit|mlogit_footnote|mlogit_p|mlopts|mlsum|mlvecsum|mnl0_|mor|more|mov|move|mprobit|mprobit_lf|mprobit_p|mrdu0_|mrdu1_|mvdecode|mvencode|mvreg|mvreg_estat|n|nbreg|nbreg_al|nbreg_lf|nbreg_p|nbreg_sw|nestreg|net|newey|newey_7|newey_p|news|nl|nl_7|nl_9|nl_9_p|nl_p|nl_p_7|nlcom|nlcom_p|nlexp2|nlexp2_7|nlexp2a|nlexp2a_7|nlexp3|nlexp3_7|nlgom3|nlgom3_7|nlgom4|nlgom4_7|nlinit|nllog3|nllog3_7|nllog4|nllog4_7|nlog_rd|nlogit|nlogit_p|nlogitgen|nlogittree|nlpred|no|nobreak|noi|nois|noisi|noisil|noisily|note|notes|notes_dlg|nptrend|numlabel|numlist|odbc|old_ver|olo|olog|ologi|ologi_sw|ologit|ologit_p|ologitp|on|one|onew|onewa|oneway|op_colnm|op_comp|op_diff|op_inv|op_str|opr|opro|oprob|oprob_sw|oprobi|oprobi_p|oprobit|oprobitp|opts_exclusive|order|orthog|orthpoly|ou|out|outf|outfi|outfil|outfile|outs|outsh|outshe|outshee|outsheet|ovtest|pac|pac_7|palette|parse|parse_dissim|pause|pca|pca_8|pca_display|pca_estat|pca_p|pca_rotate|pcamat|pchart|pchart_7|pchi|pchi_7|pcorr|pctile|pentium|pergram|pergram_7|permute|permute_8|personal|peto_st|pkcollapse|pkcross|pkequiv|pkexamine|pkexamine_7|pkshape|pksumm|pksumm_7|pl|plo|plot|plugin|pnorm|pnorm_7|poisgof|poiss_lf|poiss_sw|poisso_p|poisson|poisson_estat|post|postclose|postfile|postutil|pperron|pr|prais|prais_e|prais_e2|prais_p|predict|predictnl|preserve|print|pro|prob|probi|probit|probit_estat|probit_p|proc_time|procoverlay|procrustes|procrustes_estat|procrustes_p|profiler|prog|progr|progra|program|prop|proportion|prtest|prtesti|pwcorr|pwd|q|s|qby|qbys|qchi|qchi_7|qladder|qladder_7|qnorm|qnorm_7|qqplot|qqplot_7|qreg|qreg_c|qreg_p|qreg_sw|qu|quadchk|quantile|quantile_7|que|quer|query|range|ranksum|ratio|rchart|rchart_7|rcof|recast|reclink|recode|reg|reg3|reg3_p|regdw|regr|regre|regre_p2|regres|regres_p|regress|regress_estat|regriv_p|remap|ren|rena|renam|rename|renpfix|repeat|replace|report|reshape|restore|ret|retu|retur|return|rm|rmdir|robvar|roccomp|roccomp_7|roccomp_8|rocf_lf|rocfit|rocfit_8|rocgold|rocplot|rocplot_7|roctab|roctab_7|rolling|rologit|rologit_p|rot|rota|rotat|rotate|rotatemat|rreg|rreg_p|ru|run|runtest|rvfplot|rvfplot_7|rvpplot|rvpplot_7|sa|safesum|sample|sampsi|sav|save|savedresults|saveold|sc|sca|scal|scala|scalar|scatter|scm_mine|sco|scob_lf|scob_p|scobi_sw|scobit|scor|score|scoreplot|scoreplot_help|scree|screeplot|screeplot_help|sdtest|sdtesti|se|search|separate|seperate|serrbar|serrbar_7|serset|set|set_defaults|sfrancia|sh|she|shel|shell|shewhart|shewhart_7|signestimationsample|signrank|signtest|simul|simul_7|simulate|simulate_8|sktest|sleep|slogit|slogit_d2|slogit_p|smooth|snapspan|so|sor|sort|spearman|spikeplot|spikeplot_7|spikeplt|spline_x|split|sqreg|sqreg_p|sret|sretu|sretur|sreturn|ssc|st|st_ct|st_hc|st_hcd|st_hcd_sh|st_is|st_issys|st_note|st_promo|st_set|st_show|st_smpl|st_subid|stack|statsby|statsby_8|stbase|stci|stci_7|stcox|stcox_estat|stcox_fr|stcox_fr_ll|stcox_p|stcox_sw|stcoxkm|stcoxkm_7|stcstat|stcurv|stcurve|stcurve_7|stdes|stem|stepwise|stereg|stfill|stgen|stir|stjoin|stmc|stmh|stphplot|stphplot_7|stphtest|stphtest_7|stptime|strate|strate_7|streg|streg_sw|streset|sts|sts_7|stset|stsplit|stsum|sttocc|sttoct|stvary|stweib|su|suest|suest_8|sum|summ|summa|summar|summari|summariz|summarize|sunflower|sureg|survcurv|survsum|svar|svar_p|svmat|svy|svy_disp|svy_dreg|svy_est|svy_est_7|svy_estat|svy_get|svy_gnbreg_p|svy_head|svy_header|svy_heckman_p|svy_heckprob_p|svy_intreg_p|svy_ivreg_p|svy_logistic_p|svy_logit_p|svy_mlogit_p|svy_nbreg_p|svy_ologit_p|svy_oprobit_p|svy_poisson_p|svy_probit_p|svy_regress_p|svy_sub|svy_sub_7|svy_x|svy_x_7|svy_x_p|svydes|svydes_8|svygen|svygnbreg|svyheckman|svyheckprob|svyintreg|svyintreg_7|svyintrg|svyivreg|svylc|svylog_p|svylogit|svymarkout|svymarkout_8|svymean|svymlog|svymlogit|svynbreg|svyolog|svyologit|svyoprob|svyoprobit|svyopts|svypois|svypois_7|svypoisson|svyprobit|svyprobt|svyprop|svyprop_7|svyratio|svyreg|svyreg_p|svyregress|svyset|svyset_7|svyset_8|svytab|svytab_7|svytest|svytotal|sw|sw_8|swcnreg|swcox|swereg|swilk|swlogis|swlogit|swologit|swoprbt|swpois|swprobit|swqreg|swtobit|swweib|symmetry|symmi|symplot|symplot_7|syntax|sysdescribe|sysdir|sysuse|szroeter|ta|tab|tab1|tab2|tab_or|tabd|tabdi|tabdis|tabdisp|tabi|table|tabodds|tabodds_7|tabstat|tabu|tabul|tabula|tabulat|tabulate|te|tempfile|tempname|tempvar|tes|test|testnl|testparm|teststd|tetrachoric|time_it|timer|tis|tob|tobi|tobit|tobit_p|tobit_sw|token|tokeni|tokeniz|tokenize|tostring|total|translate|translator|transmap|treat_ll|treatr_p|treatreg|trim|trnb_cons|trnb_mean|trpoiss_d2|trunc_ll|truncr_p|truncreg|tsappend|tset|tsfill|tsline|tsline_ex|tsreport|tsrevar|tsrline|tsset|tssmooth|tsunab|ttest|ttesti|tut_chk|tut_wait|tutorial|tw|tware_st|two|twoway|twoway__fpfit_serset|twoway__function_gen|twoway__histogram_gen|twoway__ipoint_serset|twoway__ipoints_serset|twoway__kdensity_gen|twoway__lfit_serset|twoway__normgen_gen|twoway__pci_serset|twoway__qfit_serset|twoway__scatteri_serset|twoway__sunflower_gen|twoway_ksm_serset|ty|typ|type|typeof|u|unab|unabbrev|unabcmd|update|us|use|uselabel|var|var_mkcompanion|var_p|varbasic|varfcast|vargranger|varirf|varirf_add|varirf_cgraph|varirf_create|varirf_ctable|varirf_describe|varirf_dir|varirf_drop|varirf_erase|varirf_graph|varirf_ograph|varirf_rename|varirf_set|varirf_table|varlist|varlmar|varnorm|varsoc|varstable|varstable_w|varstable_w2|varwle|vce|vec|vec_fevd|vec_mkphi|vec_p|vec_p_w|vecirf_create|veclmar|veclmar_w|vecnorm|vecnorm_w|vecrank|vecstable|verinst|vers|versi|versio|version|view|viewsource|vif|vwls|wdatetof|webdescribe|webseek|webuse|weib1_lf|weib2_lf|weib_lf|weib_lf0|weibhet_glf|weibhet_glf_sh|weibhet_glfa|weibhet_glfa_sh|weibhet_gp|weibhet_ilf|weibhet_ilf_sh|weibhet_ilfa|weibhet_ilfa_sh|weibhet_ip|weibu_sw|weibul_p|weibull|weibull_c|weibull_s|weibullhet|wh|whelp|whi|which|whil|while|wilc_st|wilcoxon|win|wind|windo|window|winexec|wntestb|wntestb_7|wntestq|xchart|xchart_7|xcorr|xcorr_7|xi|xi_6|xmlsav|xmlsave|xmluse|xpose|xsh|xshe|xshel|xshell|xt_iis|xt_tis|xtab_p|xtabond|xtbin_p|xtclog|xtcloglog|xtcloglog_8|xtcloglog_d2|xtcloglog_pa_p|xtcloglog_re_p|xtcnt_p|xtcorr|xtdata|xtdes|xtfront_p|xtfrontier|xtgee|xtgee_elink|xtgee_estat|xtgee_makeivar|xtgee_p|xtgee_plink|xtgls|xtgls_p|xthaus|xthausman|xtht_p|xthtaylor|xtile|xtint_p|xtintreg|xtintreg_8|xtintreg_d2|xtintreg_p|xtivp_1|xtivp_2|xtivreg|xtline|xtline_ex|xtlogit|xtlogit_8|xtlogit_d2|xtlogit_fe_p|xtlogit_pa_p|xtlogit_re_p|xtmixed|xtmixed_estat|xtmixed_p|xtnb_fe|xtnb_lf|xtnbreg|xtnbreg_pa_p|xtnbreg_refe_p|xtpcse|xtpcse_p|xtpois|xtpoisson|xtpoisson_d2|xtpoisson_pa_p|xtpoisson_refe_p|xtpred|xtprobit|xtprobit_8|xtprobit_d2|xtprobit_re_p|xtps_fe|xtps_lf|xtps_ren|xtps_ren_8|xtrar_p|xtrc|xtrc_p|xtrchh|xtrefe_p|xtreg|xtreg_be|xtreg_fe|xtreg_ml|xtreg_pa_p|xtreg_re|xtregar|xtrere_p|xtset|xtsf_ll|xtsf_llti|xtsum|xttab|xttest0|xttobit|xttobit_8|xttobit_p|xttrans|yx|yxview__barlike_draw|yxview_area_draw|yxview_bar_draw|yxview_dot_draw|yxview_dropline_draw|yxview_function_draw|yxview_iarrow_draw|yxview_ilabels_draw|yxview_normal_draw|yxview_pcarrow_draw|yxview_pcbarrow_draw|yxview_pccapsym_draw|yxview_pcscatter_draw|yxview_pcspike_draw|yxview_rarea_draw|yxview_rbar_draw|yxview_rbarm_draw|yxview_rcap_draw|yxview_rcapsym_draw|yxview_rconnected_draw|yxview_rline_draw|yxview_rscatter_draw|yxview_rspike_draw|yxview_spike_draw|yxview_sunflower_draw|zap_s|zinb|zinb_llf|zinb_plf|zip|zip_llf|zip_p|zip_plf|zt_ct_5|zt_hc_5|zt_hcd_5|zt_is_5|zt_iss_5|zt_sho_5|zt_smp_5|ztbase_5|ztcox_5|ztdes_5|ztereg_5|ztfill_5|ztgen_5|ztir_5|ztjoin_5|ztnb|ztnb_p|ztp|ztp_p|zts_5|ztset_5|ztspli_5|ztsum_5|zttoct_5|ztvary_5|ztweib_5)\\b" - constant.number: "\\b[+-]?([0-9]+(\\.[0-9]+)?|\\.[0-9]+|\\.)([eE][+-]?[0-9]+)?[i]?\\b" - symbol.operator: "-|==|<=|>=|<|>|&|!=" - symbol.operator: "\\*|\\+|\\^|/|!|~|=|~=" - symbol.brackets: "[\\{\\}\\(\\)\\[\\]]" - identifier: "%-?\\d{1,2}(\\.\\d{1,2})?[gfe]c?" - identifier: "%(21x|16H|16L|8H|8L)" - identifier: "%-?(tc|tC|td|tw|tm|tq|th|ty|tg).{0,32}" - identifier: "%[-~]?\\d{1,4}s" - identifier.macro: "\\$\\w{1,32}" - identifier.macro: start: "`" end: "'" rules: [] - comment: start: "///?" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" - comment: start: "/\\*" end: "\\*/" rules: - todo: "(TODO|XXX|FIXME):?" - comment: start: "^\\s*\\*" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" micro-2.0.14/runtime/syntax/svelte.yaml0000664000175000017510000000100714663411671017446 0ustar nileshnileshfiletype: svelte detect: filename: "\\.svelte$" rules: - default: start: "" rules: - include: "javascript" - default: start: "" rules: - include: "typescript" - default: start: "" end: "" rules: - include: "css" - default: start: "^" end: "$" rules: - include: "html5" micro-2.0.14/runtime/syntax/swift.yaml0000664000175000017510000001045314663411671017305 0ustar nileshnileshfiletype: swift detect: filename: "\\.swift$" rules: # Patterns - type: \b(_)\b # Operators - symbol.operator: ([.:;,+*|=!?\\%]|<|>|/|-|&) # Declaration Keywords - statement.declaration: \b(associatedtype|class|deinit|enum|extension|fileprivate|func|import|init)\b - statement.declaration: \b(inout|internal|let|open|operator|private|protocol|public|static|struct|subscript|typealias|var)\b # Statements Keywords - statement: \b(break|case|continue|default|defer|do|else|fallthrough|for|guard)\b - statement: \b(if|inif|repeat|return|switch|where|while)\b # keyword.reserved - statement.reserved: \b(associativity|convenience|dynamic|didSet|final|get|infix|indirect|lazy|left|mutating)\b - statement.reserved: \b(none|nonmutating|override|postfix|precedence|prefix|Protocol|required)\b - statement.reserved: \b(right|set|Type|unowned|weak|willSet)\b # Expression and types - type: \b(as|Any|catch|is|rethrows|super|self|throw|throws|try)\b - statement.built_in: \b(abs|advance|alignof|alignofValue|anyGenerator|assert|assertionFailure|bridgeFromObjectiveC)\b - statement.built_in: \b(bridgeFromObjectiveCUnconditional|bridgeToObjectiveC|bridgeToObjectiveCUnconditional|contains)\b - statement.built_in: \b(count|countElements|countLeadingZeros|debugPrint|debugPrintln|distance|dropFirst|dropLast|dump|encodeBitsAsWords)\b - statement.built_in: \b(enumerate|equal|fatalError|filter|find|getBridgedObjectiveCType|getVaList|indices|insertionSort)\b - statement.built_in: \b(isBridgedToObjectiveC|isBridgedVerbatimToObjectiveC|isUniquelyReferenced|isUniquelyReferencedNonObjC)\b - statement.built_in: \b(join|lexicographicalCompare|map|max|maxElement|min|minElement|numericCast|overlaps|partition|posix)\b - statement.built_in: \b(precondition|preconditionFailure|print|println|quickSort|readLine|reduce|reflect)\b - statement.built_in: \b(reinterpretCast!reverse|roundUpToAlignment|sizeof|sizeofValue|sort|split|startsWith|stride)\b - statement.built_in: \b(strideof|strideofValue|swap|toString|transcode|underestimateCount|unsafeAddressOf|unsafeBitCast)\b - statement.built_in: \b(unsafeDowncast|unsafeUnwrap|unsafeReflect|withExtendedLifetime|withObjectAtPlusZero|withUnsafePointer)\b - statement.built_in: \b(withUnsafePointerToObject|withUnsafeMutablePointer|withUnsafeMutablePointers|withUnsafePointer)\b - statement.built_in: \b(withUnsafePointers|withVaList|zip)\b # Meta - statement.meta: \@\b(autoclosure|available|convention|exported|IBAction|IBDesignable|IBOutlet|IBInspectable|infix)\b - statement.meta: \@\b(lazy|noreturn|noescape|nonobjc|NSApplicationMain|NSCopying|NSManaged|objc|prefix|postfix)\b - statement.meta: \@\b(required|testable|warn_unused_result|UIApplicationMain)\b #preprocessor - preproc: ^[[:space:]]*#[[:space:]]*(define|else|elseif|endif|if|selector)\b - preproc.DebugIdentifier: \b(__COLUMN__|__FILE__|__FUNCTION__|__LINE__)\b - preproc.DebugIdentifier: ^[[:space:]]*#[[:space:]]*(column|file|function|line)\b # Constant - constant: \b(true|false|nil) - constant.number: ([0-9]+) # Storage Types - type.storage: \b((U)?Int(8|16|32|64))\b - type.storage: \b(Int|UInt|String|Bit|Bool|Character|Double|Optional|Float|Range)\b - type.storage: \b(AnyObject)\b # Collections - type.collections: \b(Array|Dictionary|Set)\b # Ctypes - type.ctypes: \b(CBool|CChar|CUnsignedChar|CShort|CUnsignedShort|CInt|CUnsignedInt|CLong|CUnsignedLong|CLongLong|CUnsignedLongLong|CWideChar|CChar16|CChar32|CFloat|CDouble)\b # String - constant.string: start: \" end: \" skip: \\. rules: - constant.specialChar: (\\0|\\\\|\\t|\\n|\\r|\\"|\\') - constant.interpolation: \\\([[:graph:]]*\) - constant.unicode: \\u\{[[:xdigit:]]+} # Shebang Line - comment.shebang: ^(#!).* # Doc Comment - comment.doc: (///).* # Line Comment - comment.line: "//.*" # Block Comment - comment.block: start: "/\\*" end: "\\*/" rules: - todo: "(TODO|XXX|FIXME):?" # Doc Block Comment - comment.block: start: "/\\*\\*" end: "\\*/" rules: - todo: "(TODO|XXX|FIXME):?" # Todo - todo: "(TODO|XXX|FIXME):?" micro-2.0.14/runtime/syntax/syntax_converter.go0000664000175000017510000001162514663411671021233 0ustar nileshnilesh//+build ignore package main import ( "fmt" "io/ioutil" "os" "regexp" "strings" ) type SingleRule struct { color string regex string } type MultiRule struct { color string start string end string } // JoinRule takes a syntax rule (which can be multiple regular expressions) // and joins it into one regular expression by ORing everything together func JoinRule(rule string) string { split := strings.Split(rule, `" "`) joined := strings.Join(split, "|") return joined } func parseFile(text, filename string) (filetype, syntax, header string, rules []interface{}) { lines := strings.Split(text, "\n") // Regex for parsing syntax statements syntaxParser := regexp.MustCompile(`syntax "(.*?)"\s+"(.*)"+`) // Regex for parsing header statements headerParser := regexp.MustCompile(`header "(.*)"`) // Regex for parsing standard syntax rules ruleParser := regexp.MustCompile(`color (.*?)\s+(?:\((.+?)?\)\s+)?"(.*)"`) // Regex for parsing syntax rules with start="..." end="..." ruleStartEndParser := regexp.MustCompile(`color (.*?)\s+(?:\((.+?)?\)\s+)?start="(.*)"\s+end="(.*)"`) for lineNum, line := range lines { line = strings.TrimSpace(line) if line == "" { continue } if strings.HasPrefix(line, "#") { continue } if strings.HasPrefix(line, "syntax") { syntaxMatches := syntaxParser.FindSubmatch([]byte(line)) if len(syntaxMatches) == 3 { filetype = string(syntaxMatches[1]) syntax = JoinRule(string(syntaxMatches[2])) } else { fmt.Println(filename, lineNum, "Syntax statement is not valid: "+line) continue } } if strings.HasPrefix(line, "header") { // Header statement headerMatches := headerParser.FindSubmatch([]byte(line)) if len(headerMatches) == 2 { header = JoinRule(string(headerMatches[1])) } else { fmt.Println(filename, lineNum, "Header statement is not valid: "+line) continue } } // Syntax rule, but it could be standard or start-end if ruleParser.MatchString(line) { // Standard syntax rule // Parse the line submatch := ruleParser.FindSubmatch([]byte(line)) var color string var regexStr string var flags string if len(submatch) == 4 { // If len is 4 then the user specified some additional flags to use color = string(submatch[1]) flags = string(submatch[2]) if flags != "" { regexStr = "(?" + flags + ")" + JoinRule(string(submatch[3])) } else { regexStr = JoinRule(string(submatch[3])) } } else if len(submatch) == 3 { // If len is 3, no additional flags were given color = string(submatch[1]) regexStr = JoinRule(string(submatch[2])) } else { // If len is not 3 or 4 there is a problem fmt.Println(filename, lineNum, "Invalid statement: "+line) continue } rules = append(rules, SingleRule{color, regexStr}) } else if ruleStartEndParser.MatchString(line) { // Start-end syntax rule submatch := ruleStartEndParser.FindSubmatch([]byte(line)) var color string var start string var end string // Use m and s flags by default if len(submatch) == 5 { // If len is 5 the user provided some additional flags color = string(submatch[1]) start = string(submatch[3]) end = string(submatch[4]) } else if len(submatch) == 4 { // If len is 4 the user did not provide additional flags color = string(submatch[1]) start = string(submatch[2]) end = string(submatch[3]) } else { // If len is not 4 or 5 there is a problem fmt.Println(filename, lineNum, "Invalid statement: "+line) continue } // rules[color] = "(?" + flags + ")" + "(" + start + ").*?(" + end + ")" rules = append(rules, MultiRule{color, start, end}) } } return } func generateFile(filetype, syntax, header string, rules []interface{}) string { output := "" output += fmt.Sprintf("filetype: %s\n\n", filetype) output += fmt.Sprintf("detect: \n filename: \"%s\"\n", strings.Replace(strings.Replace(syntax, "\\", "\\\\", -1), "\"", "\\\"", -1)) if header != "" { output += fmt.Sprintf(" signature: \"%s\"\n", strings.Replace(strings.Replace(header, "\\", "\\\\", -1), "\"", "\\\"", -1)) } output += "\nrules:\n" for _, r := range rules { if rule, ok := r.(SingleRule); ok { output += fmt.Sprintf(" - %s: \"%s\"\n", rule.color, strings.Replace(strings.Replace(rule.regex, "\\", "\\\\", -1), "\"", "\\\"", -1)) } else if rule, ok := r.(MultiRule); ok { output += fmt.Sprintf(" - %s:\n", rule.color) output += fmt.Sprintf(" start: \"%s\"\n", strings.Replace(strings.Replace(rule.start, "\\", "\\\\", -1), "\"", "\\\"", -1)) output += fmt.Sprintf(" end: \"%s\"\n", strings.Replace(strings.Replace(rule.end, "\\", "\\\\", -1), "\"", "\\\"", -1)) output += fmt.Sprintf(" rules: []\n\n") } } return output } func main() { if len(os.Args) < 2 { fmt.Println("no args") return } data, _ := ioutil.ReadFile(os.Args[1]) fmt.Print(generateFile(parseFile(string(data), os.Args[1]))) } micro-2.0.14/runtime/syntax/systemd.yaml0000664000175000017510000001211114663411671017632 0ustar nileshnileshfiletype: systemd detect: filename: "\\.(service|socket|timer)$" header: "^\\[Unit\\]$" rules: - statement: "^(Accept|After|Alias|AllowIsolate|Also|ANSI_COLOR|_AUDIT_LOGINUID|_AUDIT_SESSION|Backlog|Before|BindIPv6Only|BindsTo|BindToDevice|BlockIOReadBandwidth|BlockIOWeight|BlockIOWriteBandwidth|_BOOT_ID|Broadcast|BUG_REPORT_URL|BusName|Capabilities|CapabilityBoundingSet|CHASSIS|cipher|class|_CMDLINE|CODE_FILE|CODE_FUNC|CODE_LINE|_COMM|Compress|ConditionACPower|ConditionCapability|ConditionDirectoryNotEmpty|ConditionFileIsExecutable|ConditionFileNotEmpty|ConditionHost|ConditionKernelCommandLine|ConditionNull|ConditionPathExists|ConditionPathExistsGlob|ConditionPathIsDirectory|ConditionPathIsMountPoint|ConditionPathIsReadWrite|ConditionPathIsSymbolicLink|ConditionSecurity|ConditionVirtualization|Conflicts|ControlGroup|ControlGroupAttribute|ControlGroupModify|ControlGroupPersistent|controllers|Controllers|CPE_NAME|CPUAffinity|CPUSchedulingPolicy|CPUSchedulingPriority|CPUSchedulingResetOnFork|CPUShares|CrashChVT|CrashShell|__CURSOR|debug|DefaultControllers|DefaultDependencies|DefaultLimitAS|DefaultLimitCORE|DefaultLimitCPU|DefaultLimitDATA|DefaultLimitFSIZE|DefaultLimitLOCKS|DefaultLimitMEMLOCK|DefaultLimitMSGQUEUE|DefaultLimitNICE|DefaultLimitNOFILE|DefaultLimitNPROC|DefaultLimitRSS|DefaultLimitRTPRIO|DefaultLimitRTTIME|DefaultLimitSIGPENDING|DefaultLimitSTACK|DefaultStandardError|DefaultStandardOutput|Description|DeviceAllow|DeviceDeny|DirectoryMode|DirectoryNotEmpty|Documentation|DumpCore|entropy|Environment|EnvironmentFile|ERRNO|event_timeout|_EXE|ExecReload|ExecStart|ExecStartPost|ExecStartPre|ExecStop|ExecStopPost|ExecStopPre|filter|FONT|FONT_MAP|FONT_UNIMAP|ForwardToConsole|ForwardToKMsg|ForwardToSyslog|FreeBind|freq|FsckPassNo|fstab|_GID|Group|GuessMainPID|HandleHibernateKey|HandleLidSwitch|HandlePowerKey|HandleSuspendKey|hash|HibernateKeyIgnoreInhibited|HOME_URL|_HOSTNAME|ICON_NAME|ID|IdleAction|IdleActionSec|ID_LIKE|ID_MODEL|ID_MODEL_FROM_DATABASE|IgnoreOnIsolate|IgnoreOnSnapshot|IgnoreSIGPIPE|InaccessibleDirectories|InhibitDelayMaxSec|init|IOSchedulingClass|IOSchedulingPriority|IPTOS|IPTTL|JobTimeoutSec|JoinControllers|KeepAlive|KEYMAP|KEYMAP_TOGGLE|KillExcludeUsers|KillMode|KillOnlyUsers|KillSignal|KillUserProcesses|LidSwitchIgnoreInhibited|LimitAS|LimitCORE|LimitCPU|LimitDATA|LimitFSIZE|LimitLOCKS|LimitMEMLOCK|LimitMSGQUEUE|LimitNICE|LimitNOFILE|LimitNPROC|LimitRSS|LimitRTPRIO|LimitRTTIME|LimitSIGPENDING|LimitSTACK|link_priority|valueListenDatagram|ListenFIFO|ListenMessageQueue|ListenNetlink|ListenSequentialPacket|ListenSpecial|ListenStream|LogColor|LogLevel|LogLocation|LogTarget|luks|_MACHINE_ID|MakeDirectory|Mark|MaxConnections|MaxFileSec|MaxLevelConsole|MaxLevelKMsg|MaxLevelStore|MaxLevelSyslog|MaxRetentionSec|MemoryLimit|MemorySoftLimit|MESSAGE|MESSAGE_ID|MessageQueueMaxMessages|MessageQueueMessageSize|__MONOTONIC_TIMESTAMP|MountFlags|NAME|NAutoVTs|Nice|NonBlocking|NoNewPrivileges|NotifyAccess|OnActiveSec|OnBootSec|OnCalendar|OnFailure|OnFailureIsolate|OnStartupSec|OnUnitActiveSec|OnUnitInactiveSec|OOMScoreAdjust|Options|output|PAMName|PartOf|PassCredentials|PassSecurity|PathChanged|PathExists|PathExistsGlob|PathModified|PermissionsStartOnly|_PID|PIDFile|PipeSize|PowerKeyIgnoreInhibited|PRETTY_HOSTNAME|PRETTY_NAME|Priority|PRIORITY|PrivateNetwork|PrivateTmp|PropagatesReloadTo|pss|RateLimitBurst|RateLimitInterval|ReadOnlyDirectories|ReadWriteDirectories|__REALTIME_TIMESTAMP|ReceiveBuffer|RefuseManualStart|RefuseManualStop|rel|ReloadPropagatedFrom|RemainAfterExit|RequiredBy|Requires|RequiresMountsFor|RequiresOverridable|Requisite|RequisiteOverridable|ReserveVT|ResetControllers|Restart|RestartPreventExitStatus|RestartSec|RootDirectory|RootDirectoryStartOnly|RuntimeKeepFree|RuntimeMaxFileSize|RuntimeMaxUse|RuntimeWatchdogSec|samples|scale_x|scale_y|Seal|SecureBits|_SELINUX_CONTEXT|SendBuffer|SendSIGKILL|Service|ShowStatus|ShutdownWatchdogSec|size|SmackLabel|SmackLabelIPIn|SmackLabelIPOut|SocketMode|Sockets|SourcePath|_SOURCE_REALTIME_TIMESTAMP|SplitMode|StandardError|StandardInput|StandardOutput|StartLimitAction|StartLimitBurst|StartLimitInterval|static_node|StopWhenUnneeded|Storage|string_escape|none|replaceSuccessExitStatus|SupplementaryGroups|SUPPORT_URL|SuspendKeyIgnoreInhibited|SyslogFacility|SYSLOG_FACILITY|SyslogIdentifier|SYSLOG_IDENTIFIER|SyslogLevel|SyslogLevelPrefix|SYSLOG_PID|SystemCallFilter|SYSTEMD_ALIAS|_SYSTEMD_CGROUP|_SYSTEMD_OWNER_UID|SYSTEMD_READY|_SYSTEMD_SESSION|_SYSTEMD_UNIT|_SYSTEMD_USER_UNIT|SYSTEMD_WANTS|SystemKeepFree|SystemMaxFileSize|SystemMaxUse|SysVStartPriority|TCPCongestion|TCPWrapName|timeout|TimeoutSec|TimeoutStartSec|TimeoutStopSec|TimerSlackNSec|Transparent|_TRANSPORT|tries|TTYPath|TTYReset|TTYVHangup|TTYVTDisallocate|Type|_UID|UMask|Unit|User|UtmpIdentifier|VERSION|VERSION_ID|WantedBy|Wants|WatchdogSec|What|Where|WorkingDirectory)=" - preproc: "^\\.include\\>" - symbol: "=" - special: "^\\[(Unit|Install|Service|Socket|Timer)\\]" - identifier.class: "\\$MAINPID" - constant.bool: "\\b(true|false)\\b" - comment: "(^|[[:space:]])#([^{].*)?$" - indent-char.whitespace: "[[:space:]]+$" - indent-char: " + +| + +" micro-2.0.14/runtime/syntax/tcl.yaml0000664000175000017510000000426714663411671016741 0ustar nileshnileshfiletype: tcl detect: filename: "\\.tcl$" header: "^#!.*/(env +)?tclsh( |$)" rules: - statement: "\\b(after|append|array|auto_execok|auto_import|auto_load|auto_load_index|auto_qualify|binary|break|case|catch|cd|clock|close|concat|continue|else|elseif|encoding|eof|error|eval|exec|exit|expr|fblocked|fconfigure|fcopy|file|fileevent|flush|for|foreach|format|gets|glob|global|history|if|incr|info|interp|join|lappend|lindex|linsert|list|llength|load|lrange|lreplace|lsearch|lset|lsort|namespace|open|package|pid|puts|pwd|read|regexp|regsub|rename|return|scan|seek|set|socket|source|split|string|subst|switch|tclLog|tell|time|trace|unknown|unset|update|uplevel|upvar|variable|vwait|while)\\b" - statement: "\\b(array anymore|array donesearch|array exists|array get|array names|array nextelement|array set|array size|array startsearch|array statistics|array unset)\\b" - statement: "\\b(string bytelength|string compare|string equal|string first|string index|string is|string last|string length|string map|string match|string range|string repeat|string replace|string to|string tolower|string totitle|string toupper|string trim|string trimleft|string trimright|string will|string wordend|string wordstart)\\b" - statement: "\\b(alarm|auto_load_pkg|bsearch|catclose|catgets|catopen|ccollate|cconcat|cequal|chgrp|chmod|chown|chroot|cindex|clength|cmdtrace|commandloop|crange|csubstr|ctoken|ctype|dup|echo|execl|fcntl|flock|fork|fstat|ftruncate|funlock|host_info|id|infox|keyldel|keylget|keylkeys|keylset|kill|lassign|lcontain|lempty|lgets|link|lmatch|loadlibindex|loop|lvarcat|lvarpop|lvarpush|max|min|nice|pipe|profile|random|readdir|replicate|scancontext|scanfile|scanmatch|select|server_accept|server_create|signal|sleep|sync|system|tclx_findinit|tclx_fork|tclx_load_tndxs|tclx_sleep|tclx_system|tclx_wait|times|translit|try_eval|umask|wait)\\b" - identifier.class: "proc[[:space:]]|(\\{|\\})" - symbol.operator: "(\\(|\\)|\\;|`|\\\\|\\$|<|>|!|=|&|\\|)" - constant.number: "\\b[0-9]+(\\.[0-9]+)?\\b" - constant.string: "\"(\\\\.|[^\"])*\"|'(\\\\.|[^'])*'" - identifier.var: "\\$\\{?[0-9A-Z_!@#$*?-]+\\}?" - comment: "(^|;)[[:space:]]*#.*" - indent-char.whitespace: "[[:space:]]+$" micro-2.0.14/runtime/syntax/terraform.yaml0000664000175000017510000001003714663411671020150 0ustar nileshnilesh# # This syntax definition is based on the Terraform guide: # https://www.terraform.io/docs/configuration/index.html # # Formatting is loosely based on Sublime's and VSCode's syntax highlighting for Terraform: # https://github.com/totoroot/Terraform.tmLanguage/blob/master/Terraform.sublime-syntax # https://github.com/hashicorp/vscode-terraform/blob/main/syntaxes/terraform.tmGrammar.json # filetype: terraform detect: # File Extensions: # # - ".tf": the standard file extension # https://www.terraform.io/docs/configuration/index.html#code-organization # # - ".hcl": non-terraform tools often use this HCL syntax, i.e. Vault # https://www.vaultproject.io/docs/configuration/ filename: "\\.tf$|\\.hcl$" rules: # Named Values # # https://www.terraform.io/docs/language/expressions/references.html - identifier: "\\b(var|local|module|data|path|terraform)\\b" # Block types # # resource: https://www.terraform.io/docs/language/resources/syntax.html # provider: https://www.terraform.io/docs/language/providers/configuration.html # variable: https://www.terraform.io/docs/language/values/variables.html # output: https://www.terraform.io/docs/language/values/outputs.html # locals: https://www.terraform.io/docs/language/values/locals.html # module: https://www.terraform.io/docs/language/modules/syntax.html # data: https://www.terraform.io/docs/language/data-sources/index.html # terraform: https://www.terraform.io/docs/language/settings/index.html#terraform-block-syntax - special: "\\b(resource|provider|variable|output|locals|module|terraform)\\b" # Built-In type keywords # # https://www.terraform.io/docs/language/expressions/type-constraints.html#primitive-types # https://www.terraform.io/docs/language/expressions/type-constraints.html#dynamic-types-the-quot-any-quot-constraint - type.keyword: "\\b(any|string|number|bool)\\b" # Built-In Functions # # https://www.terraform.io/docs/language/functions/index.html - statement: "\\b(abs|ceil|floor|log|max|min|parseint|pow|signum|chomp|format|formatlist|indent|join|lower|regex|regexall|replace|split|strrev|substr|title|trim|trimprefix|trimsuffix|trimspace|upper|alltrue|anytrue|chunklist|coalesce|coalescelist|compact|concat|contains|distinct|element|flatten|index|keys|length|list|lookup|map|matchkeys|merge|one|range|reverse|setintersection|setproduct|setsubtract|setunion|slice|sort|sum|transpose|values|zipmap|base64decode|base64encode|base64gzip|csvdecode|jsondecode|jsonencode|textdecodebase64|textencodebase64|urlencode|yamldecode|yamlencode|abspath|dirname|pathexpand|basename|file|fileexists|fileset|filebase64|templatefile|formatdate|timeadd|timestamp|base64sha256|base64sha512|bcrypt|filebase64sha256|filebase64sha512|filemd5|filesha1|filesha256|filesha512|md5|rsadecrypt|sha1|sha256|sha512|uuid|uuidv5|cidrhost|cidrnetmask|cidrsubnet|cidrsubnets|can|defaults|nonsensitive|sensitive|tobool|tolist|tomap|tonumber|toset|tostring|try)\\b" - symbol.operator: "([~^.:;,+*|=!\\%@]|<|>|/|-|&)" - symbol.brackets: "([(){}]|\\[|\\])" - constant.number: "\\b([0-9]+|0x[0-9a-fA-F]*)\\b|'.'" - constant.bool: "\\b(true|false|null)\\b" - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "%." - constant.specialChar: "\\\\[abfnrtv'\\\"\\\\]" - constant.specialChar: "\\\\([0-7]{3}|x[A-Fa-f0-9]{2}|u[A-Fa-f0-9]{4}|U[A-Fa-f0-9]{8})" - constant.string: start: "''" end: "''" skip: "\\\\." rules: - constant.specialChar: "%." - constant.specialChar: "\\\\[abfnrtv'\\\"\\\\]" - constant.specialChar: "\\\\([0-7]{3}|x[A-Fa-f0-9]{2}|u[A-Fa-f0-9]{4}|U[A-Fa-f0-9]{8})" - comment: start: "#|//" end: "$\\n?" rules: - todo: "(TODO|XXX|FIXME):?" - comment: start: "/\\*" end: "\\*/" rules: - todo: "(TODO|XXX|FIXME):?" micro-2.0.14/runtime/syntax/tex.yaml0000664000175000017510000000142314663411671016746 0ustar nileshnileshfiletype: tex detect: filename: "\\.tex$|\\.bib$|\\.cls$" rules: # colorize the identifiers of {} and [] - identifier: start: "\\{" end: "\\}" rules: [] - identifier: start: "\\[" end: "\\]" rules: [] # numbers - constant.number: "\\b[0-9]+(\\.[0-9]+)?([[:space:]](pt|mm|cm|in|ex|em|bp|pc|dd|cc|nd|nc|sp))?\\b" # let brackets have the default color again - default: "[{}\\[\\]]" - special: "[&\\\\]" # macros - statement: "\\\\@?[a-zA-Z_]+" - statement: "\\\\%" # comments - comment: start: "[^\\\\]%|^%" end: "$" rules: [] - comment: start: "\\\\begin\\{comment\\}" end: "\\\\end\\{comment\\}" rules: [] micro-2.0.14/runtime/syntax/toml.yaml0000664000175000017510000000330614663411671017123 0ustar nileshnileshfiletype: toml detect: filename: "\\.toml" rules: # Punctuation - symbol: '[=,\.]' - symbol.brackets: '[{\[\]}]' # Strings - constant.string: start: '"""' end: '\"{3,5}' skip: '\\.' rules: - constant.specialChar: '\\u[[:xdigit:]]{4}' - constant.specialChar: '\\U[[:xdigit:]]{8}' - constant.specialChar: '\\[btnfr"\\]' - constant.string: start: '"' end: '"' skip: '\\.' rules: - constant.specialChar: '\\u[[:xdigit:]]{4}' - constant.specialChar: '\\U[[:xdigit:]]{8}' - constant.specialChar: '\\[btnfr"\\]' - constant.string: start: "'''" end: "'{3,5}" rules: [] - constant.string: start: "'" end: "'" rules: [] # Integer - constant.number: '[+-]?(\d+_)*\d+\b' - constant.number: '(0x([[:xdigit:]]+_)*[[:xdigit:]]+|0o([0-7]_)*[0-7]+|0b([01]+_)*[01]+)' # Float - constant.number: '[+-]?(\d+_)*\d+\.(\d+_)*\d+' - constant.number: '[+-]?(\d+_)*\d+(\.(\d+_)*\d+)?[Ee][+-]?(\d+_)*\d+' - constant.number: '(\+|-)(inf|nan)' # Bare key, keys starting with a digit or dash are ambiguous with numbers and are skipped - identifier: '\b[A-Za-z_][A-Za-z0-9_-]*\b' # Boolean and inf, nan without sign - constant.bool.true: '\btrue\b' - constant.bool.false: '\bfalse\b' - constant.number: '\b(inf|nan)\b' # Date and Time - constant: '\d+-\d{2}-\d{2}([T ]\d{2}:\d{2}:\d{2}(\.\d+)?([+-]\d{2}:\d{2}|Z)?)?' - constant: '\d{2}:\d{2}:\d{2}(\.\d+)?' # Comments - comment: start: "#" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" micro-2.0.14/runtime/syntax/twig.yaml0000664000175000017510000000477514663411671017135 0ustar nileshnileshfiletype: twig detect: filename: "\\.twig$" rules: - include: "html" - symbol.tag: start: "\\{\\{[[:space:]]" end: "[[:space:]]\\}\\}" rules: - identifier: "\\b(abs|batch|capitalize|convert|encoding|date(_modify)?|default|escape|first|format|join|json_encode|keys|last|length|lower|merge|nl2br|number_format|raw|replace|reverse|round|slice|sort|split|striptags|title|trim|upper|url_encode)\\b" - identifier.class: "\\b(attribute|block|constant|cycle|date|dump|include|max|min|parent|random|range|source|template_from_string)\\b" - type.keyword: "\\b(and|as|constant|defined|divisibleby|empty|even|false|in|is|iterable|not|null|odd|or|same(as)?|true|with)\\b" - symbol.operator: "[.:;,+*?|=!\\%]|<|>|/|-|&" - symbol.brackets: "[(){}]|\\[|\\]" - constant.number: "\\b[0-9]+\\b|\\b0x[0-9A-Fa-f]+\\b" - constant.string: start: "\"" end: "\"" skip: "\\\\" rules: - constant.specialChar: "\\\\." - constant.string: start: "'" end: "'" skip: "\\\\" rules: - constant.specialChar: "\\\\." - symbol.tag: start: "\\{%[[:space:]]" end: "[[:space:]]%\\}" rules: - identifier: "\\b(abs|batch|capitalize|convert|encoding|date(_modify)?|default|escape|first|format|join|json_encode|keys|last|length|lower|merge|nl2br|number_format|raw|replace|reverse|round|slice|sort|split|striptags|title|trim|upper|url_encode)\\b" - identifier.class: "\\b(attribute|block|constant|cycle|date|dump|include|max|min|parent|random|range|source|template_from_string)\\b" - type.keyword: "\\b(and|as|constant|defined|divisibleby|empty|even|false|in|is|iterable|not|null|odd|or|same(as)?|true|with)\\b" - symbol.operator: "[.:;,+*?|=!\\%]|<|>|/|-|&" - symbol.brackets: "[(){}]|\\[|\\]" - constant.number: "\\b[0-9]+\\b|\\b0x[0-9A-Fa-f]+\\b" - constant.string: start: "\"" end: "\"" skip: "\\\\" rules: - constant.specialChar: "\\\\." - constant.string: start: "'" end: "'" skip: "\\\\" rules: - constant.specialChar: "\\\\." - comment: start: "\\{#" end: "#\\}" rules: [] micro-2.0.14/runtime/syntax/typescript.yaml0000664000175000017510000000357114663411671020362 0ustar nileshnileshfiletype: typescript detect: filename: "\\.tsx?$" rules: - constant.number: "\\b[-+]?([1-9][0-9]*|0[0-7]*|0x[0-9a-fA-F]+)([uU][lL]?|[lL][uU]?)?\\b" - constant.number: "\\b[-+]?([0-9]+\\.[0-9]*|[0-9]*\\.[0-9]+)([EePp][+-]?[0-9]+)?[fFlL]?" - constant.number: "\\b[-+]?([0-9]+[EePp][+-]?[0-9]+)[fFlL]?" - identifier: "[A-Za-z_][A-Za-z0-9_]*[[:space:]]*[(]" - statement: "\\b(abstract|as|async|await|break|case|catch|class|const|constructor|continue)\\b" - statement: "\\b(debugger|declare|default|delete|do|else|enum|export|extends|finally|for|from)\\b" - statement: "\\b(function|get|if|implements|import|in|instanceof|interface|is|let|module|namespace)\\b" - statement: "\\b(new|of|package|private|protected|public|require|return|set|static|super|switch)\\b" - statement: "\\b(this|throw|try|type|typeof|var|void|while|with|yield)\\b" - constant: "\\b(false|true|null|undefined|NaN)\\b" - type: "\\b(Array|Boolean|Date|Enumerator|Error|Function|Math)\\b" - type: "\\b(Number|Object|RegExp|String|Symbol)\\b" - type: "\\b(any|unknown|boolean|never|number|string|symbol)\\b" - statement: "[-+/*=<>!~%?:&|]" - constant: "/[^*]([^/]|(\\\\/))*[^\\\\]/[gim]*" - constant: "\\\\[0-7][0-7]?[0-7]?|\\\\x[0-9a-fA-F]+|\\\\[bfnrt'\"\\?\\\\]" - comment: start: "//" end: "$" rules: [] - comment: start: "/\\*" end: "\\*/" rules: - todo: "TODO:?" - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\." - constant.string: start: "'" end: "'" skip: "\\\\." rules: - constant.specialChar: "\\\\." - constant.string: start: "`" end: "`" rules: - constant.specialChar: "\\\\." - identifier: "\\x24\\{.*?\\}" micro-2.0.14/runtime/syntax/v.yaml0000664000175000017510000000510714663411671016416 0ustar nileshnileshfiletype: v detect: rules: # Conditionals and control flow - preproc: "\\b(module|import)\\b" - statement: "\\b(if|else|for|match|select|defer|or|unsafe)\\b" - statement: "\\b(break|continue|goto|return)\\b" - type.keyword: "\\b(assert|const|enum|fn|struct|interface|type)\\b" - type.keyword: "\\b(pub|mut|__global)\\b" - preproc: "\\$\\b(if|else)\\b" - identifier.os: "\\b(mac|macos|linux|windows|freebsd|openbsd|netbsd|dragonfly|android|solaris|haiku)\\b" - identifier.compiler: "\\b(gcc|tinyc|clang|mingw|msvc|cplusplus)\\b" - identifier.platform: "\\b(amd64|aarch64|x64|x32|little_endian|big_endian)\\b" - identifier.other: "\\b(debug|test|js|glibc|prealloc|no_bounds_checking)\\b" - identifier.class: "\\b([A-Z][A-Za-z0-9_]*)\\b" - identifier.function: "\\b([a-z_]+\\()" - symbol.operator: "\\b(i[ns])\\b|[-+/*<>!=~*%&:|,.?]" - symbol.attribute: start: "^\\[" end: "\\]$" rules: - default: ".*" - symbol: "\\b(deprecated|direct_array_access|if|inline|live|ref_only|typedef|windows_stdcall)\\b" # Types - type: "\\b(byte|u(16|32|64|128)|i(nt|8|16|64|128)|f(32|64))\\b" - type: "\\b(bool|cha[nr]|map|rune|string)\\b" - type: "\\b(any(_int|_float)?|size_t|(uint|byte|char|void)ptr)\\b" - constant.bool: "\\b(true|false)\\b" - constant.none: "\\b(none)\\b" # Brackets - symbol.brackets: "(\\{|\\})" - symbol.brackets: "(\\(|\\))" - symbol.brackets: "(\\[|\\])" # Numbers and strings - constant.number: "\\b(0b[01_]+)\\b" - constant.number: "\\b(0o[0-7_]+)\\b" - constant.number: "\\b(0x[0-9a-fA-F_]+)\\b" - constant.number: "\\b([0-9_]+)\\b" - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "%." - constant.specialChar: "\\\\[abefnrtv'\\\"\\\\]" - constant.specialChar: "\\\\([0-7]{3}|x[A-Fa-f0-9]{2}|u[A-Fa-f0-9]{4}|U[A-Fa-f0-9]{8})" - constant.string: start: "'" end: "'" skip: "\\\\." rules: - constant.specialChar: "%." - constant.specialChar: "\\\\[abefnrtv'\\\"\\\\]" - constant.specialChar: "\\\\([0-7]{3}|x[A-Fa-f0-9]{2}|u[A-Fa-f0-9]{4}|U[A-Fa-f0-9]{8})" - constant.string: start: "`" end: "`" rules: [] - comment: start: "//" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" - comment: start: "/\\*" end: "\\*/" rules: - todo: "(TODO|XXX|FIXME):?" micro-2.0.14/runtime/syntax/vala.yaml0000664000175000017510000000170714663411671017076 0ustar nileshnileshfiletype: vala detect: filename: "\\.vala$" rules: - type: "\\b(float|double|bool|u?char|u?int(8|16|32|64)?|u?short|u?long|void|s?size_t|unichar)\\b" - identifier.class: "[A-Za-z_][A-Za-z0-9_]*[[:space:]]*[()]" - statement: "\\b(for|if|while|do|else|case|default|switch|try|throw|catch)\\b" - statement: "\\b(inline|typedef|struct|enum|union|extern|static|const)\\b" - statement: "\\b(operator|new|delete|return|null)\\b" - statement: "\\b(class|override|private|public|signal|this|weak)\\b" - special: "\\b(goto|break|continue)\\b" - constant.bool: "\\b(true|false)\\b" - constant.number: "\\b([0-9]+)\\b" - symbol.operator: "[\\-+/*=<>?:!~%&|]|->" - constant.string: "\"(\\\\.|[^\"])*\"|'(\\\\.|[^'])*'" - comment: "(^|[[:space:]])//.*" - comment: start: "/\\*" end: "\\*/" rules: [] - todo: "TODO:?" - indent-char.whitespace: "[[:space:]]+$" - indent-char: " + +| + +" micro-2.0.14/runtime/syntax/verilog.yaml0000664000175000017510000000570114663411671017620 0ustar nileshnileshfiletype: verilog detect: filename: "\\.(v|vh|sv|svh)$" rules: - preproc: "\\b(module|package|program|endmodule|endpackage|endprogram)\\b" - type.keyword: "\\b(task|interface|class|endtask|endinterface|endclass)\\b" # builtin functions like $display - special: "\\$[0-9A-Za-z_]+" # Verilog keywords - statement: "\\b(always|and|assign|automatic|begin|buf|bufif0|bufif1|case|casex|casez|cell|cmos|config)\\b" - statement: "\\b(deassign|default|defparam|design|disable|edge|else|end|endcase|endconfig|endfunction|endgenerate)\\b" - statement: "\\b(endprimitive|endspecify|endtable|event|for|force|forever|fork|function|generate)\\b" - statement: "\\b(genvar|highz0|highz1|if|iff|ifnone|incdir|include|initial|input|instance|join)\\b" - statement: "\\b(large|liblist|library|localparam|macromodule|medium|nand|negedge|nmos|nor|noshowcancelled)\\b" - statement: "\\b(not|notif0|notif1|null|or|output|parameter|pmos|posedge|primitive|pull0|pull1|pulldown|pullup)\\b" - statement: "\\b(pulsestyle_onevent|pulsestyle_ondetect|rcmos|realtime|reg|release|repeat|rnmos|rpmos|rtran)\\b" - statement: "\\b(rtranif0|rtranif1|scalared|showcancelled|small|specify|specparam|strong0|strong1|supply0)\\b" - statement: "\\b(supply1|table|time|tran|tranif0|tranif1|tri0|tri1|triand|trior|trireg|use|uwire)\\b" - statement: "\\b(vectored|wait|wand|weak0|weak1|while|wor|xnor|xor)\\b" # SystemVerilog keywords - statement: "\\b(alias|always_comb|always_ff|always_latch|assert|assume|before|bind|bins|binsof|break)\\b" - statement: "\\b(chandle|clocking|const|constraint|context|continue|cover|covergroup|coverpoint|cross|dist|do)\\b" - statement: "\\b(endclocking|endgroup|endproperty|endsequence|enum)\\b" - statement: "\\b(expect|export|extends|extern|final|first_match|foreach|forkjoin|ignore_bins|illegal_bins|import)\\b" - statement: "\\b(inside|intersect|join_any|join_none|local|longint|matches|modport|new)\\b" - statement: "\\b(packed|priority|property|protected|pure|rand|randc|randcase|randsequence|ref|return)\\b" - statement: "\\b(sequence|solve|static|struct|super|tagged|this|throughout|timeprecision)\\b" - statement: "\\b(timeunit|type|typedef|union|unique|virtual|wait_order|wildcard|with|within)\\b" # types - type.keyword: "\\b(int|integer|logic|wire|tri|unsigned|signed|inout|var|shortint|shortreal|real|void|string|bit|byte)\\b" # constants - constant.number: "\\b[0-9]+\\b" - constant.number: "\\b'[su]?[dboh][0-9xzXZa-fA-F]+\\b" # .asdf(...) argument syntax - special: "\\.((\\*)|([A-Za-z][A-Za-z0-9_]*))" - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\." - comment: start: "//" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" - comment: start: "/\\*" end: "\\*/" rules: - todo: "(TODO|XXX|FIXME):?" micro-2.0.14/runtime/syntax/vhdl.yaml0000664000175000017510000000454714663411671017115 0ustar nileshnileshfiletype: vhdl detect: filename: "\\.vhdl?$" rules: - type: "(?i)\\b(string|integer|natural|positive|(un)?signed|std_u?logic(_vector)?|bit(_vector)?|boolean|u?x01z?|array|range)\\b" - identifier: "(?i)library[[:space:]]+[a-zA-Z_0-9]+" - identifier: "(?i)use[[:space:]]+[a-zA-Z_0-9\\.]+" - identifier: "(?i)component[[:space:]]+[a-zA-Z_0-9]+" - identifier: "(?i)(architecture|configuration)[[:space:]]+[a-zA-Z_0-9]+[[:space:]]+of[[:space:]]+[a-zA-Z_0-9]+" - identifier: "(?i)(entity|package)[[:space:]]+[a-zA-Z_0-9]+[[:space:]]+is" - identifier: "(?i)end[[:space:]]+((architecture|entity|component|process|package|generate)[[:space:]]+)?[a-zA-Z_0-9]+" - statement: "(?i)\\b(abs|access|after|alias|all|and|architecture|assert|attribute)\\b" - statement: "(?i)\\b(begin|block|body|buffer|bus|case|component|configuration|constant)\\b" - statement: "(?i)\\b(disconnect|downto|else|elsif|end|entity|exit)\\b" - statement: "(?i)\\b(file|for|function|generate|generic|guarded)\\b" - statement: "(?i)\\b(if|impure|in|inertial|inout|is)\\b" - statement: "(?i)\\b(label|library|linkage|literal|loop|map|mod)\\b" - statement: "(?i)\\b(nand|new|next|nor|not|null|of|on|open|or|others|out)\\b" - statement: "(?i)\\b(package|port|postponed|procedure|process|pure)\\b" - statement: "(?i)\\b(range|record|register|reject|rem|report|return|rol|ror)\\b" - statement: "(?i)\\b(select|severity|shared|signal|sla|sll|sra|srl|subtype)\\b" - statement: "(?i)\\b(then|to|transport|type|unaffected|units|until|use)\\b" - statement: "(?i)\\b(variable|wait|when|while|with|xnor|xor)\\b" - statement: "(?i)'(base|left|right|high|low|pos|val|succ|pred|leftof|rightof|image|(last_)?value)" - statement: "(?i)'((reverse_)?range|length|ascending|event|stable)" - statement: "(?i)'(simple|path|instance)_name" - statement: "(?i)\\b(std_match|(rising|falling)_edge|is_x)\\b" - statement: "(?i)\\bto_(unsigned|signed|integer|u?x01z?|stdu?logic(vector)?)\\b" - symbol.operator: "(\\+|-|\\*|/|&|<|>|=|\\.|:)" - constant.number: "(?i)'([0-1]|u|x|z|w|l|h|-)'|[box]?\"([0-1a-fA-F]|u|x|z|w|l|h|-)+\"" - constant.number: "(?i)\\b[0-9\\._]+(e[\\-]?[0-9]+)?( ?[fpnum]?s)?\\b" - constant.bool: "(?i)\\b(true|false)\\b" - constant: "(?i)\\b(note|warning|error|failure)\\b" - constant.string: "\"[^\"]*\"" - comment: "--.*" micro-2.0.14/runtime/syntax/vi.yaml0000664000175000017510000000137614663411671016573 0ustar nileshnileshfiletype: vi detect: filename: "(^|/|\\.)(ex|vim)rc$|\\.vim" rules: - identifier: "[A-Za-z_][A-Za-z0-9_]*[(]+[A-Za-z0-9_:.,\\s]*[)]+" - special: "[()]+" - statement: "\\b([nvxsoilc]?(nore|un)?map|[nvlx]n|[ico]?no|[cilovx][um]|s?unm)\\b" - statement: "\\b(snor|nun|nm|set|if|endif|let|unlet|source)\\b" - statement: "[!&=?]" - constant.number: "\\b[0-9]+\\b" - comment: start: "(^\"|[ \t]+\" |[ \t]+\"$)" end: "$" rules: [] - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\." - constant.string: start: "'" end: "'" skip: "\\\\." rules: - constant.specialChar: "\\\\." micro-2.0.14/runtime/syntax/vue.yaml0000664000175000017510000000725114663411671016752 0ustar nileshnileshfiletype: vue detect: filename: "\\.vue$" rules: - default: start: "" end: "" limit-group: symbol.tag rules: - error: "<[^!].*?>" - symbol.tag: "(?i)<[/]?(a|a(bbr|ddress|rea|rticle|side|udio)|b|b(ase|d(i|o)|lockquote|r|utton)|ca(nvas|ption)|center|cite|co(de|l|lgroup)|d(ata|atalist|d|el|etails|fn|ialog|l|t)|em|embed|fieldset|fig(caption|ure)|form|iframe|h[1-6]|hr|i|img|in(put|s)|kbd|keygen|label|legend|li|link|ma(in|p|rk)|menu|menuitem|met(a|er)|nav|noscript|o(bject|l|pt(group|ion)|utput)|p|param|picture|pre|progress|q|r(p|t|uby)|s|samp|se(ction|lect)|svg|small|source|span|strong|su(b|p|mmary)|textarea|time|track|u|ul|var|video|wbr)( .*)*?>" - symbol.tag.extended: "(?i)<[/]?(body|div|html|head(er)?|footer|title|table|t(body|d|h(ead)?|r|foot))( .*)*?>" - preproc: "(?i)<[/]?(script|style)( .*)*?>" - special: "&[^;[[:space:]]]*;" - identifier: "(alt|bgcolor|class|height|href|id|label|longdesc|name|on(click|focus|load|mouseover)|placeholder|size|span|src|style|target|type|value|width)=" - symbol: "[:=]" - constant.string: "\"[^\"]*\"" - constant.number: "(?i)#[0-9a-fA-F]{6,6}" - symbol.tag: "<|>" - constant.string.url: "(ftp(s)?|http(s)?|git|chrome)://[^ ]+" - comment: "" #- preproc: "" - comment.block: start: "" rules: [] # Bootstrap - symbol.tag.extended: "(?i)<[/]?(b-alert|b-aspect|b-avatar|b-badge|b-icon|b-breadcrumb|b-button-group|b-button-toolbar|b-button|b-calendar|b-card-text|b-card-input|b-card|b-carousel-slide|b-carousel|b-collapse|b-dropdown|b-dropdown-item|b-dropdown-divider|b-embed|b-form-checkbox-group|b-form-checkbox|b-form-datepicker|b-form-file|b-form-group|b-form-input|b-form-radio|b-form-rating|b-form-select|b-form-spinbutton|b-form-tags|b-form-textarea|b-form|b-form-timepicker|b-img-lazy|b-img|b-input-group|b-jumbotron|b-input|b-container|b-row|b-col|b-link|b-list-group|b-list-group-item|b-media|b-modal|b-nav|b-nav-item|b-nav-item-dropdown|b-nav-text|b-nav-form|b-navbar|b-navbar-brand|b-navbar-toggle|b-navbar-nav|b-overlay|b-pagination|b-pagination-nav|b-popover|b-progress|b-progress-bar|b-sidebar|b-skeleton-wrapper|b-skeleton|b-spinner|b-table|b-table-lite|b-table-simple|b-tabs|b-tab|b-time|b-toast|b-tooltip)\\b" - identifier: "(variant|title|show|shadow|icon|align-h|align-v|label-for|@submit|tag|img-alt|img-src|data-toggle|data-target|aria-controls|aria-expanded|aria-label|aria-disabled|tabindex|:interval|background|img-width|img-height|@sliding-start|@sliding-end|cols|header|@reset)=" - symbol: "[:=]" # Vue - symbol.tag.extended: "(?i)<[/]?(component|transition|transition-group|keep-alive|slot)\\b" - identifier: "(v-text|v-html|v-show|v-if|v-else|v-else-if|v-for|v-on|v-bind|v-model|v-slot|v-pre|v-cloak|v-once|key|ref|is|@click)=" - symbol: "[:=]" # Vue-router - symbol.tag.extended: "(?i)<[/]?(router-link|router-view)\\b" - identifier: "(to|v-slot)=" - symbol: "[:=]" - default: start: "" limit-group: symbol.tag rules: - include: "javascript" - default: start: "" end: "" rules: - include: "typescript" - default: start: "" end: "" limit-group: symbol.tag rules: - include: "css" micro-2.0.14/runtime/syntax/xml.yaml0000664000175000017510000000146514663411671016754 0ustar nileshnileshfiletype: xml detect: filename: "\\.(xml|sgml?|rng|svg|plist)$" header: "<\\?xml.*\\?>" rules: - preproc: start: "" rules: [] - comment: start: "" rules: [] - symbol.tag: start: "<\\??" end: "\\??>" rules: - identifier: start: " " end: "=" rules: [] - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\." - constant.string: start: "'" end: "'" skip: "\\\\." rules: - constant.specialChar: "\\\\." micro-2.0.14/runtime/syntax/xresources.yaml0000664000175000017510000000060214663411671020346 0ustar nileshnileshfiletype: xresources detect: filename: "X(defaults|resources)$" rules: - special: "^[[:alnum:]]+\\*" - identifier.var: "\\*[[:alnum:]]+\\:" - constant.number: "\\b[0-9]+\\b" - symbol.operator: "[*:=]" - constant.bool: "\\b(true|false)\\b" - comment: "(^|[[:space:]])!([^{].*)?$" - indent-char.whitespace: "[[:space:]]+$" - indent-char: " + +| + +" micro-2.0.14/runtime/syntax/yaml.yaml0000664000175000017510000000155314663411671017114 0ustar nileshnileshfiletype: yaml detect: filename: "\\.ya?ml$" header: "%YAML" rules: - type: "(^| )!!(binary|bool|float|int|map|null|omap|seq|set|str) " - constant: "\\b(YES|yes|Y|y|ON|on|TRUE|True|true|NO|no|N|n|OFF|off|FALSE|False|false)\\b" - statement: "(:[[:space:]]|\\[|\\]|:[[:space:]]+[|>]|^[[:space:]]*- )" - identifier: "[[:space:]][\\*&][A-Za-z0-9]+" - type: "[-.\\w]+:" - statement: ":" - special: "(^---|^\\.\\.\\.|^%YAML|^%TAG)" - constant.string: start: "(^| )\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\." - constant.string: start: "(^| )'" end: "'" skip: "(\\\\.)|('')" rules: - constant.specialChar: "\\\\." - comment: start: "#" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" micro-2.0.14/runtime/syntax/yum.yaml0000664000175000017510000000053314663411671016761 0ustar nileshnileshfiletype: yum detect: filename: "\\.repo$|yum.*\\.conf$" rules: - identifier: "^[[:space:]]*[^=]*=" - constant.specialChar: "^[[:space:]]*\\[.*\\]$" - statement: "\\$(releasever|arch|basearch|uuid|YUM[0-9])" - comment: "(^|[[:space:]])#([^{].*)?$" - indent-char.whitespace: "[[:space:]]+$" - indent-char: " + +| + +" micro-2.0.14/runtime/syntax/zig.yaml0000664000175000017510000000334714663411671016746 0ustar nileshnileshfiletype: zig detect: filename: "\\.z(ig|on)$" rules: # Reserved words - statement: "\\b(addrspace|align|allowzero|and|asm|async|await|break|callconv|catch|comptime|const|continue|defer|else|errdefer|error|export|extern|fn|for|if|inline|noalias|noinline|nosuspend|or|orelse|packed|pub|resume|return|linksection|suspend|switch|test|threadlocal|try|unreachable|usingnamespace|var|volatile|while)\\b" # builtin functions - special: "@[a-zA-Z_]+" # Primitive Types - type: "\\b(anyframe|anytype|anyerror|anyopaque|bool|comptime_int|comptime_float|enum|f(16|32|64|80|128)|i(8|16|32|64|128)|isize|noreturn|opaque|struct|type|union|u(8|16|32|64|128)|usize|void)\\b" - type: "\\b(c_u?(short|int|long(long)?)|c_longdouble|c_void)\\b" # Operators - symbol.operator: "[-!|=;%.+^*:&?<>~]" # Parenthesis - symbol.brackets: "[(){}]|\\[|\\]" # Constants - constant: "\\b(null|undefined)\\b" - constant.number: "\\b(0b[01_]+|0o[0-7_]+|[0-9_]+|0x[a-fA-F0-9_]+)\\b" - constant.bool: "\\b(true|false)\\b" - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\([nrt\\\\'\"]|x[a-fA-F0-9]{2}|u{[a-fA-F0-9]+})" - constant.string: start: "'" end: "'" skip: "\\\\." rules: - error: "..+" - constant.specialChar: "\\\\([nrt\\\\'\"]|x[a-fA-F0-9]{2}|u{[a-fA-F0-9]+})" - constant.string: start: "\\\\\\\\" end: "$" skip: "\\\\." rules: - constant.specialChar: "\\\\([nrt\\\\'\"]|x[a-fA-F0-9]{2}|u{[a-fA-F0-9]+})" - comment: start: "//" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" micro-2.0.14/runtime/syntax/zscript.yaml0000664000175000017510000000466314663411671017655 0ustar nileshnileshfiletype: zscript # Loosely based on the csharp.yaml definition # (?i) on everything because ZScript isn't case sensitive detect: filename: "(?i)\\.z(c|sc)$" rules: # ZScript only has one preprocessor directive and a required engine version declaration - preproc: "(?i)#include" - preproc: "(?i)version" # State labels ("goto" word overridden by state logic rule below) - symbol.tag: "(?i)[a-z0-9.]+:" - symbol.tag: "(?i)goto [a-z0-9]+[\\+0-9]*" # Classes - identifier.class: "(?i)class +[a-z0-9_]+ *((:) +[a-z0-9.]+)?" # Functions (open paren overridden by symbol.brackets rule because perl regex apparently doesn't support postive lookahead) - identifier: "(?i)[\\.]*[a-z0-9_]+[ ]*[(]+" # Variable types - type: "(?i)\\b(actor|object|vector2|vector3|name|string|color|sound|void|double|bool|int|float|float64|uint8|uint16|uint|int8|int16|TextureID|SpriteID|Array|voidptr|short|action|state|statelabel)\\b" # Keywords - statement: "(?i)\\b(class|default|private|static|native|return|if|else|for|while|do|deprecated|null|readonly|true|false|struct|extend|clearscope|vararg|ui|play|virtual|virtualscope|meta|Property|in|out|states|override|super|is|let|const|replaces|protected|self|abstract|enum|switch|case)\\b" # State logic keywords - special: "(?i)\\b(goto|loop|stop|break|continue|fail)\\b" # Symbols - symbol.operator: "[\\-+/*=<>?:!~%&|]" - symbol.brackets: "[(){}]|\\[|\\]" # Constants - constant.bool: "(?i)(\\b(true|false)\\b|NULL)" - constant.number: "(?i)\\b([0-9][.]*[0-9]*)+?\\b" - constant.number: "(?i)\\b(0x[A-Fa-f0-9_]+)?\\b" - constant.number: "(?i)\\b(0b[0-1_]+)[FL]?\\b" #- constant.number: "(?i)\\b(([0-9][.]*[0-9]*)+|0x[A-Fa-f0-9_]+|0b[0-1_]+)[FL]?\\b" # Strings - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\([btnfr]|'|\\\"|\\\\)" - constant.specialChar: "\\\\u[A-Fa-f0-9]{4}" - constant.string: start: "'" end: "'" skip: "\\\\." rules: - constant.specialChar: "\\\\([btnfr]|'|\\\"|\\\\)" - constant.specialChar: "\\\\u[A-Fa-f0-9]{4}" # Comments - comment: start: "//" end: "$" rules: - todo: "(TODO|XXX|FIXME):?" - comment: start: "/\\*" end: "\\*/" rules: - todo: "(TODO|XXX|FIXME):?" micro-2.0.14/runtime/syntax/zsh.yaml0000664000175000017510000000412214663411671016751 0ustar nileshnileshfiletype: zsh detect: filename: "(\\.zsh$|\\.?(zshenv|zprofile|zshrc|zlogin|zlogout)$)" header: "^#!.*/(env +)?zsh( |$)" rules: ## Numbers - constant.number: "\\b[0-9]+\\b" ## Conditionals and control flow - statement: "\\b(always|break|bye|case|continue|disown|do|done|elif|else|esac|exit|fi|for|function|if|in|local|read|return|select|shift|then|time|until|while)\\b" - statement: "(\\{|\\}|\\(|\\)|\\;|\\]|\\[|`|\\\\|\\$|<|>|!|=|&|\\|)" ## Conditional flags - special: "-[Ldefgruwx]\\b" - special: "-(eq|ne|gt|lt|ge|le|s|n|z)\\b" ## Bash-inherited - statement: "\\b((un)?alias|bindkey|builtin|cd|declare|eval|exec|export|jobs|let|popd|pushd|set|source|typeset|umask|unset)\\b" ## ZSH-specific - type: "\\b(add-zsh-hook|autoload|chdir|compinit|dirs|(dis|en)able|echotc|emulate|print|prompt(init)?|(un)?setopt|zle|zmodload|zstyle|whence)\\b" ## Common linux commands - statement: "\\b((g|ig)?awk|find|\\w{0,4}grep|kill|killall|\\w{0,4}less|make|pkill|sed|tar)\\b" ## Coreutils commands - statement: "\\b(base64|basename|cat|chcon|chgrp|chmod|chown|chroot|cksum|comm|cp|csplit|cut|date|dd|df|dir|dircolors|dirname|du|echo|env|expand|expr|factor|false|fmt|fold|head|hostid|id|install|join|link|ln|logname|ls|md5sum|mkdir|mkfifo|mknod|mktemp|mv|nice|nl|nohup|nproc|numfmt|od|paste|pathchk|pinky|pr|printenv|printf|ptx|pwd|readlink|realpath|rm|rmdir|runcon|seq|(sha1|sha224|sha256|sha384|sha512)sum|shred|shuf|sleep|sort|split|stat|stdbuf|stty|sum|sync|tac|tail|tee|test|timeout|touch|tr|true|truncate|tsort|tty|uname|unexpand|uniq|unlink|users|vdir|wc|who|whoami|yes)\\b" ## Function definition - identifier: "^\\s+(function\\s+)[0-9A-Z_]+\\s+\\(\\)" # (i) ## Variables - identifier: "\\$\\{?[0-9A-Z_!@#$*?-]+\\}?" #(i) - constant.string: start: "\"" end: "\"" skip: "\\\\." rules: - constant.specialChar: "\\\\." - constant.string: start: "'" end: "'" rules: [] - comment: start: "(^|\\s)#" end: "$" rules: [] micro-2.0.14/snapcraft.yaml0000664000175000017510000000154414663411671015122 0ustar nileshnileshname: micro summary: A modern and intuitive terminal-based text editor description: | Micro is a terminal-based text editor that aims to be easy to use and intuitive, while also taking advantage of the full capabilities of modern terminals. confinement: classic adopt-info: micro base: core20 apps: micro: command: bin/micro parts: micro: source: . source-type: git plugin: go build-packages: [make] build-attributes: [no-patchelf] override-pull: | snapcraftctl pull version="$(go run $SNAPCRAFT_PART_SRC/tools/build-version.go)" [ -n "$(echo $version | grep "dev")" ] && grade=devel || grade=stable snapcraftctl set-version "$version" snapcraftctl set-grade "$grade" override-build: | make build-tags mkdir $SNAPCRAFT_PART_INSTALL/bin mv ./micro $SNAPCRAFT_PART_INSTALL/bin/ micro-2.0.14/tools/0000775000175000017510000000000014663411671013411 5ustar nileshnileshmicro-2.0.14/tools/build-date.go0000664000175000017510000000066314663411671015757 0ustar nileshnilesh//+build ignore package main import ( "fmt" "os" "strconv" "time" ) func main() { var buildTime time.Time epoch := os.Getenv("SOURCE_DATE_EPOCH") if epoch != "" { i, err := strconv.Atoi(epoch) if err != nil { fmt.Errorf("SOURCE_DATE_EPOCH is not a valid integer") os.Exit(1) } buildTime = time.Unix(int64(i), 0) } else { buildTime = time.Now().Local() } fmt.Println(buildTime.Format("January 02, 2006")) } micro-2.0.14/tools/build-version.go0000664000175000017510000000402014663411671016516 0ustar nileshnilesh//+build ignore package main import ( "fmt" "log" "os/exec" "strings" "github.com/blang/semver" ) func getTag(match ...string) (string, *semver.PRVersion) { args := append([]string{ "describe", "--tags", }, match...) if tag, err := exec.Command("git", args...).Output(); err != nil { return "", nil } else { tagParts := strings.Split(string(tag), "-") if len(tagParts) == 3 { ahead, err := semver.NewPRVersion(tagParts[1]) if err == nil { return tagParts[0], &ahead } log.Printf("semver.NewPRVersion(%s): %v", tagParts[1], err) } else if len(tagParts) == 4 { ahead, err := semver.NewPRVersion(tagParts[2]) if err == nil { return tagParts[0] + "-" + tagParts[1], &ahead } log.Printf("semver.NewPRVersion(%s): %v", tagParts[2], err) } return string(tag), nil } } func main() { // Find the last vX.X.X Tag and get how many builds we are ahead of it. versionStr, ahead := getTag("--match", "v*") version, err := semver.ParseTolerant(versionStr) if err != nil { // no version tag found so just return what ever we can find. log.Printf("semver.ParseTolerant(%s): %v", versionStr, err) fmt.Println("0.0.0-unknown") return } // Get the tag of the current revision. tag, _ := getTag("--exact-match") if tag == versionStr { // Seems that we are going to build a release. // So the version number should already be correct. fmt.Println(version.String()) return } // If we don't have any tag assume "dev" if tag == "" || strings.HasPrefix(tag, "nightly") { tag = "dev" } // Get the most likely next version: if !strings.Contains(version.String(), "rc") { version.Patch = version.Patch + 1 } if pr, err := semver.NewPRVersion(tag); err == nil { // append the tag as pre-release name version.Pre = append(version.Pre, pr) } else { log.Printf("semver.NewPRVersion(%s): %v", tag, err) } if ahead != nil { // if we know how many commits we are ahead of the last release, append that too. version.Pre = append(version.Pre, *ahead) } fmt.Println(version.String()) } micro-2.0.14/tools/compile-linux.sh0000775000175000017510000000246214663411671016541 0ustar nileshnileshcd .. mkdir -p binaries mkdir -p micro-$1 cp LICENSE micro-$1 cp README.md micro-$1 cp LICENSE-THIRD-PARTY micro-$1 HASH="$(git rev-parse --short HEAD)" VERSION="$(go run tools/build-version.go)" DATE="$(go run tools/build-date.go)" ADDITIONAL_GO_LINKER_FLAGS="$(go run tools/info-plist.go $VERSION)" echo "Linux 64" GOOS=linux GOARCH=amd64 go build -ldflags "-s -w -X main.Version=$1 -X main.CommitHash=$HASH -X 'main.CompileDate=$DATE'" -o micro-$1/micro ./cmd/micro tar -czf micro-$1-linux64.tar.gz micro-$1 mv micro-$1-linux64.tar.gz binaries echo "Linux 32" GOOS=linux GOARCH=386 go build -ldflags "-s -w -X main.Version=$1 -X main.CommitHash=$HASH -X 'main.CompileDate=$DATE'" -o micro-$1/micro ./cmd/micro tar -czf micro-$1-linux32.tar.gz micro-$1 mv micro-$1-linux32.tar.gz binaries echo "Linux arm 32" GOOS=linux GOARM=6 GOARCH=arm go build -ldflags "-s -w -X main.Version=$1 -X main.CommitHash=$HASH -X 'main.CompileDate=$DATE'" -o micro-$1/micro ./cmd/micro tar -czf micro-$1-linux-arm.tar.gz micro-$1 mv micro-$1-linux-arm.tar.gz binaries echo "Linux arm 64" GOOS=linux GOARCH=arm64 go build -ldflags "-s -w -X main.Version=$1 -X main.CommitHash=$HASH -X 'main.CompileDate=$DATE'" -o micro-$1/micro ./cmd/micro tar -czf micro-$1-linux-arm64.tar.gz micro-$1 mv micro-$1-linux-arm64.tar.gz binaries rm -rf micro-$1 micro-2.0.14/tools/cross-compile.sh0000775000175000017510000000462514663411671016536 0ustar nileshnilesh#!/bin/sh set -e VERSION="$1" if [ -z "$VERSION" ]; then VERSION="$(go run tools/build-version.go)" fi mkdir -p binaries mkdir -p micro-$VERSION cp LICENSE micro-$VERSION cp README.md micro-$VERSION cp LICENSE-THIRD-PARTY micro-$VERSION cp assets/packaging/micro.1 micro-$VERSION cp assets/packaging/micro.desktop micro-$VERSION cp assets/micro-logo-mark.svg micro-$VERSION/micro.svg create_artefact_generic() { mv micro micro-$VERSION/ tar -czf micro-$VERSION-$1.tgz micro-$VERSION sha256sum micro-$VERSION-$1.tgz > micro-$VERSION-$1.tgz.sha mv micro-$VERSION-$1.* binaries rm micro-$VERSION/micro } create_artefact_windows() { mv micro.exe micro-$VERSION/ zip -r -q -T micro-$VERSION-$1.zip micro-$VERSION sha256sum micro-$VERSION-$1.zip > micro-$VERSION-$1.zip.sha mv micro-$VERSION-$1.* binaries rm micro-$VERSION/micro.exe } # Mac echo "OSX 64" GOOS=darwin GOARCH=amd64 make build create_artefact_generic "osx" # Mac ARM64 echo "MacOS ARM64" GOOS=darwin GOARCH=arm64 make build create_artefact_generic "macos-arm64" # Linux echo "Linux 64" GOOS=linux GOARCH=amd64 make build if ./tools/package-deb.sh $VERSION; then sha256sum micro-$VERSION-amd64.deb > micro-$VERSION-amd64.deb.sha mv micro-$VERSION-amd64.* binaries fi create_artefact_generic "linux64" echo "Linux 64 fully static" CGO_ENABLED=0 GOOS=linux GOARCH=amd64 make build create_artefact_generic "linux64-static" echo "Linux 32" GOOS=linux GOARCH=386 make build create_artefact_generic "linux32" echo "Linux ARM 32" GOOS=linux GOARM=6 GOARCH=arm make build create_artefact_generic "linux-arm" echo "Linux ARM 64" GOOS=linux GOARCH=arm64 make build create_artefact_generic "linux-arm64" # NetBSD echo "NetBSD 64" GOOS=netbsd GOARCH=amd64 make build create_artefact_generic "netbsd64" echo "NetBSD 32" GOOS=netbsd GOARCH=386 make build create_artefact_generic "netbsd32" # OpenBSD echo "OpenBSD 64" GOOS=openbsd GOARCH=amd64 make build create_artefact_generic "openbsd64" echo "OpenBSD 32" GOOS=openbsd GOARCH=386 make build create_artefact_generic "openbsd32" # FreeBSD echo "FreeBSD 64" GOOS=freebsd GOARCH=amd64 make build create_artefact_generic "freebsd64" echo "FreeBSD 32" GOOS=freebsd GOARCH=386 make build create_artefact_generic "freebsd32" # Windows echo "Windows 64" GOOS=windows GOARCH=amd64 make build create_artefact_windows "win64" echo "Windows 32" GOOS=windows GOARCH=386 make build create_artefact_windows "win32" rm -rf micro-$VERSION micro-2.0.14/tools/info-plist.go0000664000175000017510000000215214663411671016024 0ustar nileshnilesh//go:build ignore // +build ignore package main import ( "fmt" "io/ioutil" "os" "runtime" ) func check(e error) { if e != nil { panic(e) } } func main() { if runtime.GOOS != "darwin" { return } if len(os.Args) == 3 { if os.Args[1] == "darwin" && runtime.GOOS == "darwin" { rawInfoPlistString := ` CFBundleIdentifier io.github.micro-editor CFBundleName micro CFBundleInfoDictionaryVersion 6.0 CFBundlePackageType APPL CFBundleShortVersionString ` + os.Args[2] + ` ` infoPlistData := []byte(rawInfoPlistString) err := ioutil.WriteFile("/tmp/micro-info.plist", infoPlistData, 0644) check(err) fmt.Println("-linkmode external -extldflags -Wl,-sectcreate,__TEXT,__info_plist,/tmp/micro-info.plist") } } else { panic("missing arguments") } } micro-2.0.14/tools/nightly-release.sh0000775000175000017510000000251614663411671017050 0ustar nileshnilesh# This script updates the nightly release on Github for micro # Must be run from inside the micro git repository commitID=$(git rev-parse --short HEAD) go run remove-nightly-assets.go echo "Cross compiling binaries" ./cross-compile.sh $1 mv ../binaries . MESSAGE=$'Nightly build\n\nAutogenerated nightly build of micro' echo "Updating release" hub release edit nightly \ --prerelease \ --draft=false \ --message "$MESSAGE (please DISREGARD the creation date of this Github release). Assets uploaded on $(date) for commit $commitID." \ --attach "binaries/micro-$1-osx.tar.gz" \ --attach "binaries/micro-$1-macos-arm64.tar.gz" \ --attach "binaries/micro-$1-linux64.tar.gz" \ --attach "binaries/micro-$1-linux64-static.tar.gz" \ --attach "binaries/micro-$1-amd64.deb" \ --attach "binaries/micro-$1-linux32.tar.gz" \ --attach "binaries/micro-$1-linux-arm.tar.gz" \ --attach "binaries/micro-$1-linux-arm64.tar.gz" \ --attach "binaries/micro-$1-freebsd64.tar.gz" \ --attach "binaries/micro-$1-freebsd32.tar.gz" \ --attach "binaries/micro-$1-openbsd64.tar.gz" \ --attach "binaries/micro-$1-openbsd32.tar.gz" \ --attach "binaries/micro-$1-netbsd64.tar.gz" \ --attach "binaries/micro-$1-netbsd32.tar.gz" \ --attach "binaries/micro-$1-win64.zip" \ --attach "binaries/micro-$1-win32.zip" micro-2.0.14/tools/package-deb.sh0000775000175000017510000000057614663411671016103 0ustar nileshnileshcommand -v fpm > /dev/null && fpm -s dir -t deb -p micro-$1-amd64.deb --name micro --license mit --version $1 --deb-recommends xclip --description "A modern and intuitive terminal-based text editor" --after-install ./assets/packaging/deb/micro.postinst --before-remove ./assets/packaging/deb/micro.prerm ./micro=/usr/bin/micro ./assets/packaging/micro.1=/usr/share/man/man1/micro.1 micro-2.0.14/tools/pre-release.sh0000775000175000017510000000235114663411671016155 0ustar nileshnilesh# This script creates releases on Github for micro # You must have the correct Github access token to run this script # $1 is the title, $2 is the description commitID=$(git rev-parse HEAD) tag="v$1" echo "Creating tag" git tag $tag $commitID hub push --tags echo "Cross compiling binaries" ./cross-compile.sh $1 mv ../binaries . NL=$'\n' echo "Creating new release" hub release create $tag \ --prerelease \ --message "$1${NL}${NL}$2" \ --attach "binaries/micro-$1-osx.tar.gz" \ --attach "binaries/micro-$1-macos-arm64.tar.gz" \ --attach "binaries/micro-$1-linux64.tar.gz" \ --attach "binaries/micro-$1-linux64-static.tar.gz" \ --attach "binaries/micro-$1-amd64.deb" \ --attach "binaries/micro-$1-linux32.tar.gz" \ --attach "binaries/micro-$1-linux-arm.tar.gz" \ --attach "binaries/micro-$1-linux-arm64.tar.gz" \ --attach "binaries/micro-$1-freebsd64.tar.gz" \ --attach "binaries/micro-$1-freebsd32.tar.gz" \ --attach "binaries/micro-$1-openbsd64.tar.gz" \ --attach "binaries/micro-$1-openbsd32.tar.gz" \ --attach "binaries/micro-$1-netbsd64.tar.gz" \ --attach "binaries/micro-$1-netbsd32.tar.gz" \ --attach "binaries/micro-$1-win64.zip" \ --attach "binaries/micro-$1-win32.zip" micro-2.0.14/tools/release.sh0000775000175000017510000000232614663411671015373 0ustar nileshnilesh# This script creates releases on Github for micro # You must have the correct Github access token to run this script # $1 is the title, $2 is the description commitID=$(git rev-parse HEAD) tag="v$1" echo "Creating tag" git tag $tag $commitID hub push --tags NL=$'\n' echo "Cross compiling binaries" ./cross-compile.sh $1 mv ../binaries . echo "Creating new release" hub release create $tag \ --message "$1${NL}${NL}$2" \ --attach "binaries/micro-$1-osx.tar.gz" \ --attach "binaries/micro-$1-macos-arm64.tar.gz" \ --attach "binaries/micro-$1-linux64.tar.gz" \ --attach "binaries/micro-$1-linux64-static.tar.gz" \ --attach "binaries/micro-$1-amd64.deb" \ --attach "binaries/micro-$1-linux32.tar.gz" \ --attach "binaries/micro-$1-linux-arm.tar.gz" \ --attach "binaries/micro-$1-linux-arm64.tar.gz" \ --attach "binaries/micro-$1-freebsd64.tar.gz" \ --attach "binaries/micro-$1-freebsd32.tar.gz" \ --attach "binaries/micro-$1-openbsd64.tar.gz" \ --attach "binaries/micro-$1-openbsd32.tar.gz" \ --attach "binaries/micro-$1-netbsd64.tar.gz" \ --attach "binaries/micro-$1-netbsd32.tar.gz" \ --attach "binaries/micro-$1-win64.zip" \ --attach "binaries/micro-$1-win32.zip" micro-2.0.14/tools/remove-nightly-assets.go0000664000175000017510000000147314663411671020216 0ustar nileshnilesh//+build ignore package main import ( "fmt" "io/ioutil" "net/http" "os/exec" "strings" "github.com/zyedidia/json5" ) func main() { resp, err := http.Get("https://api.github.com/repos/zyedidia/micro/releases") if err != nil { fmt.Println(err.Error()) return } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) var data interface{} err = json5.Unmarshal(body, &data) for _, val := range data.([]interface{}) { m := val.(map[string]interface{}) releaseName := m["name"].(string) assets := m["assets"].([]interface{}) for _, asset := range assets { assetInfo := asset.(map[string]interface{}) url := assetInfo["url"].(string) if strings.Contains(strings.ToLower(releaseName), "nightly") { cmd := exec.Command("hub", "api", "-X", "DELETE", url) cmd.Run() } } } } micro-2.0.14/tools/testgen.go0000664000175000017510000001366614663411671015425 0ustar nileshnilesh//+build ignore package main import ( "fmt" "io/ioutil" "log" "os" "regexp" "strings" "github.com/robertkrimen/otto/ast" "github.com/robertkrimen/otto/parser" ) type walker struct { nodes []ast.Node } func (w *walker) Enter(node ast.Node) ast.Visitor { w.nodes = append(w.nodes, node) return w } func (w *walker) Exit(node ast.Node) { } func getAllNodes(node ast.Node) []ast.Node { w := &walker{} ast.Walk(w, node) return w.nodes } func getCalls(node ast.Node, name string) []*ast.CallExpression { nodes := []*ast.CallExpression{} for _, n := range getAllNodes(node) { if ce, ok := n.(*ast.CallExpression); ok { var calleeName string switch callee := ce.Callee.(type) { case *ast.Identifier: calleeName = callee.Name case *ast.DotExpression: calleeName = callee.Identifier.Name default: continue } if calleeName == name { nodes = append(nodes, ce) } } } return nodes } func getPropertyValue(node ast.Node, key string) ast.Expression { for _, p := range node.(*ast.ObjectLiteral).Value { if p.Key == key { return p.Value } } return nil } type operation struct { startLine int startColumn int endLine int endColumn int text []string } type check struct { before []string operations []operation after []string } type test struct { description string checks []check } func stringSliceToGoSource(slice []string) string { var b strings.Builder b.WriteString("[]string{\n") for _, s := range slice { b.WriteString(fmt.Sprintf("%#v,\n", s)) } b.WriteString("}") return b.String() } func testToGoTest(test test, name string) string { var b strings.Builder b.WriteString("func Test") b.WriteString(name) b.WriteString("(t *testing.T) {\n") for _, c := range test.checks { b.WriteString("check(\n") b.WriteString("t,\n") b.WriteString(fmt.Sprintf("%v,\n", stringSliceToGoSource(c.before))) b.WriteString("[]operation{\n") for _, op := range c.operations { b.WriteString("operation{\n") b.WriteString(fmt.Sprintf("start: Loc{%v, %v},\n", op.startColumn, op.startLine)) b.WriteString(fmt.Sprintf("end: Loc{%v, %v},\n", op.endColumn, op.endLine)) b.WriteString(fmt.Sprintf("text: %v,\n", stringSliceToGoSource(op.text))) b.WriteString("},\n") } b.WriteString("},\n") b.WriteString(fmt.Sprintf("%v,\n", stringSliceToGoSource(c.after))) b.WriteString(")\n") } b.WriteString("}\n") return b.String() } func nodeToStringSlice(node ast.Node) []string { var result []string for _, s := range node.(*ast.ArrayLiteral).Value { result = append(result, s.(*ast.StringLiteral).Value) } return result } func nodeToStringSlice2(node ast.Node) []string { var result []string for _, o := range node.(*ast.ArrayLiteral).Value { result = append(result, getPropertyValue(o, "text").(*ast.StringLiteral).Value) } return result } func nodeToInt(node ast.Node) int { return int(node.(*ast.NumberLiteral).Value.(int64)) } func getChecks(node ast.Node) []check { checks := []check{} for _, ce := range getCalls(node, "testApplyEdits") { if len(ce.ArgumentList) != 3 { // Wrong function continue } before := nodeToStringSlice2(ce.ArgumentList[0]) after := nodeToStringSlice2(ce.ArgumentList[2]) var operations []operation for _, op := range ce.ArgumentList[1].(*ast.ArrayLiteral).Value { args := getPropertyValue(op, "range").(*ast.NewExpression).ArgumentList operations = append(operations, operation{ startLine: nodeToInt(args[0]) - 1, startColumn: nodeToInt(args[1]) - 1, endLine: nodeToInt(args[2]) - 1, endColumn: nodeToInt(args[3]) - 1, text: []string{getPropertyValue(op, "text").(*ast.StringLiteral).Value}, }) } checks = append(checks, check{before, operations, after}) } for _, ce := range getCalls(node, "testApplyEditsWithSyncedModels") { if len(ce.ArgumentList) > 3 && ce.ArgumentList[3].(*ast.BooleanLiteral).Value { // inputEditsAreInvalid == true continue } before := nodeToStringSlice(ce.ArgumentList[0]) after := nodeToStringSlice(ce.ArgumentList[2]) var operations []operation for _, op := range getCalls(ce.ArgumentList[1], "editOp") { operations = append(operations, operation{ startLine: nodeToInt(op.ArgumentList[0]) - 1, startColumn: nodeToInt(op.ArgumentList[1]) - 1, endLine: nodeToInt(op.ArgumentList[2]) - 1, endColumn: nodeToInt(op.ArgumentList[3]) - 1, text: nodeToStringSlice(op.ArgumentList[4]), }) } checks = append(checks, check{before, operations, after}) } return checks } func getTests(node ast.Node) []test { tests := []test{} for _, ce := range getCalls(node, "test") { description := ce.ArgumentList[0].(*ast.StringLiteral).Value body := ce.ArgumentList[1].(*ast.FunctionLiteral).Body checks := getChecks(body) if len(checks) > 0 { tests = append(tests, test{description, checks}) } } return tests } func main() { var tests []test for _, filename := range os.Args[1:] { source, err := ioutil.ReadFile(filename) if err != nil { log.Fatalln(err) } program, err := parser.ParseFile(nil, "", source, parser.IgnoreRegExpErrors) if err != nil { log.Fatalln(err) } tests = append(tests, getTests(program)...) } if len(tests) == 0 { log.Fatalln("no tests found!") } fmt.Println("// This file is generated from VSCode model tests by the testgen tool.") fmt.Println("// DO NOT EDIT THIS FILE BY HAND; your changes will be overwritten!\n") fmt.Println("package buffer") fmt.Println(`import "testing"`) re := regexp.MustCompile(`[^\w]`) usedNames := map[string]bool{} for _, test := range tests { name := strings.Title(strings.ToLower(test.description)) name = re.ReplaceAllLiteralString(name, "") if name == "" { name = "Unnamed" } if usedNames[name] { for i := 2; ; i++ { newName := fmt.Sprintf("%v_%v", name, i) if !usedNames[newName] { name = newName break } } } usedNames[name] = true fmt.Println(testToGoTest(test, name)) } } micro-2.0.14/tools/update-nightly-tag.sh0000775000175000017510000000050614663411671017460 0ustar nileshnileshcommitID=$(git rev-parse --short HEAD) echo "Moving tag" hub push origin :refs/tags/nightly git tag -f nightly $commitID hub push --tags MESSAGE=$'Nightly build\n\nAutogenerated nightly build of micro' echo "Creating new release" hub release create nightly \ --prerelease \ --draft=false \ --message "$MESSAGE." micro-2.0.14/tools/vendor-src.sh0000775000175000017510000000014214663411671016027 0ustar nileshnileshcd ../.. tar czf "$1".tar.gz micro zip -rq "$1".zip micro mv "$1".tar.gz micro mv "$1".zip micro