diff --git a/scripts/emacs/phafi-autoload.el b/scripts/emacs/phafi-autoload.el index fbd9b400eb7d365b2148e09ef65d35b30356d0e2..a1ea597ca7e8f32cd19356585a6cc2bf206f5c78 100644 --- a/scripts/emacs/phafi-autoload.el +++ b/scripts/emacs/phafi-autoload.el @@ -36,6 +36,9 @@ (phafi-autoload-load "popup-0.5") (phafi-autoload-load "auto-complete-1.4") (phafi-autoload-load "web-mode-master") +(phafi-autoload-load "yasnippet") +(phafi-autoload-load "php-auto-yasnippets-2.2.1") + (require 'php-mode) (add-to-list 'auto-mode-alist '("\\.php[34]?\\'\\|\\.phtml\\'" . php-mode)) @@ -58,13 +61,28 @@ (require 'phafi-mode) + (defun phafi-autoload-php-mode() (autoload 'phafi-mode "phafi-mode" "PHAFI mode") - (phafi-mode)) + (phafi-mode) + (local-set-key (kbd "C-c C-y") 'yas/create-php-snippet) + ) (add-hook 'php-mode-hook 'phafi-autoload-php-mode t) +(require 'yasnippet) +(add-to-list 'yas-snippet-dirs + (concat (file-name-directory (symbol-file 'phafi-php-mode)) "yasnippet/snippets/text-mode/") + ) +(yas-global-mode 1) + +(require 'php-auto-yasnippets) +(setq php-auto-yasnippet-php-program + (concat (file-name-directory (symbol-file 'phafi-php-mode)) + "php-auto-yasnippets-2.2.1" + "/Create-PHP-YASnippet.php")) + (require 'web-mode) (add-to-list 'auto-mode-alist '("\\.phtml\\'" . web-mode)) diff --git a/scripts/emacs/phafi-mode.el b/scripts/emacs/phafi-mode.el index 18aa36d555c142cfa65d3bd6bb27c33f04b4ffe2..2f19b76676e45a7ff24c23154b6c50d9b417ec2d 100644 --- a/scripts/emacs/phafi-mode.el +++ b/scripts/emacs/phafi-mode.el @@ -77,7 +77,7 @@ (gtags-mode t) (setq ag-highlight-search t) (setq ag-arguments(list "--smart-case" "--nogroup" "--column" "--ignore phonetix.txt" "--ignore TAGS" "--ignore-dir report" "--ignore coverage.xml" "--ignore-dir emacs" "--ignore-dir fichiers" "--ignore-dir jQuery" "--ignore-dir ckeditor" "--ignore jquery.mobile-1.1.0.min.js" "--ignore jquery.mobile-1.2.0.min.js" "--" )) - (setq ac-sources '(ac-source-gtags ac-source-words-in-buffer)) + (setq ac-sources '(ac-source-gtags ac-source-words-in-buffer ac-source-php-auto-yasnippets)) (imenu-add-menubar-index) (phafi-init-menu) (phafi-enable-autotest) diff --git a/scripts/emacs/php-auto-yasnippets-2.2.1/Create-PHP-YASnippet.php b/scripts/emacs/php-auto-yasnippets-2.2.1/Create-PHP-YASnippet.php new file mode 100644 index 0000000000000000000000000000000000000000..797e4011839c47dc091734a42a9ad28641a3bf80 --- /dev/null +++ b/scripts/emacs/php-auto-yasnippets-2.2.1/Create-PHP-YASnippet.php @@ -0,0 +1,266 @@ +<?php + +/********************************************************************* + * + * This program accepts the name of a standard library PHP function or + * method as a command-line argument and returns a 'snippet' + * representing that function and its parameters for use with the + * php-auto-yasnippets Emacs package: + * + * https://github.com/ejmr/php-auto-yasnippets + * + * If the first command-line argument is a method then the program + * must also receive a second argument: the name of the class that + * implements that method. + * + * Copyright 2013 Eric James Michael Ritz + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + ********************************************************************/ + +/* The error codes we use. These follow the common practice of using + * zero for success and various non-zero values for errors. However, + * we do not create any output for errors, such as error messages. + * Because the output of this program is intended to appear in an + * Emacs buffer we do not want to clutter that buffer with things like + * error messages. If the program cannot produce useful output then + * it exits silently with an error code. + */ +define("SUCCESS", 0); +define("ERROR_NOT_CLI", 1); +define("ERROR_MISSING_ARGUMENT", 2); +define("ERROR_UNKNOWN_FUNCTION", 3); +define("ERROR_UNKNOWN_METHOD", 4); +define("ERROR_REQUIRED_FILE_MISSING", 5); + +/* We only want to be able to run this from the command-line. It + * should be fine to run as part of another SAPI as well, but honestly + * who knows. Better to lock this up as tightly as possible than to + * find out later that it creates an obscure security hole. + */ +if (PHP_SAPI !== "cli" || isset($_SERVER["SERVER_NAME"])) +{ + exit(ERROR_NOT_CLI); +} + +$args = array(); +$required_files = array(); +$next_arg_is_required_file = false; +foreach ($argv as $arg) +{ + if ($next_arg_is_required_file) + { + $required_files[] = $arg; + $next_arg_is_required_file = false; + } + elseif ($arg === '--require-once') + { + $next_arg_is_required_file = true; + } + else + { + $args[] = $arg; + } +} + + +/* We have the right number of arguments? We should have two at a + * minimum: the name of the program itself, and the function name. + */ +if (count($args) < 2) +{ + exit(ERROR_MISSING_ARGUMENT); +} + +foreach ($required_files as $file) +{ + if (! is_file($file)) { + exit(ERROR_REQUIRED_FILE_MISSING); + } + + require_once $file; +} + +/* If we get to here then we have a name on the command-line. It may + * not actually be a proper function name though. So when we create + * the ReflectionFunction object we need to check for the exception it + * may throw if the function is unrecognized. + */ +$function_name = (string) $args[1]; + +try +{ + $function = new ReflectionFunction($function_name); +} +catch (ReflectionException $error) +{ + /* Creating a ReflectionFunction object will fail if + * $function_name represents the name of a class method. If + * that is the case then the program should have received a + * second argument, the class implementing that method. + * + * If we have another command-line argument we treat that + * as a class name and try to get reflection data for a second + * time by treating $function_name as a method. + */ + if ($argc >= 3) + { + try + { + $class_name = (string) $args[2]; + $function = new ReflectionMethod($class_name, $function_name); + } + catch (ReflectionException $error) + { + exit(ERROR_UNKNOWN_METHOD); + } + } + /* Without a class name to use we cannot get any method + * information, so if we get to this point then we have to + * assume we just do not know the function. + */ + else + { + exit(ERROR_UNKNOWN_FUNCTION); + } +} + +/* Snippets can have 'directives', documented here: + * + * http://capitaomorte.github.com/yasnippet/snippet-development.html + * + * We need to create two directives: '#key' and '#name'. They tell + * YASnippet what to look for to trigger the expansion of the snippet + * (#key) and what to show in the menu of available snippets (#name). + * The name of the function suffices for both of these. + * + * Finally we put all of the directives together into a single string + * that we will attach to the rest of the output later. + */ +$snippet_directives = array(); +$snippet_directives[] = "#key: $function_name"; +$snippet_directives[] = "#name: $function_name"; + +/* If possible we also add the '#group' directive. This directive + * will help Emacs organize the snippets into sub-menus, making it + * easier for the user to navigate once he starts creating a large + * number of snippets with this program. PHP groups many functions + * into 'extensions', so we use the extension name for the group name. + * Thus a function like json_encode() will get the directive '#group: + * json'. If $function actually represents a method then we also try + * to add the name of the class to the group, creating a sub-group + * using that class name. + * + * However, not all functions and methods belong to an extension. For + * methods we still use the class name for the group in the absence of + * an extension name. But for functions we omit the '#group' + * directive if there is no extension name. + */ +$group_name_pieces = array(); + +if ($function->getExtensionName()) +{ + $group_name_pieces[] = $function->getExtensionName(); +} + +if ($function instanceof ReflectionMethod) +{ + $class_name = $function->getDeclaringClass()->getName(); + + /* If the class name belongs to a namespace then we create + * further sub-groups to reflect that. + */ + str_replace("\\", ".", $class_name); + $group_name_pieces[] = $class_name; +} + +if (count($group_name_pieces) > 0) +{ + $snippet_directives[] = sprintf( + "#group: %s", + implode(".", $group_name_pieces) + ); +} + +/* We assume the name of the function is already in the buffer and + * that Emacs will append any output to that. So we create an array + * of strings, each representing a parameter for the function, and + * then combine them in the end to create our output. The program + * creates an entire snippet definition, including required metadata, + * but the strings in $snippet_chunks make up the text that will + * actually appear in Emacs, i.e. $snippet_chunks is the function or + * method expansion that we show the user. + */ +$snippet_chunks = array(); + +foreach ($function->getParameters() as $parameter) +{ + $type_hint = null; + + if ($parameter->isArray()) + { + $type_hint = "array "; + } + else if ($parameter->getClass()) + { + $type_hint = $parameter->getClass()->getName() . " "; + } + + $name = (string) $type_hint . "$" . $parameter->getName(); + + if ($parameter->isPassedByReference()) + { + $name = "&" . $name; + } + + /* If a parameter is optional then we wrap it in square braces + * to follow the same convention as the PHP manual for + * denoting optional parameters. But first we check to see if + * the parameter has a default value, which may be possible + * since it is optional. + */ + if ($parameter->isOptional()) + { + if ($parameter->isDefaultValueAvailable()) + { + $name = $name . " = " . (string) $parameter->getDefaultValue(); + } + else if ($parameter->allowsNull()) + { + $name = $name . " = null"; + } + + $name = "[$name]"; + } + + $snippet_chunks[] = sprintf( + '${%d:%s,}', + // We must add one to the position because PHP starts + // from zero, but for the snippet we want parameter + // numbering to start from one. + $parameter->getPosition() + 1, + $name + ); +} + +/* Now that we have built all the pieces of the snippet we can combine + * them, wrap the parameter chunks in parentheses, and be done. + */ +printf("%s\n# --\n%s(%s)", + implode("\n", $snippet_directives), + $function_name, + implode(" ", $snippet_chunks)); + +exit(SUCCESS); diff --git a/scripts/emacs/php-auto-yasnippets-2.2.1/LICENSE b/scripts/emacs/php-auto-yasnippets-2.2.1/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..94a9ed024d3859793618152ea559a168bbcbb5e2 --- /dev/null +++ b/scripts/emacs/php-auto-yasnippets-2.2.1/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + <program> Copyright (C) <year> <name of author> + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +<http://www.gnu.org/licenses/>. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +<http://www.gnu.org/philosophy/why-not-lgpl.html>. diff --git a/scripts/emacs/php-auto-yasnippets-2.2.1/README.md b/scripts/emacs/php-auto-yasnippets-2.2.1/README.md new file mode 100644 index 0000000000000000000000000000000000000000..d7c873ce3a4daf0ee6bb41eb3148939cd139b855 --- /dev/null +++ b/scripts/emacs/php-auto-yasnippets-2.2.1/README.md @@ -0,0 +1,172 @@ +Automatic YASnippets for PHP in Emacs +===================================== + +The php-auto-yasnippets package for [GNU Emacs][emacs] will +automatically create ‘snippets’ for [standard PHP functions][php]. It +defines these snippets using the [YASnippet package][yas], with optional +support for [auto-complete][auto-complete]. For +example, if you type the PHP function + +```php +implode +``` + +and press `C-c C-y` then php-auto-yasnippets will expand that into + +```php +implode($glue, $pieces) +``` + +with the cursor ready to overwrite `$glue` with the string you want to +use. Pressing Tab will skip over to `$pieces` to do the +same. This way you can be sure you not only have the correct number +of arguments, but also that you have them in the correct order. PHP +comes with a large standard library and functions that sound similar +sometimes require arguments in contrasting orders. This package will +help you avoid having to remember those corner cases. + +If a function has any optional parameters then php-auto-yasnippets +will wrap them in square braces. This is the same convention the PHP +manual uses to indicate optional parameters. For example, +php-auto-yasnippets will expand `filter_input` into this: + +```php +filter_input($type, $variable_name, [$filter], [$options]) +``` + +If you do not need the optional parameters you can delete them by +pressing `C-d` when you Tab over to them. + +You can use the prefix command to expand method names. When you use +the prefix, u.g. `C-u C-c C-y`, the package will ask you for the name +of the class which implements that method. This information is +necessary in order to generate the correct snippet. + +Requirements +------------ + +* PHP 5.3 or later +* [YASnippet][yas] +* [php-mode][php-mode] + +Installation +------------ + +To use php-auto-yasnippets you need to do three things. First, place +the package in your load-path (`C-h v load-path` for help) and load it +from your Emacs configuration file by adding: + +```lisp +(require 'php-auto-yasnippets) +``` + +Second, make sure the variable `php-auto-yasnippet-php-program` points +to the program `Create-PHP-YASnippet.php`. That PHP program should +have come with this package; if you do not have it then you can get it +from the [project website][home]. By default php-auto-yasnippets will +search for the PHP program in the same directory as the Elisp code, +i.e. the `php-auto-yasnippets.el`. If you want to put the PHP program +in another place then use `setq` in your configuration file to set the +variable to the proper path, e.g: + +```lisp +(require 'php-auto-yasnippets) +(setq php-auto-yasnippet-php-program "~/path/to/Create-PHP-YASnippet.php") +``` + +Finally, bind the function `yas/create-php-snippet` to a key of your +choice. You *must* do this because php-auto-yasnippets defines no +key-bindings. And since the package requires php-mode, and is most +useful when writing PHP code, you may want to use a key-binding that +only works when using php-mode. For example: + +```lisp +(define-key php-mode-map (kbd "C-c C-y") 'yas/create-php-snippet) +``` + +Now if you type the name of a PHP function and press `C-c C-y` it will +expand into a snippet containing all of the parameters, their names, +any default values, et cetera. + + +auto-complete +------------- + +With auto-complete support activated, it's even simpler: + +```php +imp +``` + +followed by `Tab`, then `Return` (to choose the first completion) expands the +snippet to + +```php +implode($glue, $pieces) +``` + +First, install and configure [auto-complete][auto-complete]. + +Add + +```lisp +(payas/ac-setup) +``` + +to your php-auto-yasnippets setup, and you should be good to go. + +Note that auto-completion does not support user-defined functions or +classes (it relies on auto-complete's php-mode dictionary of functions). + + +Loading User Code +----------------- + +This package can also generate snippets for user-defined functions and methods. + +You can tell a buffer what files to load for function and class definitions by +setting `php-auto-yasnippet-required-files` to a list of required paths. + +If you use [Composer](http://getcomposer.org/), you might put something like +this in `~/project/.dir-locals.el`: + +```lisp +(php-mode . ((php-auto-yasnippet-required-files (list "~/project/vendor/autoload.php")))) +``` + +Now you can generate snippets for any classes Composer autoloads, in any PHP +file in the project. + + +Contributors +------------ + +* [Glynn Forrest](http://glynnforrest.com) +* [Yasuyuki Oka](http://yasuyk.github.io/) +* [Steve Purcell](http://www.sanityinc.com/) +* [Nate Eagleson](http://www.nateeag.com/) + + +Miscellaneous +------------- + +This package uses [Semantic Versioning][semver]. + + +License +------- + +[GNU General Public License][gpl] + +Copyright 2013, 2014 Eric James Michael Ritz + + + +[emacs]: http://www.gnu.org/software/emacs/ +[php]: http://php.net/ +[yas]: https://github.com/capitaomorte/yasnippet +[auto-complete]: https://github.com/auto-complete/auto-complete +[php-mode]: https://github.com/ejmr/php-mode +[gpl]: http://www.gnu.org/copyleft/gpl.html +[home]: https://github.com/ejmr/php-auto-yasnippets +[semver]: http://semver.org/ diff --git a/scripts/emacs/php-auto-yasnippets-2.2.1/php-auto-yasnippets.el b/scripts/emacs/php-auto-yasnippets-2.2.1/php-auto-yasnippets.el new file mode 100644 index 0000000000000000000000000000000000000000..a564fd00e39f5b5dee8e504a968c9acbfc4b5727 --- /dev/null +++ b/scripts/emacs/php-auto-yasnippets-2.2.1/php-auto-yasnippets.el @@ -0,0 +1,309 @@ +;;; php-auto-yasnippets.el --- Creates snippets for PHP functions +;; +;; Copyright 2013, 2014 Eric James Michael Ritz +;; +;; Author: Eric James Michael Ritz +;; URL: https://github.com/ejmr/php-auto-yasnippets +;; Version: 2.2.1 +;; Package-Requires: ((php-mode "1.11") (yasnippet "0.8.0")) +;; +;; +;; +;;; License: +;; +;; This file is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published +;; by the Free Software Foundation; either version 3 of the License, +;; or (at your option) any later version. +;; +;; This file is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this file; if not, write to the Free Software +;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +;; 02110-1301, USA. +;; +;; +;; +;;; Commentary: +;; +;; This package provides a function that automatically creates +;; snippets for PHP standard library functions, for use with the +;; YASnippets package, available at: +;; +;; https://github.com/capitaomorte/yasnippet +;; +;; This package also requires php-mode, available at: +;; +;; https://github.com/ejmr/php-mode +;; +;; To use php-auto-yasnippets you need to do three things. First, +;; place the package in your load-path (`C-h v load-path' for help) +;; and load it from your Emacs configuration file by adding: +;; +;; (require 'php-auto-yasnippets) +;; +;; Second, make sure the variable `php-auto-yasnippet-php-program' +;; points to the program `Create-PHP-YASnippet.php'. That PHP program +;; should have come with this package; if you do not have it then you +;; can get it from the project GitHub URL at the top of this file. By +;; default this package looks for the PHP program in the same +;; directory as this Elisp file. You can use `setq' in your +;; configuration file to set the variable to the proper path if the +;; PHP program is in a different directory, e.g: +;; +;; (require 'php-auto-yasnippets) +;; (setq php-auto-yasnippet-php-program "~/path/to/Create-PHP-YASnippet.php") +;; +;; Finally, bind the function `yas/create-php-snippet' to a key of +;; your choice. Since this package requires php-mode, and since it is +;; most useful when writing PHP code, you may want to use a +;; key-binding that only works when using php-mode. For example: +;; +;; (define-key php-mode-map (kbd "C-c C-y") 'yas/create-php-snippet) +;; +;; Now if you type the name of a PHP function and press `C-c C-y' it +;; will expand into a snippet containing all of the parameters, their +;; names, any default values, et cetera. If you type the name of a +;; method then you need to tell the package the name of the class that +;; implements that method, otherwise it will not be able to create the +;; snippet. Using the prefix command, e.g. `C-u C-c C-y', prompts for +;; the class name in the minibuffer. + + + +;;; Code: + +(require 'thingatpt) +(require 'php-mode) +(require 'yasnippet) + + +;;; This section defines constants the package uses as well as any +;;; global variables which the user may wish to change. + +(defconst php-auto-yasnippet-version "2.2.1" + "The version number for the php-auto-yasnippet package.") + +(defvar php-auto-yasnippet-php-program + (let ((current (or load-file-name (buffer-file-name)))) + (expand-file-name "Create-PHP-YASnippet.php" (file-name-directory current))) + "The path to the program `Create-PHP-YASnippet.php'.") + +;;; We use this variable to restrict the effects of the +;;; payas/remove-extra-whitespace function. YASnippets lets us assign +;;; a hook to run after the expansion of each snippet. We use that +;;; payas/remove-extra-whitespace hook to get rid of extra whitespace +;;; from the PHP snippets this package creates. +;;; +;;; However, the problem is we install payas/remove-extra-whitespace +;;; as a hook for YASnippets to execute after expanding *any* snippet. +;;; That means our function runs for snippets that have absolutely +;;; nothing to do with this package or PHP. This is undesirable +;;; because our hook could mess up the behavior of other snippets by +;;; erroneously 'cleaning up' their whitespace. +;;; +;;; Ideally we only want YASnippets to run our hook if we are +;;; expanding a snippet created by this package. To achieve this we +;;; use this php-auto-yasnippet-executing variable. We set the +;;; variable to true inside of the primary public API function: +;;; yas/create-php-snippet, i.e. when the user generates a snippet +;;; with this package. Then the payas/remove-extra-whitespace hook +;;; will test for this variable; if it has a true value, meaning we +;;; just ran yas/create-php-snippet, then the hook will perform its +;;; clean-up and then set the variable back to a nil value so that our +;;; hook only takes effect once after each call to +;;; yas/create-php-snippet. +;;; +;;; The ultimate effect is that payas/remove-extra-whitespace only +;;; affects snippets expanding via yas/create-php-snippet, limiting +;;; the hook's behavior to snippets this package creates. +(defvar php-auto-yasnippet-executing nil + "Non-nil means `yas/create-php-snippet' is now working.") + +(defvar php-auto-yasnippet-required-files nil + "List of files on disk to include when creating a PHP snippet. +This makes it possible to generate snippets for user code. +It's probably best to set this per-project via .dir-locals.") + +(eval-after-load 'auto-complete + '(progn + (ac-define-source php-auto-yasnippets + '((depends yasnippet) + ;; TODO The php-mode dictionary contains a few things (keywords and + ;; the like) that should not be included + (candidates . (ac-mode-dictionary 'php-mode)) + (action . payas/ac-insert-func-and-create-snippet) + + ;; Since these trigger yasnippet, use the yasnippet face. + (candidate-face . ac-yasnippet-candidate-face) + (selection-face . ac-yasnippet-selection-face) + + ;; The 'p' suffix on auto-complete entries stands for 'PHP', and helps + ;; distinguish them from regular yasnippet entries. + (symbol . "p"))))) + +(defun payas/ac-insert-func-and-create-snippet () + "Insert the selected function name then create its auto-snippet." + (ac-expand) + (yas/create-php-snippet nil)) + +(defun payas/ac-setup () + "Add ac-source-php-auto-yasnippets to ac-sources." + (interactive) + (add-to-list 'ac-sources 'ac-source-php-auto-yasnippets)) + + +;;; Below are all of the internal functions. All of these functions +;;; begin with the 'payas' prefix in their name, short for 'PHP Auto +;;; YASnippets'. No code outside of this file should rely on any of +;;; these functions. + +(defun payas/create-template (input) + "Creates a snippet for INPUT string in the current buffer. + +INPUT should be either the name of a PHP function, or the name of +a PHP method followed by the name of the class implementing it, +separated by a space. For example, \"json_decode\" for a +function or \"push SplQueue\" for a method and class. + +Because this function sends output to the current buffer always +wrap `with-temp-buffer' around calls to it, because the output +this function creates should go directly to the function +`yas--parse-template', and it expects the template definition to +be in the current buffer. + +This function runs `php-auto-yasnippet-php-program' to generate +the snippet. The return value is the exit code of that program." + (save-match-data + (let* ((input-chunks (split-string input)) + (function-or-method-name (first input-chunks)) + (class-name (or (second input-chunks) "")) + (args (list php-executable nil (current-buffer) nil php-auto-yasnippet-php-program))) + + (setq command-args (list function-or-method-name class-name)) + (dolist (elt php-auto-yasnippet-required-files command-args) + (setq command-args (cons elt command-args)) + (setq command-args (cons "--require-once" command-args))) + + (setq args (append args command-args)) + (apply 'call-process args)))) + +(defun payas/report-error (error-code &optional user-input) + "Reports an error based on the given ERROR-CODE. + +The ERROR-CODE is an integer representing the exit status of the +program `php-auto-yasnippet-php-program'. That program exits +with zero for success and non-zero for any errors. This function +shows an error message based on the possible exit codes that +program may return. See the commentary in that program for a +description of possible ERROR-CODE values and their meaning. + +The optional value USER-INPUT, if provided, must be the string +given to `payas/create-template' that caused the PHP program to +return ERROR-CODE. + +If there is nothing to do for the ERROR-CODE then the function +returns nil. However, the function may not return at all if it +signals an error." + (cond ((= error-code 1) + (error "Cannot run the program %s" php-auto-yasnippet-php-program)) + ((= error-code 2) + (error "No function name given to %s" php-auto-yasnippet-php-program)) + ;; We get this error code when the PHP program exits with the + ;; value ERROR_UNKNOWN_FUNCTION. That means the user tried to + ;; create a snippet for a function the program does not + ;; recognize. So arguably we should report this via user-error + ;; since we could say the fault is on the user. However, if we + ;; do that then we are making the assumption that + ;; php-auto-yasnippets made no mistake in selecting the function + ;; name from the buffer. It is possible that the function is not + ;; recognized because we screwed up and did not send the + ;; complete function name. So until we are completely confident + ;; about that aspect of the code we will treat this as an error + ;; on our part and not as a mistake by the user. + ((= error-code 3) + (error "%s is not a recognized PHP function" user-input)) + ;; If we get this error code, ERROR_UNKNOWN_METHOD, then we + ;; can reformat user-input to use PHP's notation for a + ;; better-looking error message. + ((= error-code 4) + (let* ((input-chunks (split-string user-input)) + (method-name (concat (second input-chunks) + "::" + (first input-chunks)))) + (error "%s is not a recognized PHP method" method-name))) + (t nil))) + +(defun payas/define-template (input) + "Create a snippet for INPUT. + +The INPUT must be the name of an available PHP function. This +function creates a snippet for that function and associates it +with `php-mode'." + (unless (gethash 'php-mode yas--tables) + (yas--table-get-create 'php-mode)) + (unless (yas--get-template-by-uuid 'php-mode input) + (with-temp-buffer + (let ((exit-code (payas/create-template input))) + (if (/= exit-code 0) + (payas/report-error exit-code input)) + (yas-define-snippets + 'php-mode + (list (yas--parse-template))))))) + +(defun payas/remove-extra-whitespace () + "Remove whitespace before a function's closing parenthesis. + +After expanding a snippet the user can press `C-d' to the +parameter at the point. This is most useful for optional +parameters in PHP functions. But this behavior leaves too much +whitespace for each parameter the user deletes. This function +cleans up that whitespace so that the PHP code looks better." + ;; After we're done with a snippet we move in front of the closing + ;; bracket and remove any whitespace between here and the final + ;; parameter. If a trailing comma is left it is also deleted. + (when php-auto-yasnippet-executing + (save-excursion + (backward-char 1) + (delete-horizontal-space) + (backward-char 1) + (if (looking-at-p ",") + (delete-char 1))) + (setq php-auto-yasnippet-executing nil))) + + +;;; This section contains the public API. + +;;;###autoload +(defun yas/initialize () + "Setup yasnippet hook for php-auto-yasnippet." + (add-hook 'yas-after-exit-snippet-hook #'payas/remove-extra-whitespace)) + +;;;###autoload +(eval-after-load 'yasnippet '(yas/initialize)) + +;;;###autoload +(defun yas/create-php-snippet (prefix) + "Creates and expands a snippet for the PHP function at point. + +If called with the universal prefix then it prompts the user for +the name of a PHP class and treats the name at point as the name +of a method for that class." + (interactive "P") + (let ((function (thing-at-point 'symbol)) + (class + (if prefix + (read-from-minibuffer "Class: ")))) + (if class + (payas/define-template (concat function " " class)) + (payas/define-template function)) + (setq php-auto-yasnippet-executing t) + (yas-expand))) + +(provide 'php-auto-yasnippets) + +;;; php-auto-yasnippets.el ends here diff --git a/scripts/emacs/yasnippet/.gitignore b/scripts/emacs/yasnippet/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..4926904bd1518c92df20c808d0b5cd8131de91da --- /dev/null +++ b/scripts/emacs/yasnippet/.gitignore @@ -0,0 +1,11 @@ +authors.txt +doc/gh-pages +doc/*.html +doc/html-revision +pkg/ +extras/imported/** +!extras/imported/*/.yas-setup.el +.yas-compiled-snippets.el +*.elc +ert-x.* +ert.* diff --git a/scripts/emacs/yasnippet/.gitmodules b/scripts/emacs/yasnippet/.gitmodules new file mode 100644 index 0000000000000000000000000000000000000000..607033b6b17ab4eb093496247a681a976c373f5c --- /dev/null +++ b/scripts/emacs/yasnippet/.gitmodules @@ -0,0 +1,6 @@ +[submodule "snippets"] + path = snippets + url = https://github.com/AndreaCrotti/yasnippet-snippets.git +[submodule "yasmate"] + path = yasmate + url = https://github.com/capitaomorte/yasmate.git diff --git a/scripts/emacs/yasnippet/.travis.yml b/scripts/emacs/yasnippet/.travis.yml new file mode 100644 index 0000000000000000000000000000000000000000..83990f6d1fe3921779468b2b3c2f47280d5d4dec --- /dev/null +++ b/scripts/emacs/yasnippet/.travis.yml @@ -0,0 +1,23 @@ +language: emacs +before_install: + - echo -e "\ndeb http://us.archive.ubuntu.com/ubuntu raring universe multiverse main" | sudo tee -a /etc/apt/sources.list + - echo -e "\ndeb http://emacs.naquadah.org/ stable/" | sudo tee -a /etc/apt/sources.list + - echo -e "\ndeb-src http://emacs.naquadah.org/ stable/" | sudo tee -a /etc/apt/sources.list + - cat /etc/apt/sources.list + - wget -q -O - http://emacs.naquadah.org/key.gpg | sudo apt-key add - + - sudo apt-get update +install: + - sudo apt-get install emacs + - sudo apt-get -t raring install libgnutls26 + - sudo apt-get install emacs-snapshot-nox + - curl -O https://raw.github.com/ohler/ert/c619b56c5bc6a866e33787489545b87d79973205/lisp/emacs-lisp/ert.el + - curl -O https://raw.github.com/ohler/ert/c619b56c5bc6a866e33787489545b87d79973205/lisp/emacs-lisp/ert-x.el + - curl -o cl-lib.el http://elpa.gnu.org/packages/cl-lib-0.3.el +before_script: +script: + - export EMACS=emacs; rm *.elc; rake compile; rake tests + - rm ert*.el; rm cl-lib.el + - export EMACS=emacs-snapshot; rm *.elc; rake compile; rake tests +notifications: + email: + - joaotavora@gmail.com diff --git a/scripts/emacs/yasnippet/ChangeLog b/scripts/emacs/yasnippet/ChangeLog new file mode 100644 index 0000000000000000000000000000000000000000..c7355338ffef9823c6d521528497130d5f68c138 --- /dev/null +++ b/scripts/emacs/yasnippet/ChangeLog @@ -0,0 +1,28 @@ +2014-01-06 João Távora <joaotavora@gmail.com> + + * yasnippet.el (yas-reload-all): run `yas-after-reload-hook'. + (yas-after-reload-hook): add it. + +2013-12-31 João Távora <joaotavora@gmail.com> + + * yasnippet.el (require): require cl-lib during byte-compilation + and load. + + * yasnippet-tests.el (example-for-issue-404-external-emacs): Use + `yas-with-snippet-dirs' to not depend on bundled snippets. + (yas--call-with-temporary-redefinitions): Use `cl-labels' since no + cl-flet in emacs-24.3's cl-lib.el. + (loading-with-cyclic-parenthood): prog-mode doesn't exist in emacs + 24.3. + (yas-batch-run-tests): remove it. + + * Rakefile: use ert-run-tests-batch-and-exit. + + * README.mdown: Add Travis CI badge. + + * .travis.yml: new file + + +;; Local Variables: +;; coding: utf-8 +;; End: diff --git a/scripts/emacs/yasnippet/README.mdown b/scripts/emacs/yasnippet/README.mdown new file mode 100644 index 0000000000000000000000000000000000000000..4a8d597c0e358750aaeb2f5aa0d79a4e5be2a8da --- /dev/null +++ b/scripts/emacs/yasnippet/README.mdown @@ -0,0 +1,159 @@ +[](https://travis-ci.org/capitaomorte/yasnippet) + +# Intro + +**YASnippet** is a template system for Emacs. It allows you to +type an abbreviation and automatically expand it into function +templates. Bundled language templates include: C, C++, C#, Perl, +Python, Ruby, SQL, LaTeX, HTML, CSS and more. The snippet syntax +is inspired from [TextMate's][textmate-snippets] syntax, you can +even [import](#import) most TextMate templates to +YASnippet. Watch [a demo on YouTube][youtube-demo] or download a +[higher resolution version][high-res-demo]. + +[textmate-snippets]: http://manual.macromates.com/en/snippets +[youtube-demo]: http://www.youtube.com/watch?v=ZCGmZK4V7Sg +[high-res-demo]: http://yasnippet.googlecode.com/files/yas_demo.avi + +# Installation + +## Install the most recent version + +Clone this repository somewhere + + $ cd ~/.emacs.d/plugins + $ git clone --recursive https://github.com/capitaomorte/yasnippet + +Add the following in your `.emacs` file: + + (add-to-list 'load-path + "~/.emacs.d/plugins/yasnippet") + (require 'yasnippet) + (yas-global-mode 1) + +Add your own snippets to `~/.emacs.d/snippets` by placing files there or invoking `yas-new-snippet`. + +## Install with `package-install` + +In a recent emacs `M-x list-packages` is the recommended way to list and install packages. +[MELPA][melpa] keeps a very recent snapshot of YASnippet, see http://melpa.milkbox.net/#installing. + +## Install with el-get + +El-get is a nice way to get the most recent version, too. See +https://github.com/dimitri/el-get for instructions. Be sure to install the +"master" branch since the 3.x series still use the old googlecode code, base. +Consider using this "local" recipe. + + (push '(:name yasnippet + :website "https://github.com/capitaomorte/yasnippet.git" + :description "YASnippet is a template system for Emacs." + :type github + :pkgname "capitaomorte/yasnippet" + :features "yasnippet" + :compile "yasnippet.el") + el-get-sources) + +## Use `yas-minor-mode` on a per-buffer basis + +To use YASnippet as a non-global minor mode, replace `(yas-global-mode 1)` with +`(yas-reload-all)` to load the snippet tables. Then add a call to +`(yas-minor-mode)` to the major-modes where you to enable YASnippet. + + (add-hook 'prog-mode-hook + '(lambda () + (yas-minor-mode))) + +# Where are the snippets? + +<a name="import"></a> + +Yasnippet no longer bundles snippets directly, but it's very easy to +get some! + +If you git-cloned yasnippet with the `--recursive` option you'll also +download "git submodules" and find two subdirs under the main tree. + +1. `snippets/` + + Points to [yasnippet-snippets][yasnippet-snippets] the snippet + collection of [AndreaCrotti](https://github.com/AndreaCrotti). + + The default configuraiton already points to this dir, so to use + them, just make sure the submodule really was downloaded + (i.e. there are some files under `snippets/`) + +2. `yasmate/` + + Points to a github repo of the [yasmate][yasmate] tool, which is + dedicated to converting textmate bundles into yasnippet snippets. + + To use these snippets you have to run the tool first, so + [see its doc][yasmate]), and then point the `yas-snippet-dirs` + variable to the `.../yasmate/snippets` subdir. + + If you have a working ruby environment, you can probably get lucky + directly with `rake convert-bundles`. + +Naturally, you can point `yas-snippet-dirs` to good snippet collections out +there. If you have created snippets for a mode, or multiple modes, +consider creating a repository to host them, then tell users that it +should be added like this to `yas-snippet-dirs`: + + (setq yas-snippet-dirs + '("~/.emacs.d/snippets" ;; personal snippets + "/path/to/some/collection/" ;; foo-mode and bar-mode snippet collection + "/path/to/yasnippet/yasmate/snippets" ;; the yasmate collection + "/path/to/yasnippet/snippets" ;; the default collection + )) + + (yas-global-mode 1) ;; or M-x yas-reload-all if you've started YASnippet already. + +# Documentation, issues, etc + +Please refer to the comprehensive (albeit slightly outdated) +[documentation][docs] for full customization +and support. If you find a bug, please report it on +[the GitHub issue tracker][issues]. (please **do not** submit new issues to the old +[googlecode tracker][googlecode tracker]) + +## Important note regarding bug reporting +If you think have found a bug, please report it clearly. Yasnippet +does have (lots of) bugs and your reports are very valuable. Here's +a [great example](https://github.com/capitaomorte/yasnippet/issues/318) +of a bug report. It has everything needed for a sucessfull analysis and +speedy resolution: + +*Before* reporting try to reproduce the bug **without** your usual +`.emacs` (or whatever startup file you use). Do so either by starting +emacs from the command line with the `-Q` switch, or by temporarily +moving away your `.emacs` and creating a new smaller one just for +reproducing the bug. Paste that file in your bug report. Paste any sequence +of relevant shell commands before you launch Emacs. + +*Then*, describe steps taken to reproduce from an +end-user perspective. Try to be as unambiguous as possible. + +Also, don't forget to state the Emacs version (use `M-x emacs-version`) and +the yasnippet version you are using (if using the latest from github, +do `git log -1` in the dir). + +Any more info is welcome, but don't just paste a backtrace or an error +message string you got. I'm not saying your analysis might not be +useful but following the instructions above immediately gives me a +clear picture of what is happening. + +There is also a [YASnippet google group][forum]. I will keep the group +open for reference and for discussion among users, unfortunately I +can't guarantee a timely response, so maybe creating a github issue +clearly marking your intent (user support/bug/feature request). + +Finally, thank you very much for using YASnippet! + +[docs]: http://capitaomorte.github.com/yasnippet/ +[issues]: https://github.com/capitaomorte/yasnippet/issues +[googlecode tracker]: http://code.google.com/p/yasnippet/issues/list +[forum]: http://groups.google.com/group/smart-snippet +[melpa]: http://melpa.milkbox.net/ +[yasmate]: http://github.com/capitaomorte/yasmate +[yasnippet-snippets]: http://github.com/AndreaCrotti/yasnippet-snippets diff --git a/scripts/emacs/yasnippet/Rakefile b/scripts/emacs/yasnippet/Rakefile new file mode 100644 index 0000000000000000000000000000000000000000..2965eb9d5ba5a7699cb4a23e34901a26a8b406c4 --- /dev/null +++ b/scripts/emacs/yasnippet/Rakefile @@ -0,0 +1,107 @@ +# -*- Ruby -*- + +require 'fileutils' + +$EMACS = ENV["EMACS"] +if not $EMACS or $EMACS == 't' + $EMACS = "emacs" +end + +def find_version + File.read("yasnippet.el", :encoding => "UTF-8") =~ /;; Package-version: *([0-9.]+?) *$/ + $version = $1 +end +find_version +FileUtils.mkdir_p('pkg') + +desc "run tests in batch mode" +task :tests do + sh "#{$EMACS} -Q -L . -l yasnippet-tests.el" + + " --batch -f ert-run-tests-batch-and-exit" +end + +desc "create a release package" +task :package do + release_dir = "pkg/yasnippet-#{$version}" + FileUtils.mkdir_p(release_dir) + files = ['snippets', 'yasnippet.el'] + FileUtils.cp_r files, release_dir + File.open(File.join(release_dir,'yasnippet-pkg.el'), 'w') do |file| + file.puts <<END +(define-package "yasnippet" + "#{$version}" + "A template system for Emacs") +END + end + sh "git clean -f snippets" + FileUtils.cd 'pkg' do + sh "tar cf yasnippet-#{$version}.tar yasnippet-#{$version}" + end +end + +desc "create a release package and upload it to google code" +task :release => [:package, 'doc:archive'] do + raise "Not implemented for github yet!" +end + +desc "Generate document" +task :doc do + sh "#{$EMACS} -Q -L . --batch -l doc/yas-doc-helper.el" + + " -f yas--generate-html-batch" +end + +namespace :doc do + task :archive do + release_dir = "pkg/yasnippet-#{$version}" + FileUtils.mkdir_p(release_dir) + sh "tar cjf pkg/yasnippet-doc-#{$version}.tar.bz2 " + + "--exclude=doc/.svn --exclude=doc/images/.svn doc/*.html doc/images" + end + + task :upload do + if File.exists? 'doc/gh-pages' + Dir.chdir 'doc/gh-pages' do + sh "git checkout gh-pages" + end + Dir.glob("doc/*.{html,css}").each do |file| + FileUtils.cp file, 'doc/gh-pages' + end + Dir.glob("doc/images/*").each do |file| + FileUtils.cp file, 'doc/gh-pages/images' + end + Dir.glob("doc/stylesheets/*.css").each do |file| + FileUtils.cp file, 'doc/gh-pages/stylesheets' + end + curRev = `git rev-parse --verify HEAD`.chomp() + expRev = IO.read('doc/html-revision').chomp() + if curRev != expRev + raise ("The HTML rev: #{expRev},\n" + + "current rev: #{curRev}!\n") + end + if !system "git diff-index --quiet HEAD" + system "git status --untracked-files=no" + raise "You have uncommitted changes!" + end + Dir.chdir 'doc/gh-pages' do + sh "git commit -a -m 'Automatic documentation update.\n\n" + + "From #{curRev.chomp()}'" + sh "git push" + end + end + end +end + +desc "Compile yasnippet.el into yasnippet.elc" + +rule '.elc' => '.el' do |t| + sh "#{$EMACS} --batch -L . --eval \"(byte-compile-file \\\"#{t.source}\\\")\"" +end +task :compile => FileList["yasnippet.el"].ext('elc') + +task :default => :doc + +desc "use yasmate to convert textmate bundles" +task :convert_bundles do + cd "yasmate" + sh "rake convert_bundles" + end diff --git a/scripts/emacs/yasnippet/doc/faq.org b/scripts/emacs/yasnippet/doc/faq.org new file mode 100644 index 0000000000000000000000000000000000000000..79249e86f52f3a1298fa55bcbc2e372b77c302c2 --- /dev/null +++ b/scripts/emacs/yasnippet/doc/faq.org @@ -0,0 +1,158 @@ +#+SETUPFILE: org-setup.inc + +#+TITLE: Frequently Asked Questions + +* Why is there an extra newline? + +If you have a newline at the end of the snippet definition file, then +YASnippet will add a newline when you expanding a snippet. Please don't +add a newline at the end if you don't want it when you saving the +snippet file. + +Note some editors will automatically add a newline for you. In Emacs, if +you set =require-final-newline= to =t=, it will add the final newline +for you automatically. + +* Why doesn't TAB expand a snippet? + +First check the mode line to see if there's =yas=. If not, then try +=M-x yas-minor-mode= to manually turn on the minor mode and try to +expand the snippet again. If it works, then, you can add the following +code to your =.emacs= /before/ loading YASnippet: + +#+BEGIN_SRC emacs-lisp + (add-hook 'the-major-mode-hook 'yas-minor-mode-on) +#+END_SRC + +where =the-major-mode= is the major mode in which [[sym:yas-minor-mode][=yas-minor-mode=]] isn't +enabled by default. + +From YASnippet 0.6 you can also use the command =M-x yas-global-mode= to +turn on YASnippet automatically for /all/ major modes. + +If [[sym:yas-minor-mode][=yas-minor-mode=]] is on but the snippet still not expanded. Then try +to see what command is bound to the =TAB= key: press =C-h k= and then +press =TAB=. Emacs will show you the result. + +You'll see a buffer prompted by Emacs saying that +=TAB runs the command ...=. Alternatively, you might see +=<tab> runs the command ...=, note the difference between =TAB= and +=<tab>= where the latter has priority. If you see =<tab>= bound to a +command other than [[sym:yas-expand][=yas-expand=]], (e.g. in =org-mode=) you can try the +following code to work around: + +#+BEGIN_SRC emacs-lisp + (add-hook 'org-mode-hook + (let ((original-command (lookup-key org-mode-map [tab]))) + `(lambda () + (setq yas-fallback-behavior + '(apply ,original-command)) + (local-set-key [tab] 'yas-expand)))) +#+END_SRC + +replace =org-mode-hook= and =org-mode-map= with the major mode hook you +are dealing with (Use =C-h m= to see what major mode you are in). + +As an alternative, you can also try + +#+BEGIN_SRC emacs-lisp + (defun yas-advise-indent-function (function-symbol) + (eval `(defadvice ,function-symbol (around yas-try-expand-first activate) + ,(format + "Try to expand a snippet before point, then call `%s' as usual" + function-symbol) + (let ((yas-fallback-behavior nil)) + (unless (and (interactive-p) + (yas-expand)) + ad-do-it))))) + + (yas-advise-indent-function 'ruby-indent-line) +#+END_SRC + +To /advise/ the modes indentation function bound to TAB, (in this case +=ruby-indent-line=) to first try to run [[sym:yas-expand][=yas-expand=]]. + +If the output of =C-h k RET <tab>= tells you that =<tab>= is indeed +bound to [[sym:yas-expand][=yas-expand=]] but YASnippet still doesn't work, check your +configuration and you may also ask for help on the [[http://groups.google.com/group/smart-snippet][discussion group]]. +See this particular [[http://code.google.com/p/yasnippet/issues/detail?id=93&can=1][thread]] for quite some solutions and alternatives. + +Don't forget to attach the information on what command is bound to TAB +as well as the mode information (Can be obtained by =C-h m=). + +* Why doesn't TAB navigation work with flyspell + +A workaround is to inhibit flyspell overlays while the snippet is +active: + +#+BEGIN_SRC emacs-lisp + (add-hook 'flyspell-incorrect-hook + #'(lambda (dummy1 dummy2 dymmy3) + (and yas-active-field-overlay + (overlay-buffer yas-active-field-overlay)))) +#+END_SRC + +This is apparently related to overlay priorities. For some reason, the +=keymap= property of flyspell's overlays always takes priority over the +same property in YASnippet's overlays, even if one sets the latter's +=priority= property to something big. If you know emacs-lisp and can +solve this problem, drop a line in the +[[http://groups.google.com/group/smart-snippet][discussion group]]. + +* How to I use alternative keys, i.e. not TAB? + +Edit the keymaps [[sym:yas-minor-mode-map][=yas-minor-mode-map=]] and +[[sym:yas-keymap][=yas-keymap=]] as you would any other keymap: + +#+begin_src emacs-lisp :exports code + (define-key yas-minor-mode-map (kbd "<tab>") nil) + (define-key yas-minor-mode-map (kbd "TAB") nil) + (define-key yas-minor-mode-map (kbd "<the new key>") 'yas-expand) + + ;;keys for navigation + (define-key yas-keymap [(tab)] nil) + (define-key yas-keymap (kbd "TAB") nil) + (define-key yas-keymap [(shift tab)] nil) + (define-key yas-keymap [backtab] nil) + (define-key yas-keymap (kbd "<new-next-field-key>") 'yas-next-field-or-maybe-expand) + (define-key yas-keymap (kbd "<new-prev-field-key>") 'yas-prev) +#+end_src + +* How do I turn off the minor mode where in some buffers? + +The best way, since version 0.6.1c, is to set the default value of the +variable [[sym:yas-dont-activate][=yas-dont-activate=]] to a lambda function like so: + +#+BEGIN_SRC emacs-lisp + (set-default 'yas-dont-activate + #'(lambda () + (and yas-root-directory + (null (yas-get-snippet-tables))))) +#+END_SRC + +This is also the default value starting for that version. It skips the +minor mode in buffers where it is not applicable (no snippet tables), +but only once you have setup your yas-root-directory. + +* How do I define an abbrev key containing characters not supported by the filesystem? + +- *Note*: This question applies if you're still defining snippets + whose key /is/ the filename. This is behavior still provided by + version 0.6 for backward compatibilty, but is somewhat + deprecated... + +For example, you want to define a snippet by the key =<= which is not a +valid character for filename on Windows. This means you can't use the +filename as a trigger key in this case. + +You should rather use the =# key:= directive to specify the key of the +defined snippet explicitly and name your snippet with an arbitrary valid +filename, =lt.YASnippet= for example, using =<= for the =# key:= +directive: + +#+BEGIN_SRC snippet + # key: < + # name: <...></...> + # -- + <${1:div}>$0</$1> +#+END_SRC diff --git a/scripts/emacs/yasnippet/doc/images/bg-content-left.png b/scripts/emacs/yasnippet/doc/images/bg-content-left.png new file mode 100644 index 0000000000000000000000000000000000000000..a64b346e650cecd1fec5ccb082fbf14c757670ce Binary files /dev/null and b/scripts/emacs/yasnippet/doc/images/bg-content-left.png differ diff --git a/scripts/emacs/yasnippet/doc/images/bg-content-right.png b/scripts/emacs/yasnippet/doc/images/bg-content-right.png new file mode 100644 index 0000000000000000000000000000000000000000..f07ebb5cdef30d8c8d942d07981d23c7edf6aef9 Binary files /dev/null and b/scripts/emacs/yasnippet/doc/images/bg-content-right.png differ diff --git a/scripts/emacs/yasnippet/doc/images/bg-content.png b/scripts/emacs/yasnippet/doc/images/bg-content.png new file mode 100644 index 0000000000000000000000000000000000000000..d55828ed2277055f51b88ba8ee1259a0b0dea10c Binary files /dev/null and b/scripts/emacs/yasnippet/doc/images/bg-content.png differ diff --git a/scripts/emacs/yasnippet/doc/images/bg-navigation-item-hover.png b/scripts/emacs/yasnippet/doc/images/bg-navigation-item-hover.png new file mode 100644 index 0000000000000000000000000000000000000000..c783d718b7b2887ac752ed4febedd3d370be44aa Binary files /dev/null and b/scripts/emacs/yasnippet/doc/images/bg-navigation-item-hover.png differ diff --git a/scripts/emacs/yasnippet/doc/images/bg-navigation-item.png b/scripts/emacs/yasnippet/doc/images/bg-navigation-item.png new file mode 100644 index 0000000000000000000000000000000000000000..d2452ac4f22e3e954c9a15b899a4cc75b0d73ce7 Binary files /dev/null and b/scripts/emacs/yasnippet/doc/images/bg-navigation-item.png differ diff --git a/scripts/emacs/yasnippet/doc/images/bg-navigation.png b/scripts/emacs/yasnippet/doc/images/bg-navigation.png new file mode 100644 index 0000000000000000000000000000000000000000..18b9559de682b2004880080943a45df9d2c3730e Binary files /dev/null and b/scripts/emacs/yasnippet/doc/images/bg-navigation.png differ diff --git a/scripts/emacs/yasnippet/doc/images/body.png b/scripts/emacs/yasnippet/doc/images/body.png new file mode 100644 index 0000000000000000000000000000000000000000..b361e7b38058f271a77df9f99f6e84e4aed19da5 Binary files /dev/null and b/scripts/emacs/yasnippet/doc/images/body.png differ diff --git a/scripts/emacs/yasnippet/doc/images/customization-group.png b/scripts/emacs/yasnippet/doc/images/customization-group.png new file mode 100644 index 0000000000000000000000000000000000000000..b10827fcca631b671c46fa407d0ea62e38fb94bf Binary files /dev/null and b/scripts/emacs/yasnippet/doc/images/customization-group.png differ diff --git a/scripts/emacs/yasnippet/doc/images/dropdown-menu.png b/scripts/emacs/yasnippet/doc/images/dropdown-menu.png new file mode 100644 index 0000000000000000000000000000000000000000..57d482e5eefe37bc395efc5e8eec876aecd8fb33 Binary files /dev/null and b/scripts/emacs/yasnippet/doc/images/dropdown-menu.png differ diff --git a/scripts/emacs/yasnippet/doc/images/external.png b/scripts/emacs/yasnippet/doc/images/external.png new file mode 100644 index 0000000000000000000000000000000000000000..419c06fb960b0b665791c90044a78621616a4cb8 Binary files /dev/null and b/scripts/emacs/yasnippet/doc/images/external.png differ diff --git a/scripts/emacs/yasnippet/doc/images/ido-menu.png b/scripts/emacs/yasnippet/doc/images/ido-menu.png new file mode 100644 index 0000000000000000000000000000000000000000..df392c5ce789c7dc93b017251876eeb8b7e416c2 Binary files /dev/null and b/scripts/emacs/yasnippet/doc/images/ido-menu.png differ diff --git a/scripts/emacs/yasnippet/doc/images/menu-1.png b/scripts/emacs/yasnippet/doc/images/menu-1.png new file mode 100644 index 0000000000000000000000000000000000000000..d2e6a5180d31e22c4a2b43b87227db08c8c7160c Binary files /dev/null and b/scripts/emacs/yasnippet/doc/images/menu-1.png differ diff --git a/scripts/emacs/yasnippet/doc/images/menu-2.png b/scripts/emacs/yasnippet/doc/images/menu-2.png new file mode 100644 index 0000000000000000000000000000000000000000..abb8a72f3080e174d2c957688686bbef9732aad1 Binary files /dev/null and b/scripts/emacs/yasnippet/doc/images/menu-2.png differ diff --git a/scripts/emacs/yasnippet/doc/images/menu-groups.png b/scripts/emacs/yasnippet/doc/images/menu-groups.png new file mode 100644 index 0000000000000000000000000000000000000000..fcedda8998393a625fe27f12dcd40b9a53d7f728 Binary files /dev/null and b/scripts/emacs/yasnippet/doc/images/menu-groups.png differ diff --git a/scripts/emacs/yasnippet/doc/images/menu-parent.png b/scripts/emacs/yasnippet/doc/images/menu-parent.png new file mode 100644 index 0000000000000000000000000000000000000000..f0fa10c7da24d0772d8d841e57a28c3934b342f7 Binary files /dev/null and b/scripts/emacs/yasnippet/doc/images/menu-parent.png differ diff --git a/scripts/emacs/yasnippet/doc/images/minor-mode-indicator.png b/scripts/emacs/yasnippet/doc/images/minor-mode-indicator.png new file mode 100644 index 0000000000000000000000000000000000000000..3743455982a397cd2f62ede024960abb8223d8eb Binary files /dev/null and b/scripts/emacs/yasnippet/doc/images/minor-mode-indicator.png differ diff --git a/scripts/emacs/yasnippet/doc/images/x-menu.png b/scripts/emacs/yasnippet/doc/images/x-menu.png new file mode 100644 index 0000000000000000000000000000000000000000..3bc9a155fbb4ce668f733a80486c66828e512c5a Binary files /dev/null and b/scripts/emacs/yasnippet/doc/images/x-menu.png differ diff --git a/scripts/emacs/yasnippet/doc/index.org b/scripts/emacs/yasnippet/doc/index.org new file mode 100644 index 0000000000000000000000000000000000000000..f99b3f7f5dbfa8f023657f9c9813d181833fefcc --- /dev/null +++ b/scripts/emacs/yasnippet/doc/index.org @@ -0,0 +1,47 @@ +#+SETUPFILE: org-setup.inc +#+TITLE: Yet another snippet extension + +The YASnippet documentation has been split into separate parts: + +0. [[https://github.com/capitaomorte/yasnippet/blob/master/README.mdown][README]] + + Contains an introduction, installation instructions and other important + notes. + +1. [[file:snippet-organization.org][Organizing Snippets]] + + Describes ways to organize your snippets in the hard disk. + +2. [[file:snippet-expansion.org][Expanding Snippets]] + + Describes how YASnippet chooses snippets for expansion at point. + + Maybe, you'll want some snippets to be expanded in a particular mode, + or only under certain conditions, or be prompted using =ido=, etc... + +3. [[file:snippet-development.org][Writing Snippets]] + + Describes the YASnippet definition syntax, which is very close (but + not equivalent) to Textmate's. Includes a section about converting + TextMate snippets. + +4. [[file:snippet-menu.org][The YASnippet menu]] + + Explains how to use the YASnippet menu to explore, learn and modify + snippets. + +5. [[file:faq.org][Frequently asked questions]] + + Answers to frequently asked questions. + +6. [[file:snippet-reference.org][YASnippet Symbol Reference]] + + An automatically generated listing of all YASnippet commands, + (customization) variables, and functions. + + +# Local Variables: +# mode: org +# fill-column: 80 +# coding: utf-8 +# End: diff --git a/scripts/emacs/yasnippet/doc/nav-menu.html.inc b/scripts/emacs/yasnippet/doc/nav-menu.html.inc new file mode 100644 index 0000000000000000000000000000000000000000..b31ec940e020fa00c5a21277d9faa4893752c309 --- /dev/null +++ b/scripts/emacs/yasnippet/doc/nav-menu.html.inc @@ -0,0 +1,16 @@ +<nav> + <ul class="center"> + <li> <a href="index.html">Overview</a> + <li> <a href="https://github.com/capitaomorte/yasnippet/blob/master/README.mdown"> + Intro and Tutorial</a> + <li class="center">Snippet + <ul> + <li> <a href="snippet-organization.html">Organization</a> + <li> <a href="snippet-expansion.html">Expansion</a> + <li> <a href="snippet-development.html">Development</a> + <li> <a href="snippet-menu.html">Menu</a> + </ul> + <li> <a href="faq.html">FAQ</a> + <li> <a href="snippet-reference.html">Reference</a> + </ul> +</nav> diff --git a/scripts/emacs/yasnippet/doc/org-setup.inc b/scripts/emacs/yasnippet/doc/org-setup.inc new file mode 100644 index 0000000000000000000000000000000000000000..98f7c04a7626dab34eab4cd191c0e1570c8b36fd --- /dev/null +++ b/scripts/emacs/yasnippet/doc/org-setup.inc @@ -0,0 +1,9 @@ +# -*- mode: org -*- + +#+STARTUP: showall + +#+LINK: sym file:snippet-reference.org::#%s + +#+OPTIONS: author:nil num:nil +#+AUTHOR: +#+STYLE: <link rel="stylesheet" type="text/css" href="stylesheets/manual.css" /> diff --git a/scripts/emacs/yasnippet/doc/snippet-development.org b/scripts/emacs/yasnippet/doc/snippet-development.org new file mode 100644 index 0000000000000000000000000000000000000000..a7d653a81f5148705d5abbae4b5f822397a39aec --- /dev/null +++ b/scripts/emacs/yasnippet/doc/snippet-development.org @@ -0,0 +1,402 @@ +#+SETUPFILE: org-setup.inc + +#+TITLE: Writing snippets + +* Snippet development + +** Quickly finding snippets + +There are some ways you can quickly find a snippet file: + +- =M-x yas-new-snippet= + + Prompts you for a snippet name, then tries to guess a suitable + directory to store it, prompting you for creation if it does not + exist. Finally, places you in a new buffer set to =snippet-mode= so + you can write your snippet. + +- =M-x yas-find-snippets= + + Lets you find the snippet file in the directory the snippet was + loaded from (if it exists) like =find-file-other-window=. The + directory searching logic is similar to =M-x yas-new-snippet=. + +- =M-x yas-visit-snippet-file= + + Prompts you for possible snippet expansions like + [[sym:yas-insert-snippet][=yas-insert-snippet=]], but instead of expanding it, takes you directly + to the snippet definition's file, if it exists. + +Once you find this file it will be set to =snippet-mode= (see ahead) and +you can start editing your snippet. + +** Using the =snippet-mode= major mode + +There is a major mode =snippet-mode= to edit snippets. You can set the +buffer to this mode with =M-x snippet-mode=. It provides reasonably +useful syntax highlighting. + +Two commands are defined in this mode: + +- =M-x yas-load-snippet-buffer= + + When editing a snippet, this loads the snippet into the correct + mode and menu. Bound to =C-c C-c= by default while in + =snippet-mode=. + +- =M-x yas-tryout-snippet= + + When editing a snippet, this opens a new empty buffer, sets it to + the appropriate major mode and inserts the snippet there, so you + can see what it looks like. This is bound to =C-c C-t= while in + =snippet-mode=. + +There are also /snippets for writing snippets/: =vars=, =$f= and =$m= +:-). + +* File content + +A file defining a snippet generally contains the template to be +expanded. + +Optionally, if the file contains a line of =# --=, the lines above it +count as comments, some of which can be /directives/ (or meta data). +Snippet directives look like =# property: value= and tweak certain +snippets properties described below. If no =# --= is found, the whole +file is considered the snippet template. + +Here's a typical example: + +#+BEGIN_SRC snippet + # contributor: pluskid <pluskid@gmail.com> + # name: __...__ + # -- + __${init}__ +#+END_SRC + +Here's a list of currently supported directives: + +** =# key:= snippet abbrev + +This is the probably the most important directive, it's the +abbreviation you type to expand a snippet just before hitting the key +that runs [[sym:yas-expand][=yas-expand=]]. If you don't specify this +the snippet will not be expandable through the trigger mechanism. + +** =# name:= snippet name + +This is a one-line description of the snippet. It will be displayed in +the menu. It's a good idea to select a descriptive name for a snippet -- +especially distinguishable among similar snippets. + +If you omit this name it will default to the file name the snippet was +loaded from. + +** =# condition:= snippet condition + +This is a piece of Emacs-lisp code. If a snippet has a condition, then +it will only be expanded when the condition code evaluate to some +non-nil value. + +See also [[sym:yas-buffer-local-condition][=yas-buffer-local-condition=]] in +[[./snippet-expansion.org][Expanding snippets]] + +** =# group:= snippet menu grouping + +When expanding/visiting snippets from the menu-bar menu, snippets for a +given mode can be grouped into sub-menus . This is useful if one has too +many snippets for a mode which will make the menu too long. + +The =# group:= property only affect menu construction (See +[[./snippet-menu.org][the YASnippet menu]]) and the same effect can be +achieved by grouping snippets into sub-directories and using the +=.yas-make-groups= special file (for this see +[[./snippet-organization.org][Organizing Snippets]] + +Refer to the bundled snippets for =ruby-mode= for examples on the +=# group:= directive. Group can also be nested, e.g. +=control structure.loops= tells that the snippet is under the =loops= +group which is under the =control structure= group. + +** =# expand-env:= expand environment + +This is another piece of Emacs-lisp code in the form of a =let= /varlist +form/, i.e. a list of lists assigning values to variables. It can be +used to override variable values while the snippet is being expanded. + +Interesting variables to override are [[sym:yas-wrap-around-region][=yas-wrap-around-region=]] and +[[sym:yas-indent-line][=yas-indent-line=]] (see [[./snippet-expansion.org][Expanding Snippets]]). + +As an example, you might normally have [[sym:yas-indent-line][=yas-indent-line=]] set to '=auto= +and [[sym:yas-wrap-around-region][=yas-wrap-around-region=]] set to =t=, but for this particularly +brilliant piece of ASCII art these values would mess up your hard work. +You can then use: + +#+BEGIN_SRC snippet + # name: ASCII home + # expand-env: ((yas-indent-line 'fixed) (yas-wrap-around-region 'nil)) + # -- + welcome to my + X humble + / \ home, + / \ $0 + / \ + /-------\ + | | + | +-+ | + | | | | + +--+-+--+ +#+END_SRC + +** =# binding:= direct keybinding + +You can use this directive to expand a snippet directly from a normal +Emacs keybinding. The keybinding will be registered in the Emacs keymap +named after the major mode the snippet is active for. + +Additionally a variable [[sym:yas-prefix][=yas-prefix=]] is set to to the prefix argument +you normally use for a command. This allows for small variations on the +same snippet, for example in this "html-mode" snippet. + +#+BEGIN_SRC snippet + # name: <p>...</p> + # binding: C-c C-c C-m + # -- + <p>`(when yas-prefix "\n")`$0`(when yas-prefix "\n")`</p> +#+END_SRC + +This binding will be recorded in the keymap =html-mode-map=. To expand a +paragraph tag newlines, just press =C-u C-c C-c C-m=. Omitting the =C-u= +will expand the paragraph tag without newlines. + +** =# contributor:= snippet author + +This is optional and has no effect whatsoever on snippet functionality, +but it looks nice. + +* Template syntax + +The syntax of the snippet template is simple but powerful, very similar +to TextMate's. + +** Plain Text + +Arbitrary text can be included as the content of a template. They are +usually interpreted as plain text, except =$= and ==. You need to +use \` to escape them: =\$= and =\=. The \` itself may also needed to be +escaped as =\\= sometimes. + +** Embedded Emacs-lisp code + +Emacs-Lisp code can be embedded inside the template, written inside +back-quotes (==). The lisp forms are evaluated when the snippet is +being expanded. The evaluation is done in the same buffer as the +snippet being expanded. + +Here's an example for c-mode` to calculate the header file guard +dynamically: + +#+BEGIN_SRC snippet + #ifndef ${1:_`(upcase (file-name-nondirectory (file-name-sans-extension (buffer-file-name))))`_H_} + #define $1 + + $0 + + #endif /* $1 */ +#+END_SRC + +From version 0.6, snippets expansions are run with some special +Emacs-lisp variables bound. One of this is [[sym:yas-selected-text][=yas-selected-text=]]. You can +therefore define a snippet like: + +#+BEGIN_SRC snippet + for ($1;$2;$3) { + `yas-selected-text`$0 + } +#+END_SRC + +to "wrap" the selected region inside your recently inserted snippet. +Alternatively, you can also customize the variable +[[sym:yas-wrap-around-region][=yas-wrap-around-region=]] to =t= which will do this automatically. + +** Tab stop fields + +Tab stops are fields that you can navigate back and forth by =TAB= and +=S-TAB=. They are written by =$= followed with a number. =$0= has the +special meaning of the /exit point/ of a snippet. That is the last place +to go when you've traveled all the fields. Here's a typical example: + +#+BEGIN_SRC snippet + <div$1> + $0 + </div> +#+END_SRC +** Placeholder fields + +Tab stops can have default values -- a.k.a placeholders. The syntax is +like this: + +#+BEGIN_SRC snippet + ${N:default value} +#+END_SRC + +They acts as the default value for a tab stop. But when you firstly +type at a tab stop, the default value will be replaced by your typing. +The number can be omitted if you don't want to create [[mirrors]] or +[[transformations]] for this field. + +** <<Mirrors>> + +We refer the tab stops with placeholders as a /field/. A field can have +mirrors. Its mirrors will get updated when you change the text of a +field. Here's an example: + +#+BEGIN_SRC snippet + \begin{${1:enumerate}} + $0 + \end{$1} +#+END_SRC + +When you type "document" at =${1:enumerate}=, the word "document" will +also be inserted at =\end{$1}=. The best explanation is to see the +screencast([[http://www.youtube.com/watch?v=vOj7btx3ATg][YouTube]] or [[http://yasnippet.googlecode.com/files/yasnippet.avi][avi video]]). + +The tab stops with the same number to the field act as its mirrors. If +none of the tab stops has an initial value, the first one is selected as +the field and others mirrors. + +** Mirrors with <<transformations>> + +If the value of an =${n:=-construct starts with and contains =$(=, then +it is interpreted as a mirror for field =n= with a transformation. The +mirror's text content is calculated according to this transformation, +which is Emacs-lisp code that gets evaluated in an environment where the +variable =text= (or [[sym:yas-text][=yas-text=]]) is bound to the text content (string) +contained in the field =n=.Here's an example for Objective-C: + +#+BEGIN_SRC snippet + - (${1:id})${2:foo} + { + return $2; + } + + - (void)set${2:$(capitalize text)}:($1)aValue + { + [$2 autorelease]; + $2 = [aValue retain]; + } + $0 +#+END_SRC + +Look at =${2:$(capitalize text)}=, it is a mirror with transformation +instead of a field. The actual field is at the first line: =${2:foo}=. +When you type text in =${2:foo}=, the transformation will be evaluated +and the result will be placed there as the transformed text. So in this +example, if you type "baz" in the field, the transformed text will be +"Baz". This example is also available in the screencast. + +Another example is for =rst-mode=. In reStructuredText, the document +title can be some text surrounded by "===" below and above. The "===" +should be at least as long as the text. So + +#+BEGIN_SRC rst + ===== + Title + ===== +#+END_SRC + +is a valid title but + +#+BEGIN_SRC rst + === + Title + === +#+END_SRC + +is not. Here's an snippet for rst title: + +#+BEGIN_SRC snippet + ${1:$(make-string (string-width text) ?\=)} + ${1:Title} + ${1:$(make-string (string-width text) ?\=)} + + $0 +#+END_SRC + +** Fields with transformations + +From version 0.6 on, you can also have lisp transformation inside +fields. These work mostly mirror transformations but are evaluated when +you first enter the field, after each change you make to the field and +also just before you exit the field. + +The syntax is also a tiny bit different, so that the parser can +distinguish between fields and mirrors. In the following example + +: #define "${1:mydefine$(upcase yas-text)}" + +=mydefine= gets automatically upcased to =MYDEFINE= once you enter the +field. As you type text, it gets filtered through the transformation +every time. + +Note that to tell this kind of expression from a mirror with a +transformation, YASnippet needs extra text between the =:= and the +transformation's =$=. If you don't want this extra-text, you can use two +=$='s instead. + +: #define "${1:$$(upcase yas-text)}" + +Please note that as soon as a transformation takes place, it changes the +value of the field and sets it its internal modification state to +=true=. As a consequence, the auto-deletion behaviour of normal fields +does not take place. This is by design. + +** Choosing fields value from a list and other tricks + +As mentioned, the field transformation is invoked just after you enter +the field, and with some useful variables bound, notably +[[sym:yas-modified-p][=yas-modified-p=]] and [[sym:yas-moving-away-p][=yas-moving-away-p=]]. Because of this feature you +can place a transformation in the primary field that lets you select +default values for it. + +The [[sym:yas-choose-value][=yas-choose-value=]] does this work for you. For example: + +#+BEGIN_SRC snippet + <div align="${2:$$(yas-choose-value '("right" "center" "left"))}"> + $0 + </div> +#+END_SRC + +See the definition of [[sym:yas-choose-value][=yas-choose-value=]] to see how it was written using +the two variables. + +Here's another use, for LaTeX-mode, which calls reftex-label just as you +enter snippet field 2. This one makes use of [[sym:yas-modified-p][=yas-modified-p=]] directly. + +#+BEGIN_SRC snippet + \section{${1:"Titel der Tour"}}% + \index{$1}% + \label{{2:"waiting for reftex-label call..."$(unless yas-modified-p (reftex-label nil 'dont- + insert))}}% +#+END_SRC + +The function [[sym:yas-verify-value][=yas-verify-value=]] has another neat trick, and makes use +of [[sym:yas-moving-away-p][=yas-moving-away-p=]]. Try it and see! Also, check out this [[http://groups.google.com/group/smart-snippet/browse_thread/thread/282a90a118e1b662][thread]] + +** Nested placeholder fields + +From version 0.6 on, you can also have nested placeholders of the type: + +#+BEGIN_SRC snippet + <div${1: id="${2:some_id}"}>$0</div> +#+END_SRC + +This allows you to choose if you want to give this =div= an =id= +attribute. If you tab forward after expanding it will let you change +"some\_id" to whatever you like. Alternatively, you can just press =C-d= +(which executes [[sym:yas-skip-and-clear-or-delete-char][=yas-skip-and-clear-or-delete-char=]]) and go straight to +the exit marker. + +By the way, =C-d= will only clear the field if you cursor is at the +beginning of the field /and/ it hasn't been changed yet. Otherwise, it +performs the normal Emacs =delete-char= command. diff --git a/scripts/emacs/yasnippet/doc/snippet-expansion.org b/scripts/emacs/yasnippet/doc/snippet-expansion.org new file mode 100644 index 0000000000000000000000000000000000000000..876b81c3ef08053015f05be8cd1fb1437a42e456 --- /dev/null +++ b/scripts/emacs/yasnippet/doc/snippet-expansion.org @@ -0,0 +1,262 @@ +#+SETUPFILE: org-setup.inc + +#+TITLE: Expanding snippets + + This section describes how YASnippet chooses snippets for expansion at point. + + Maybe, you'll want some snippets to be expanded in a particular + mode, or only under certain conditions, or be prompted using + +* Triggering expansion + + You can use YASnippet to expand snippets in different ways: + + - When [[sym:yas-minor-mode][=yas-minor-mode=]] is active: + - Type the snippet's *trigger key* then calling [[sym:yas-expand][=yas-expand=]] + (bound to =TAB= by default). + + - Use the snippet's *keybinding*. + + - By expanding directly from the "YASnippet" menu in the menu-bar + + - Using hippie-expand + + - Call [[sym:yas-insert-snippet][=yas-insert-snippet=]] (use =M-x yas-insert-snippet== or its + keybinding =C-c & C-s=). + + - Use m2m's excellent auto-complete + TODO: example for this + + - Expanding from emacs-lisp code + +** Trigger key + +[[sym:yas-expand][=yas-expand=]] tries to expand a /snippet abbrev/ (also known as +/snippet key/) before point. + +When [[sym:yas-minor-mode][=yas-minor-mode=]] is enabled, it binds [[sym:yas-expand][=yas-expand=]] to =TAB= and +=<tab>= by default, however, you can freely set it to some other key: + +#+begin_src emacs-lisp :exports code + (define-key yas-minor-mode-map (kbd "<tab>") nil) + (define-key yas-minor-mode-map (kbd "TAB") nil) + (define-key yas-minor-mode-map (kbd "<the new key>") 'yas-expand) +#+end_src + +To enable the YASnippet minor mode in all buffers globally use the +command [[sym:yas-global-mode][=yas-global-mode=]]. This will enable a modeline indicator, +=yas=: + +[[./images/minor-mode-indicator.png]] + +When you use [[sym:yas-global-mode][=yas-global-mode=]] you can also selectively disable +YASnippet in some buffers by setting the buffer-local variable +[[sym:yas-dont-active][=yas-dont-active=]] in the buffer's mode hook. + +*** Fallback behaviour + +[[sym:yas-fallback-behaviour][=yas-fallback-behaviour=]] is a customization variable bound to +'=call-other-command= by default. If [[sym:yas-expand][=yas-expand=]] failed to find any +suitable snippet to expand, it will disable the minor mode temporarily +and find if there's any other command bound to the same key. + +If found, the command will be called. Usually this works very well +--when there's a snippet, expand it, otherwise, call whatever command +originally bind to the trigger key. + +However, you can change this behavior by customizing the +[[sym:yas-fallback-behavior][=yas-fallback-behavior=]] variable. If you set this variable to +'=return-nil=, it will return =nil= instead of trying to call the +/original/ command when no snippet is found. + +** Insert at point + +The command [[#yas-insert-snippet][=yas-insert-snippet=]] lets you insert snippets at point +/for your current major mode/. It prompts you for the snippet key +first, and then for a snippet template if more than one template +exists for the same key. + +The list presented contains the snippets that can be inserted at point, +according to the condition system. If you want to see all applicable +snippets for the major mode, prefix this command with =C-u=. + +The prompting methods used are again controlled by +[[sym:yas-prompt-functions][=yas-prompt-functions=]]. + +** Snippet keybinding + +See the section of the =# binding:= directive in +[[./snippet-development.org][Writing Snippets]]. + +** Expanding from the menu + +See [[./snippet-menu.org][the YASnippet Menu]]. + +** Expanding with =hippie-expand= + +To integrate with =hippie-expand=, just put +[[sym:yas-hippie-try-expand][=yas-hippie-try-expand=]] in +=hippie-expand-try-functions-list=. This probably makes more sense +when placed at the top of the list, but it can be put anywhere you +prefer. + +** Expanding from emacs-lisp code + +Sometimes you might want to expand a snippet directly from you own +elisp code. You should call +[[sym:yas-expand-snippet][=yas-expand-snippet=]] instead of +[[sym:yas-expand][=yas-expand=]] in this case. + +As with expanding from the menubar, the condition system and multiple +candidates doesn't affect expansion. In fact, expanding from the +YASnippet menu has the same effect of evaluating the follow code: + +#+BEGIN_SRC emacs-lisp + (yas-expand-snippet template) +#+END_SRC + +See the internal documentation on [[sym:yas-expand-snippet][=yas-expand-snippet=]] for more +information. + +* Controlling expansion + +** Eligible snippets + +YASnippet does quite a bit of filtering to find out which snippets are +eligible for expanding at the current cursor position. + +In particular, the following things matter: + +- Currently loaded snippets tables + + These are loaded from a directory hierarchy in your file system. See + [[./snippet-organization.org][Organizing Snippets]]. They are named + after major modes like =html-mode=, =ruby-mode=, etc... + +- Major mode of the current buffer + + If the currrent major mode matches one of the loaded snippet tables, + then all that table's snippets are considered for expansion. Use + =M-x describe-variable RET major-mode RET= to find out which major + mode you are in currently. + +- Parent tables + + Snippet tables defined as the parent of some other eligible table are + also considered. This works recursively, i.e. parents of parents of + eligible tables are also considered. + +- Buffer-local list of extra modes + + Use [[#yas-activate-extra-mode][=yas-activate-extra-mode=]] to + consider snippet tables whose name does not correspond to a major + mode. Typically, you call this from a minor mode hook, for example: + +#+BEGIN_SRC emacs-lisp + ;; When entering rinari-minor-mode, consider also the snippets in the + ;; snippet table "rails-mode" + (add-hook 'rinari-minor-mode-hook + #'(lambda () + (yas-activate-extra-mode 'rails-mode))) +#+END_SRC + +- Buffer-local + [[sym:yas-buffer-local-condition][=yas-buffer-local-condition=]] + variable + + This variable provides finer grained control over what snippets can + be expanded in the current buffer. The default value won't let you + expand snippets inside comments or string literals for example. See + The condition system\_ for more info. + +** The condition system + +Consider this scenario: you are an old Emacs hacker. You like the +abbrev-way and bind [[sym:yas-expand][=yas-expand=]] to =SPC=. However, you don't want +=if= to be expanded as a snippet when you are typing in a comment +block or a string (e.g. in =python-mode=). + +If you use the =# condition := directive (see +[[./snippet-development.org][Writing Snippets]]) you could just specify +the condition for =if= to be =(not (python-in-string/comment))=. But how +about =while=, =for=, etc. ? Writing the same condition for all the +snippets is just boring. So has a buffer local variable +[[sym:yas-buffer-local-condition][=yas-buffer-local-condition=]]. You can set this variable to +=(not (python-in-string/comment))= in =python-mode-hook=. + +Then, what if you really want some particular snippet to expand even +inside a comment? Set [[sym:yas-buffer-local-condition][=yas-buffer-local-condition=]] like this + +#+BEGIN_SRC emacs-lisp + (add-hook 'python-mode-hook + (lambda () + (setq yas-buffer-local-condition + '(if (python-in-string/comment) + '(require-snippet-condition . force-in-comment) + t)))) +#+END_SRC + +... and specify the condition for a snippet that you're going to expand +in comment to be evaluated to the symbol =force-in-comment=. Then it can +be expanded as you expected, while other snippets like =if= still can't +expanded in comment. + +For the full set of possible conditions, see the documentation for +[[sym:yas-buffer-local-condition][=yas-buffer-local-condition=]]. + +** Multiples snippet with the same key + +The rules outlined [[Eligible%20snippets][above]] can return more than +one snippet to be expanded at point. + +When there are multiple candidates, YASnippet will let you select one. +The UI for selecting multiple candidate can be customized through +[[sym:yas-prompt-functions][=yas-prompt-functions=]] , which defines your preferred methods of being +prompted for snippets. + +You can customize it with +=M-x customize-variable RET yas-prompt-functions RET=. Alternatively you +can put in your emacs-file: + +#+BEGIN_SRC emacs-lisp + (setq yas-prompt-functions '(yas-x-prompt yas-dropdown-prompt)) +#+END_SRC + +Currently there are some alternatives solution with YASnippet. + +*** Use the X window system + +[[./images/x-menu.png]] + +The function [[sym:yas-x-prompt][=yas-x-prompt=]] can be used to show a popup menu for you to +select. This menu will be part of you native window system widget, which +means: + +- It usually looks beautiful. E.g. when you compile Emacs with gtk + support, this menu will be rendered with your gtk theme. +- Your window system may or may not allow to you use =C-n=, =C-p= to + navigate this menu. +- This function can't be used when in a terminal. + +*** Minibuffer prompting + +[[./images/ido-menu.png]] + +You can use functions [[sym:yas-completing-prompt][=yas-completing-prompt=]] for the classic emacs +completion method or [[sym:yas-ido-prompt][=yas-ido-prompt=]] for a much nicer looking method. +The best way is to try it. This works in a terminal. + +*** Use =dropdown-menu.el= + +[[./images/dropdown-menu.png]] + +The function [[sym:yas-dropdown-prompt][=yas-dropdown-prompt=]] can also be placed in the +[[sym:yas-prompt-functions][=yas-prompt-functions=]] list. + +This works in both window system and terminal and is customizable, you +can use =C-n=, =C-p= to navigate, =q= to quit and even press =6= as a +shortcut to select the 6th candidate. + +*** Roll your own + +See the documentation on variable [[sym:yas-prompt-functions][=yas-prompt-functions=]] diff --git a/scripts/emacs/yasnippet/doc/snippet-menu.org b/scripts/emacs/yasnippet/doc/snippet-menu.org new file mode 100644 index 0000000000000000000000000000000000000000..46b9b0c0c5ecdd30e394d1feb3a2fa95d5b0d9bc --- /dev/null +++ b/scripts/emacs/yasnippet/doc/snippet-menu.org @@ -0,0 +1,69 @@ +#+SETUPFILE: org-setup.inc + +#+TITLE: YASnippet menu + +When [[sym:yas-minor-mode][=yas-minor-mode=]] is active, YASnippet will setup a menu just after +the "Buffers" menu in the menubar. + +In this menu, you can find + +- The currently loaded snippet definitions, organized by major mode, + and optional grouping. + +- A rundown of the most common commands, (followed by their + keybindings) including commands to load directories and reload all + snippet definitions. + +- A series of submenus for customizing and exploring YASnippet + behavior. + +[[./images/menu-1.png]] + +* Loading snippets from menu + +Invoking "Load snippets..." from the menu invokes [[sym:yas-load-directory][=yas-load-directory=]] +and prompts you for a snippet directory hierarchy to load. + +Also useful is the "Reload everything" item to invoke [[#yas-reload-all][=yas-reload-all=]] +which uncondionally reloads all the snippets directories defined in +[[sym:yas-snippet-dirs][=yas-snippet-dirs=]] and rebuilds the menus. + +* Snippet menu behavior + +YASnippet will list in this section all the loaded snippet definitions +organized by snippet table name. + +You can use this section to explore currently loaded snippets. If you +click on one of them, the default behavior is to expand it, +unconditionally, inside the current buffer. + +You can however, customize variable [[sym:yas-visit-from-menu][=yas-visit-from-menu=]] to be =t= +which will take you to the snippet definition file when you select it +from the menu. + +If you want the menu show only snippet tables whose name corresponds to +a "real" major mode. You do this by setting [[sym:yas-use-menu][=yas-use-menu=]] to +'=real-modes=. + +Finally, to have the menu show only the tables for the currently active +mode, set [[sym:yas-use-menu][=yas-use-menu=]] to =abbreviate=. + +These customizations can also be found in the menu itself, under the +"Snippet menu behavior" submenu. + +* Controlling indenting + +The "Indenting" submenu contains options to control the values of +[[sym:yas-indent-line][=yas-indent-line=]] and [[sym:yas-also-auto-indent-first-line][=yas-also-auto-indent-first-line=]]. See +[[./snippet-development.org][Writing snippets]] . + +* Prompting method + +The "Prompting method" submenu contains options to control the value of +[[sym:yas-prompt-functions][=yas-prompt-functions=]]. See [[./snippet-expansion.org][Expanding +snippets]] . + +* Misc + +The "Misc" submenu contains options to control the values of more +variables. diff --git a/scripts/emacs/yasnippet/doc/snippet-organization.org b/scripts/emacs/yasnippet/doc/snippet-organization.org new file mode 100644 index 0000000000000000000000000000000000000000..ee55a8efb6a6be1d5bc43004ed189ace6281bf20 --- /dev/null +++ b/scripts/emacs/yasnippet/doc/snippet-organization.org @@ -0,0 +1,121 @@ +#+SETUPFILE: org-setup.inc + +#+TITLE: Organizing snippets + +* Basic structure + + Snippet collections can be stored in plain text files. They are arranged by + sub-directories naming *snippet tables*. These mostly name Emacs major names. + + #+begin_example + . + |-- c-mode + | `-- printf + |-- java-mode + | `-- println + `-- text-mode + |-- email + `-- time + #+end_example + + The collections are loaded into *snippet tables* which the + triggering mechanism (see [[file:snippet-expansion.org][Expanding Snippets]]) looks up and + (hopefully) causes the right snippet to be expanded for you. + +* Setting up =yas-snippet-dirs= + + The emacs variable [[sym:yas-snippet-dirs][=yas-snippet-dirs=]] tells YASnippet + which collections to consider. It's used when you activate + [[sym:yas-global-mode][=yas-global-mode=]] or call + [[sym:yas-reload-all][=yas-reload-all=]] interactively. + + The default considers: + + - a personal collection that lives in =~/.emacs.d/snippets= + - the bundled collection, taken as a relative path to =yasnippet.el= localtion + + When you come across other snippet collections, do the following to try them + out: + + #+begin_src emacs-lisp :exports code + ;; Develop in ~/emacs.d/mysnippets, but also + ;; try out snippets in ~/Downloads/interesting-snippets + (setq yas-snippet-dirs '("~/emacs.d/mysnippets" + "~/Downloads/interesting-snippets")) + + ;; OR, keeping YASnippet defaults try out ~/Downloads/interesting-snippets + (setq yas-snippet-dirs (append yas-snippet-dirs + '("~/Downloads/interesting-snippets"))) + #+end_src + + Collections appearing earlier in the list shadow snippets with same names + appearing in collections later in the list. [[sym:yas-new-snippet][=yas-new-snippet=]] always stores + snippets in the first collection. + +* The =.yas-parents= file + + It's very useful to have certain modes share snippets between + themselves. To do this, choose a mode subdirectory and place a + =.yas-parents= containing a whitespace-separated list of other mode + names. When you reload those modes become parents of the original + mode. + + #+begin_example + . + |-- c-mode + | |-- .yas-parents # contains "cc-mode text-mode" + | `-- printf + |-- cc-mode + | |-- for + | `-- while + |-- java-mode + | |-- .yas-parents # contains "cc-mode text-mode" + | `-- println + `-- text-mode + |-- email + `-- time + #+end_example + + +* TODO The =.yas-make-groups= file + + If you place an empty plain text file =.yas-make-groups= inside one + of the mode directories, the names of these sub-directories are + considered groups of snippets and [[snippet-menu.org][the menu]] is organized much more + cleanly: + + [[./images/menu-groups.png]] + + Another way to achieve this is to place a =# group:= directive + inside the snippet definition. See [[./snippet-development.org][Writing Snippets]]. + + #+begin_example + $ tree ruby-mode/ + ruby-mode/ + |-- .yas-make-groups + |-- collections + | |-- each + | `-- ... + |-- control structure + | |-- forin + | `-- ... + |-- definitions + | `-- ... + `-- general + `-- ... + #+end_example + + Yet another way to create a nice snippet menu is to write into + =.yas-make-groups= a menu definition. TODO + +* TODO The =.yas-setup.el= file + +** TODO + +* TODO The =.yas-compiled-snippet.el= file + +** TODO + +* TODO The =.yas-skip= file + +** TODO diff --git a/scripts/emacs/yasnippet/doc/snippet-reference.org b/scripts/emacs/yasnippet/doc/snippet-reference.org new file mode 100644 index 0000000000000000000000000000000000000000..a38fca5a906758962332216bf80bd4640b11d87a --- /dev/null +++ b/scripts/emacs/yasnippet/doc/snippet-reference.org @@ -0,0 +1,12 @@ +#+SETUPFILE: org-setup.inc + +#+TITLE: Reference + +#+BEGIN_SRC emacs-lisp :exports results :results value raw +(yas--document-symbols 1 `("Interactive functions" . ,#'interactive-form) + `("Customization variables" . ,#'(lambda (sym) + (and (boundp sym) + (get sym 'standard-value)))) + `("Useful functions" . ,#'fboundp) + `("Useful variables" . ,#'boundp)) +#+END_SRC diff --git a/scripts/emacs/yasnippet/doc/stylesheets/manual.css b/scripts/emacs/yasnippet/doc/stylesheets/manual.css new file mode 100644 index 0000000000000000000000000000000000000000..f2677c5a4fbb0e4b2d2fd02a533d528685e303f6 --- /dev/null +++ b/scripts/emacs/yasnippet/doc/stylesheets/manual.css @@ -0,0 +1,26 @@ +nav > ul > li.center > ul { + padding: 0; +} + +nav li { + vertical-align: top; + + display: inline; + list-style-type: none; + padding: 0.5em; +} + +nav > ul > li { + display: inline-block; +} + +/* match org's css for <pre> */ +code { + background-color: #F3F5F7; + font-family: courier, monospace; +} + +#content { + margin-left: 5%; + margin-right: 10%; +} diff --git a/scripts/emacs/yasnippet/doc/stylesheets/styles.css b/scripts/emacs/yasnippet/doc/stylesheets/styles.css new file mode 100644 index 0000000000000000000000000000000000000000..f402e0dd0a15557f76b3484c0760fe5d149d2823 --- /dev/null +++ b/scripts/emacs/yasnippet/doc/stylesheets/styles.css @@ -0,0 +1,93 @@ +@media all +{ + body { + margin: 1em auto; + /*margin: 10px 18% 10px 18%;*/ + font-family: Arial; + /*text-align: justify;*/ + font-size: 14pt; + padding: 10px; + line-height: 1.2em; + max-width: 600pt; + } + + div#table-of-contents { + position: fixed; + left: 0%; + right: 0%; + top: 0px; + z-index: 100; + background: black; + } + + div#table-of-contents h2 { + display: none; + } + + div#table-of-contents a { + text-decoration: none; + color: white; + } + + div#table-of-contents a:visited { + color: white; + } + + div#table-of-contents a:hover { + color: orange; + } + + div.outline-2 h2{ + padding-top: 50px; + } + + div#text-table-of-contents { + text-color: white; + text-align: center; + margin-left: 30%; + margin-right: 30%; + } + + div#text-table-of-contents ul { + height: 2em; + width: 500px; + list-style: none; + margin: auto; + } + + div#text-table-of-contents ul li { + float: left; + margin-left:auto; + margin-right: auto; + padding-left: 10px; + } + + div#postamble{ + position: fixed; + width: 800px; + height: 250px; + left: 50%; + right: 50%; + margin:-75px 0 0 -400px; + bottom: -20px; + font-size: 10pt; + color: grey; + background: url('siscog-bottom-logo.png') no-repeat; + /* background-size: 100% 100%; */ + } + + div#postamble *{ + display: none; + } + + div#postamble p.date{ + position: relative; + bottom: -200px; + text-align: center; + display: block; + } + + + + +} \ No newline at end of file diff --git a/scripts/emacs/yasnippet/doc/yas-doc-helper.el b/scripts/emacs/yasnippet/doc/yas-doc-helper.el new file mode 100755 index 0000000000000000000000000000000000000000..925e41beecb522eebfc3cd37efcfbbb292e39dce --- /dev/null +++ b/scripts/emacs/yasnippet/doc/yas-doc-helper.el @@ -0,0 +1,156 @@ +;;; yas-doc-helper.el --- Help generate documentation for YASnippet + +;; Copyright (C) 2012 João Távora + +;; Author: João Távora <joaotavora@gmail.com> +;; Keywords: convenience + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see <http://www.gnu.org/licenses/>. + +;;; Commentary: + +;; Some functions to help generate YASnippet docs + +;;; Code: + +(eval-when-compile + (require 'cl)) +(require 'org) +(require 'org-publish) +(require 'yasnippet) ; docstrings must be loaded + +(defun yas--document-symbol (symbol level) + (flet ((concat-lines (&rest lines) + (mapconcat #'identity lines "\n"))) + (let* ((stars (make-string level ?*)) + (args (and (fboundp symbol) + (mapcar #'symbol-name (help-function-arglist symbol t)))) + (heading (cond ((fboundp symbol) + (format + "%s =%s= (%s)" stars symbol + (mapconcat (lambda (a) + (format (if (string-prefix-p "&" a) + "/%s/" "=%s=") a)) + args " "))) + (t + (format "%s =%s=\n" stars symbol)))) + (after-heading + (concat-lines ":PROPERTIES:" + (format ":CUSTOM_ID: %s" symbol) + ":END:")) + (body (or (cond ((fboundp symbol) + (let ((doc-synth (car-safe (get symbol 'function-documentation)))) + (if (functionp doc-synth) + (funcall doc-synth nil) + (documentation symbol t)))) + ((boundp symbol) + (documentation-property symbol 'variable-documentation t)) + (t + (format "*WARNING*: no symbol named =%s=" symbol))) + (format "*WARNING*: no doc for symbol =%s=" symbol))) + (case-fold-search nil)) + ;; do some transformations on the body: + ;; ARGxxx becomes @<code>arg@</code>xxx + ;; FOO becomes /foo/ + ;; `bar' becomes [[#bar][=bar=]] + (setq body (replace-regexp-in-string + "\\<\\([A-Z][-A-Z0-9]+\\)\\(\\sw+\\)?\\>" + #'(lambda (match) + (let* ((match1 (match-string 1 match)) + (prefix (downcase match1)) + (suffix (match-string 2 match)) + (fmt (cond + ((member prefix args) "@<code>%s@</code>") + ((null suffix) "/%s/")))) + (if fmt (format fmt prefix) + match1))) + body t t 1) + body (replace-regexp-in-string + "`\\([a-z-]+\\)'" + #'(lambda (match) + (let* ((name (downcase (match-string 1 match))) + (sym (intern name))) + (if (memq sym yas--exported-syms) + (format "[[#%s][=%s=]]" name name) + (format "=%s=" name)))) + body t)) + ;; output the paragraph + ;; + (concat-lines heading + after-heading + body)))) + +(defun yas--document-symbols (level &rest names-and-predicates) + (let ((sym-lists (make-vector (length names-and-predicates) nil)) + (stars (make-string level ?*))) + (loop for sym in yas--exported-syms + do (loop for test in (mapcar #'cdr names-and-predicates) + for i from 0 + do (when (funcall test sym) + (push sym (aref sym-lists i)) + (return)))) + (loop for slist across sym-lists + for name in (mapcar #'car names-and-predicates) + concat (format "\n%s %s\n" stars name) + concat (mapconcat (lambda (sym) + (yas--document-symbol sym (1+ level))) + slist "\n\n")))) + +(defun yas--internal-link-snippet () + (interactive) + (yas-expand-snippet "[[#$1][=${1:`yas/selected-text`}=]]")) + +(define-key org-mode-map [M-f8] 'yas--internal-link-snippet) + +;; This lets all the org files be exported to HTML with +;; `org-publish-current-project' (C-c C-e P). + +(let* ((dir (if load-file-name (file-name-directory load-file-name) + default-directory)) + (rev (with-temp-file (expand-file-name "html-revision" dir) + (or (when (eq (call-process "git" nil t nil + "rev-parse" "--verify" "HEAD") 0) + (buffer-string)) + (princ yas--version (current-buffer))))) + (proj-plist + (list + :base-directory dir :publishing-directory dir + :html-preamble + (with-temp-buffer + (insert-file-contents (expand-file-name "nav-menu.html.inc" dir)) + (buffer-string)) + :html-postamble + (concat "<hr><p class='creator'>Generated by %c on %d from " + rev "</p>\n" + "<p class='xhtml-validation'>%v</p>\n"))) + (project (assoc "yasnippet" org-publish-project-alist))) + (if project + (setcdr project proj-plist) + (push `("yasnippet" . ,proj-plist) + org-publish-project-alist))) + +(defun yas--generate-html-batch () + (let ((org-publish-use-timestamps-flag nil) + (org-export-copy-to-kill-ring nil) + (org-confirm-babel-evaluate nil) + (make-backup-files nil)) + (org-publish "yasnippet" 'force))) + + + +(provide 'yas-doc-helper) +;;; yas-doc-helper.el ends here +;; Local Variables: +;; coding: utf-8 +;; End: diff --git a/scripts/emacs/yasnippet/snippets/text-mode/php-mode/setup b/scripts/emacs/yasnippet/snippets/text-mode/php-mode/setup new file mode 100644 index 0000000000000000000000000000000000000000..d20195ee2d4c7670273e102da2e641cae0cb4d48 --- /dev/null +++ b/scripts/emacs/yasnippet/snippets/text-mode/php-mode/setup @@ -0,0 +1,6 @@ +#contributor : Laurent Laffont <llaffont@afi-sa.fr> +#name : funct ...(...) +# -- +public function setUp() { + $0 +} \ No newline at end of file diff --git a/scripts/emacs/yasnippet/snippets/text-mode/php-mode/should b/scripts/emacs/yasnippet/snippets/text-mode/php-mode/should new file mode 100644 index 0000000000000000000000000000000000000000..26f5d62b8c5e8305e4701511c258322c6d29d60a --- /dev/null +++ b/scripts/emacs/yasnippet/snippets/text-mode/php-mode/should @@ -0,0 +1,7 @@ +#contributor : Laurent Laffont <laurent.laffont@gmail.com> +#name : should ...(...) +# -- +/** @test */ +public function ${context}Should${Expectation}() { + $0 +} \ No newline at end of file diff --git a/scripts/emacs/yasnippet/snippets/text-mode/php-mode/th b/scripts/emacs/yasnippet/snippets/text-mode/php-mode/th new file mode 100644 index 0000000000000000000000000000000000000000..f83ad3fdae7aa7786ee15b8d92ff63808500454b --- /dev/null +++ b/scripts/emacs/yasnippet/snippets/text-mode/php-mode/th @@ -0,0 +1,4 @@ +#contributor : Laurent Laffont <llaffont@afi-sa.fr> +#name : th +# -- +$this->$0 \ No newline at end of file diff --git a/scripts/emacs/yasnippet/yasnippet-debug.el b/scripts/emacs/yasnippet/yasnippet-debug.el new file mode 100644 index 0000000000000000000000000000000000000000..7e3be0ece22bd4c0d4d6c721fb90b424e849c22d --- /dev/null +++ b/scripts/emacs/yasnippet/yasnippet-debug.el @@ -0,0 +1,127 @@ +;;; yasnippet-debug.el --- debug functions for yasnippet + +;; Copyright (C) 2010 João Távora + +;; Author: João Távora +;; Keywords: emulations, convenience + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see <http://www.gnu.org/licenses/>. + +;;; Commentary: + +;; Just some debug functions + +;;; Code: + +(require 'yasnippet) + +(defun yas-debug-snippet-vars () + "Debug snippets, fields, mirrors and the `buffer-undo-list'." + (interactive) + (with-output-to-temp-buffer "*YASnippet trace*" + (princ "Interesting YASnippet vars: \n\n") + + (princ (format "\nPost command hook: %s\n" post-command-hook)) + (princ (format "\nPre command hook: %s\n" pre-command-hook)) + + (princ (format "%s live snippets in total\n" (length (yas--snippets-at-point (quote all-snippets))))) + (princ (format "%s overlays in buffer:\n\n" (length (overlays-in (point-min) (point-max))))) + (princ (format "%s live snippets at point:\n\n" (length (yas--snippets-at-point)))) + + + (dolist (snippet (yas--snippets-at-point)) + (princ (format "\tsid: %d control overlay from %d to %d\n" + (yas--snippet-id snippet) + (overlay-start (yas--snippet-control-overlay snippet)) + (overlay-end (yas--snippet-control-overlay snippet)))) + (princ (format "\tactive field: %d from %s to %s covering \"%s\"\n" + (yas--field-number (yas--snippet-active-field snippet)) + (marker-position (yas--field-start (yas--snippet-active-field snippet))) + (marker-position (yas--field-end (yas--snippet-active-field snippet))) + (buffer-substring-no-properties (yas--field-start (yas--snippet-active-field snippet)) (yas--field-end (yas--snippet-active-field snippet))))) + (when (yas--snippet-exit snippet) + (princ (format "\tsnippet-exit: at %s next: %s\n" + (yas--exit-marker (yas--snippet-exit snippet)) + (yas--exit-next (yas--snippet-exit snippet))))) + (dolist (field (yas--snippet-fields snippet)) + (princ (format "\tfield: %d from %s to %s covering \"%s\" next: %s%s\n" + (yas--field-number field) + (marker-position (yas--field-start field)) + (marker-position (yas--field-end field)) + (buffer-substring-no-properties (yas--field-start field) (yas--field-end field)) + (yas--debug-format-fom-concise (yas--field-next field)) + (if (yas--field-parent-field field) "(has a parent)" ""))) + (dolist (mirror (yas--field-mirrors field)) + (princ (format "\t\tmirror: from %s to %s covering \"%s\" next: %s\n" + (marker-position (yas--mirror-start mirror)) + (marker-position (yas--mirror-end mirror)) + (buffer-substring-no-properties (yas--mirror-start mirror) (yas--mirror-end mirror)) + (yas--debug-format-fom-concise (yas--mirror-next mirror))))))) + + (princ (format "\nUndo is %s and point-max is %s.\n" + (if (eq buffer-undo-list t) + "DISABLED" + "ENABLED") + (point-max))) + (unless (eq buffer-undo-list t) + (princ (format "Undpolist has %s elements. First 10 elements follow:\n" (length buffer-undo-list))) + (let ((first-ten (subseq buffer-undo-list 0 19))) + (dolist (undo-elem first-ten) + (princ (format "%2s: %s\n" (position undo-elem first-ten) (truncate-string-to-width (format "%s" undo-elem) 70)))))))) + +(defun yas--debug-format-fom-concise (fom) + (when fom + (cond ((yas--field-p fom) + (format "field %d from %d to %d" + (yas--field-number fom) + (marker-position (yas--field-start fom)) + (marker-position (yas--field-end fom)))) + ((yas--mirror-p fom) + (format "mirror from %d to %d" + (marker-position (yas--mirror-start fom)) + (marker-position (yas--mirror-end fom)))) + (t + (format "snippet exit at %d" + (marker-position (yas--fom-start fom))))))) + + +(defun yas-exterminate-package () + (interactive) + (yas-global-mode -1) + (yas-minor-mode -1) + (mapatoms #'(lambda (atom) + (when (string-match "yas[-/]" (symbol-name atom)) + (unintern atom obarray))))) + +(defun yas-debug-test (&optional quiet) + (interactive "P") + (yas-load-directory (or (and (listp yas-snippet-dirs) + (first yas-snippet-dirs)) + yas-snippet-dirs + "~/Source/yasnippet/snippets/")) + (set-buffer (switch-to-buffer "*YAS TEST*")) + (mapc #'yas--commit-snippet (yas--snippets-at-point 'all-snippets)) + (erase-buffer) + (setq buffer-undo-list nil) + (setq undo-in-progress nil) + (snippet-mode) + (yas-minor-mode 1) + (let ((abbrev)) + (setq abbrev "$f") + (insert abbrev)) + (unless quiet + (add-hook 'post-command-hook 'yas-debug-snippet-vars 't 'local))) + +(provide 'yasnippet-debug) +;;; yasnippet-debug.el ends here diff --git a/scripts/emacs/yasnippet/yasnippet-tests.el b/scripts/emacs/yasnippet/yasnippet-tests.el new file mode 100644 index 0000000000000000000000000000000000000000..d2f3c2cae9c684366a548c871a47669b9639fd40 --- /dev/null +++ b/scripts/emacs/yasnippet/yasnippet-tests.el @@ -0,0 +1,745 @@ +;;; yasnippet-tests.el --- some yasnippet tests + +;; Copyright (C) 2012 João Távora + +;; Author: João Távora <joaot@siscog.pt> +;; Keywords: emulations, convenience + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see <http://www.gnu.org/licenses/>. + +;;; Commentary: + +;; Test basic snippet mechanics and the loading system + +;;; Code: + +(require 'yasnippet) +(require 'ert) +(require 'ert-x) + + +;;; Snippet mechanics + +(defun yas--buffer-contents () + (buffer-substring-no-properties (point-min) (point-max))) + +(ert-deftest field-navigation () + (with-temp-buffer + (yas-minor-mode 1) + (yas-expand-snippet "${1:brother} from another ${2:mother}") + (should (string= (yas--buffer-contents) + "brother from another mother")) + + (should (looking-at "brother")) + (ert-simulate-command '(yas-next-field-or-maybe-expand)) + (should (looking-at "mother")) + (ert-simulate-command '(yas-prev-field)) + (should (looking-at "brother")))) + +(ert-deftest simple-mirror () + (with-temp-buffer + (yas-minor-mode 1) + (yas-expand-snippet "${1:brother} from another $1") + (should (string= (yas--buffer-contents) + "brother from another brother")) + (ert-simulate-command `(yas-mock-insert "bla")) + (should (string= (yas--buffer-contents) + "bla from another bla")))) + +(ert-deftest mirror-with-transformation () + (with-temp-buffer + (yas-minor-mode 1) + (yas-expand-snippet "${1:brother} from another ${1:$(upcase yas-text)}") + (should (string= (yas--buffer-contents) + "brother from another BROTHER")) + (ert-simulate-command `(yas-mock-insert "bla")) + (should (string= (yas--buffer-contents) + "bla from another BLA")))) + +(ert-deftest primary-field-transformation () + (with-temp-buffer + (yas-minor-mode 1) + (let ((snippet "${1:$$(upcase yas-text)}${1:$(concat \"bar\" yas-text)}")) + (yas-expand-snippet snippet) + (should (string= (yas--buffer-contents) "bar")) + (ert-simulate-command `(yas-mock-insert "foo")) + (should (string= (yas--buffer-contents) "FOObarFOO"))))) + +(ert-deftest nested-placeholders-kill-superfield () + (with-temp-buffer + (yas-minor-mode 1) + (yas-expand-snippet "brother from ${2:another ${3:mother}}!") + (should (string= (yas--buffer-contents) + "brother from another mother!")) + (ert-simulate-command `(yas-mock-insert "bla")) + (should (string= (yas--buffer-contents) + "brother from bla!")))) + +(ert-deftest nested-placeholders-use-subfield () + (with-temp-buffer + (yas-minor-mode 1) + (yas-expand-snippet "brother from ${2:another ${3:mother}}!") + (ert-simulate-command '(yas-next-field-or-maybe-expand)) + (ert-simulate-command `(yas-mock-insert "bla")) + (should (string= (yas--buffer-contents) + "brother from another bla!")))) + +(ert-deftest mirrors-adjacent-to-fields-with-nested-mirrors () + (with-temp-buffer + (yas-minor-mode 1) + (yas-expand-snippet "<%= f.submit \"${1:Submit}\"${2:$(and (yas-text) \", :disable_with => '\")}${2:$1ing...}${2:$(and (yas-text) \"'\")} %>") + (should (string= (yas--buffer-contents) + "<%= f.submit \"Submit\", :disable_with => 'Submiting...' %>")) + (ert-simulate-command `(yas-mock-insert "Send")) + (should (string= (yas--buffer-contents) + "<%= f.submit \"Send\", :disable_with => 'Sending...' %>")))) + +(ert-deftest deep-nested-mirroring-issue-351 () + (with-temp-buffer + (yas-minor-mode 1) + (yas-expand-snippet "${1:FOOOOOOO}${2:$1}${3:$2}${4:$3}") + (ert-simulate-command `(yas-mock-insert "abc")) + (should (string= (yas--buffer-contents) "abcabcabcabc")))) + +;; (ert-deftest in-snippet-undo () +;; (with-temp-buffer +;; (yas-minor-mode 1) +;; (yas-expand-snippet "brother from ${2:another ${3:mother}}!") +;; (ert-simulate-command '(yas-next-field-or-maybe-expand)) +;; (ert-simulate-command `(yas-mock-insert "bla")) +;; (ert-simulate-command '(undo)) +;; (should (string= (yas--buffer-contents) +;; "brother from another mother!")))) + + +;;; Snippet expansion and character escaping +;;; Thanks to @zw963 (Billy) for the testing +;;; +(ert-deftest escape-dollar () + (with-temp-buffer + (yas-minor-mode 1) + (yas-expand-snippet "bla\\${1:bla}ble") + (should (string= (yas--buffer-contents) "bla${1:bla}ble")))) + +(ert-deftest escape-closing-brace () + (with-temp-buffer + (yas-minor-mode 1) + (yas-expand-snippet "bla${1:bla\\}}ble") + (should (string= (yas--buffer-contents) "blabla}ble")) + (should (string= (yas-field-value 1) "bla}")))) + +(ert-deftest escape-backslashes () + (with-temp-buffer + (yas-minor-mode 1) + (yas-expand-snippet "bla\\ble") + (should (string= (yas--buffer-contents) "bla\\ble")))) + +(ert-deftest escape-backquotes () + (with-temp-buffer + (yas-minor-mode 1) + (yas-expand-snippet "bla`(upcase \"foo\\`bar\")`ble") + (should (string= (yas--buffer-contents) "blaFOO`BARble")))) + +(ert-deftest escape-some-elisp-with-strings () + "elisp with strings and unbalance parens inside it" + (with-temp-buffer + (yas-minor-mode 1) + ;; The rules here is: to output a literal `"' you need to escape + ;; it with one backslash. You don't need to escape them in + ;; embedded elisp. + (yas-expand-snippet "soon \\\"`(concat (upcase \"(my arms\")\"\\\" were all around her\")`") + (should (string= (yas--buffer-contents) "soon \"(MY ARMS\" were all around her")))) + +(ert-deftest escape-some-elisp-with-backslashes () + (with-temp-buffer + (yas-minor-mode 1) + ;; And the rule here is: to output a literal `\' inside a string + ;; inside embedded elisp you need a total of six `\' + (yas-expand-snippet "bla`(upcase \"hey\\\\\\yo\")`ble") + (should (string= (yas--buffer-contents) "blaHEY\\YOble")))) + +(ert-deftest be-careful-when-escaping-in-yas-selected-text () + (with-temp-buffer + (yas-minor-mode 1) + (let ((yas-selected-text "He\\\\o world!")) + (yas-expand-snippet "Look ma! `(yas-selected-text)`") + (should (string= (yas--buffer-contents) "Look ma! He\\\\o world!"))) + (yas-exit-all-snippets) + (erase-buffer) + (let ((yas-selected-text "He\"o world!")) + (yas-expand-snippet "Look ma! `(yas-selected-text)`") + (should (string= (yas--buffer-contents) "Look ma! He\"o world!"))) + (yas-exit-all-snippets) + (erase-buffer) + (let ((yas-selected-text "He\"\)\\o world!")) + (yas-expand-snippet "Look ma! `(yas-selected-text)`") + (should (string= (yas--buffer-contents) "Look ma! He\"\)\\o world!"))) + (yas-exit-all-snippets) + (erase-buffer))) + +(ert-deftest be-careful-when-escaping-in-yas-selected-text-2 () + (with-temp-buffer + (let ((yas-selected-text "He)}o world!")) + (yas-expand-snippet "Look ma! ${1:`(yas-selected-text)`} OK?") + (should (string= (yas--buffer-contents) "Look ma! He)}o world! OK?"))))) + +(ert-deftest example-for-issue-271 () + (with-temp-buffer + (yas-minor-mode 1) + (let ((yas-selected-text "aaa") + (snippet "if ${1:condition}\n`yas-selected-text`\nelse\n$3\nend")) + (yas-expand-snippet snippet) + (yas-next-field) + (ert-simulate-command `(yas-mock-insert "bbb")) + (should (string= (yas--buffer-contents) "if condition\naaa\nelse\nbbb\nend"))))) + +(ert-deftest example-for-issue-404 () + (with-temp-buffer + (c++-mode) + (yas-minor-mode 1) + (insert "#include <foo>\n") + (let ((snippet "main")) + (let ((yas-good-grace nil)) (yas-expand-snippet snippet)) + (should (string= (yas--buffer-contents) "#include <foo>\nmain"))))) + +(ert-deftest example-for-issue-404-c-mode () + (with-temp-buffer + (c-mode) + (yas-minor-mode 1) + (insert "#include <foo>\n") + (let ((snippet "main")) + (let ((yas-good-grace nil)) (yas-expand-snippet snippet)) + (should (string= (yas--buffer-contents) "#include <foo>\nmain"))))) + +(ert-deftest example-for-issue-404-external-emacs () + :tags '(:external) + (let ((fixture-el-file (make-temp-file "yas-404-fixture" nil ".el"))) + (with-temp-buffer + (insert (pp-to-string + `(condition-case _ + (progn + (require 'yasnippet-tests) + (yas-with-snippet-dirs + '((".emacs.d/snippets" + ("c-mode" + ("main" . "int main ()")))) + (yas-global-mode) + (switch-to-buffer "foo.c") + (c-mode) + (insert "#include <iostream>\nmain") + (setq yas-good-grace nil) + (yas-expand) + (should (string= (buffer-string) + "#include <iostream>\nint main ()")) + (kill-emacs 0))) + (error (kill-emacs -1))))) + (write-file fixture-el-file)) + (should (= 0 + (call-process (concat invocation-directory invocation-name) + nil nil nil + "-Q" "--batch" + "-L" "." "-l" fixture-el-file))))) + +(ert-deftest middle-of-buffer-snippet-insertion () + (with-temp-buffer + (yas-minor-mode 1) + (insert "beginning") + (save-excursion (insert "end")) + (let ((snippet "-middle-")) + (yas-expand-snippet snippet)) + (should (string= (yas--buffer-contents) "beginning-middle-end")))) + +(ert-deftest another-example-for-issue-271 () + ;; expect this to fail in batch mode since `region-active-p' doesn't + ;; used by `yas-expand-snippet' doesn't make sense in that context. + ;; + :expected-result (if noninteractive + :failed + :passed) + (with-temp-buffer + (yas-minor-mode 1) + (let ((snippet "\\${${1:1}:`yas-selected-text`}")) + (insert "aaabbbccc") + (set-mark 4) + (goto-char 7) + (yas-expand-snippet snippet) + (should (string= (yas--buffer-contents) "aaa${1:bbb}ccc"))))) + +(ert-deftest string-match-with-subregexp-in-embedded-elisp () + (with-temp-buffer + (yas-minor-mode 1) + ;; the rule here is: To use regexps in embedded `(elisp)` expressions, write + ;; it like you would normal elisp, i.e. no need to escape the backslashes. + (let ((snippet "`(if (string-match \"foo\\\\(ba+r\\\\)foo\" \"foobaaaaaaaaaarfoo\") + \"ok\" + \"fail\")`")) + (yas-expand-snippet snippet)) + (should (string= (yas--buffer-contents) "ok")))) + +(ert-deftest string-match-with-subregexp-in-mirror-transformations () + (with-temp-buffer + (yas-minor-mode 1) + ;; the rule here is: To use regexps in embedded `(elisp)` expressions, + ;; escape backslashes once, i.e. to use \\( \\) constructs, write \\\\( \\\\). + (let ((snippet "$1${1:$(if (string-match \"foo\\\\\\\\(ba+r\\\\\\\\)baz\" yas-text) + \"ok\" + \"fail\")}")) + (yas-expand-snippet snippet) + (should (string= (yas--buffer-contents) "fail")) + (ert-simulate-command `(yas-mock-insert "foobaaar")) + (should (string= (yas--buffer-contents) "foobaaarfail")) + (ert-simulate-command `(yas-mock-insert "baz")) + (should (string= (yas--buffer-contents) "foobaaarbazok"))))) + + +;;; Misc tests +;;; +(ert-deftest protection-overlay-no-cheating () + "Protection overlays at the very end of the buffer are dealt + with by cheatingly inserting a newline! + +TODO: correct this bug!" + :expected-result :failed + (with-temp-buffer + (yas-minor-mode 1) + (yas-expand-snippet "${2:brother} from another ${1:mother}") + (should (string= (yas--buffer-contents) + "brother from another mother") ;; no newline should be here! + ))) + +;;; Loading +;;; +(defun yas--call-with-temporary-redefinitions (function + &rest function-names-and-overriding-functions) + (let* ((overrides (remove-if-not #'(lambda (fdef) + (fboundp (first fdef))) + function-names-and-overriding-functions)) + (definition-names (mapcar #'first overrides)) + (overriding-functions (mapcar #'second overrides)) + (saved-functions (mapcar #'symbol-function definition-names))) + ;; saving all definitions before overriding anything ensures FDEFINITION + ;; errors don't cause accidental permanent redefinitions. + ;; + (cl-labels ((set-fdefinitions (names functions) + (loop for name in names + for fn in functions + do (fset name fn)))) + (set-fdefinitions definition-names overriding-functions) + (unwind-protect (funcall function) + (set-fdefinitions definition-names saved-functions))))) + +(defmacro yas--with-temporary-redefinitions (fdefinitions &rest body) + ;; "Temporarily (but globally) redefine each function in FDEFINITIONS. + ;; E.g.: (yas--with-temporary-redefinitions ((foo (x) ...) + ;; (bar (x) ...)) + ;; ;; code that eventually calls foo, bar of (setf foo) + ;; ...)" + ;; FIXME: This is hideous! Better use defadvice (or at least letf). + `(yas--call-with-temporary-redefinitions + (lambda () ,@body) + ,@(mapcar #'(lambda (thingy) + `(list ',(first thingy) + (lambda ,@(rest thingy)))) + fdefinitions))) + +(defmacro yas-with-overriden-buffer-list (&rest body) + (let ((saved-sym (make-symbol "yas--buffer-list"))) + `(let ((,saved-sym (symbol-function 'buffer-list))) + (yas--with-temporary-redefinitions + ((buffer-list () + (remove-if #'(lambda (buf) + (with-current-buffer buf + (eq major-mode 'lisp-interaction-mode))) + (funcall ,saved-sym)))) + ,@body)))) + + +(defmacro yas-with-some-interesting-snippet-dirs (&rest body) + `(yas-saving-variables + (yas-with-overriden-buffer-list + (yas-with-snippet-dirs + '((".emacs.d/snippets" + ("c-mode" + (".yas-parents" . "cc-mode") + ("printf" . "printf($1);")) ;; notice the overriding for issue #281 + ("emacs-lisp-mode" ("ert-deftest" . "(ert-deftest ${1:name} () $0)")) + ("lisp-interaction-mode" (".yas-parents" . "emacs-lisp-mode"))) + ("library/snippets" + ("c-mode" + (".yas-parents" . "c++-mode") + ("printf" . "printf")) + ("cc-mode" ("def" . "# define")) + ("emacs-lisp-mode" ("dolist" . "(dolist)")) + ("lisp-interaction-mode" ("sc" . "brother from another mother")))) + ,@body)))) + + +(ert-deftest basic-jit-loading () + "Test basic loading and expansion of snippets" + (yas-with-some-interesting-snippet-dirs + (yas-reload-all) + (yas--basic-jit-loading-1))) + +(ert-deftest basic-jit-loading-with-compiled-snippets () + "Test basic loading and expansion of snippets" + (yas-with-some-interesting-snippet-dirs + (yas-reload-all) + (yas-recompile-all) + (yas--with-temporary-redefinitions ((yas--load-directory-2 + (&rest _dummies) + (ert-fail "yas--load-directory-2 shouldn't be called when snippets have been compiled"))) + (yas-reload-all) + (yas--basic-jit-loading-1)))) + +(ert-deftest loading-with-cyclic-parenthood () + "Test loading when cyclic parenthood is setup." + (yas-saving-variables + (yas-with-snippet-dirs '((".emacs.d/snippets" + ("c-mode" + (".yas-parents" . "cc-mode")) + ("cc-mode" + (".yas-parents" . "yet-another-c-mode and-that-one")) + ("yet-another-c-mode" + (".yas-parents" . "c-mode and-also-this-one lisp-interaction-mode")))) + (yas-reload-all) + (with-temp-buffer + (let* ((major-mode 'c-mode) + (expected `(c-mode + cc-mode + yet-another-c-mode + and-also-this-one + and-that-one + ;; prog-mode doesn't exit in emacs 24.3 + ,@(if (fboundp 'prog-mode) + '(prog-mode)) + emacs-lisp-mode + lisp-interaction-mode)) + (observed (yas--modes-to-activate))) + (should (null (cl-set-exclusive-or expected observed))) + (should (= (length expected) + (length observed)))))))) + +(defun yas--basic-jit-loading-1 () + (with-temp-buffer + (should (= 4 (hash-table-count yas--scheduled-jit-loads))) + (should (= 0 (hash-table-count yas--tables))) + (lisp-interaction-mode) + (yas-minor-mode 1) + (should (= 2 (hash-table-count yas--scheduled-jit-loads))) + (should (= 2 (hash-table-count yas--tables))) + (should (= 1 (hash-table-count (yas--table-uuidhash (gethash 'lisp-interaction-mode yas--tables))))) + (should (= 2 (hash-table-count (yas--table-uuidhash (gethash 'emacs-lisp-mode yas--tables))))) + (yas-should-expand '(("sc" . "brother from another mother") + ("dolist" . "(dolist)") + ("ert-deftest" . "(ert-deftest name () )"))) + (c-mode) + (yas-minor-mode 1) + (should (= 0 (hash-table-count yas--scheduled-jit-loads))) + (should (= 4 (hash-table-count yas--tables))) + (should (= 1 (hash-table-count (yas--table-uuidhash (gethash 'c-mode yas--tables))))) + (should (= 1 (hash-table-count (yas--table-uuidhash (gethash 'cc-mode yas--tables))))) + (yas-should-expand '(("printf" . "printf();") + ("def" . "# define"))) + (yas-should-not-expand '("sc" "dolist" "ert-deftest")))) + + +;;; Menu +;;; +(defmacro yas-with-even-more-interesting-snippet-dirs (&rest body) + `(yas-saving-variables + (yas-with-snippet-dirs + `((".emacs.d/snippets" + ("c-mode" + (".yas-make-groups" . "") + ("printf" . "printf($1);") + ("foo-group-a" + ("fnprintf" . "fprintf($1);") + ("snprintf" . "snprintf($1);")) + ("foo-group-b" + ("strcmp" . "strecmp($1);") + ("strcasecmp" . "strcasecmp($1);"))) + ("lisp-interaction-mode" + ("ert-deftest" . "# group: barbar\n# --\n(ert-deftest ${1:name} () $0)")) + ("fancy-mode" + ("a-guy" . "# uuid: 999\n# --\nyo!") + ("a-sir" . "# uuid: 12345\n# --\nindeed!") + ("a-lady" . "# uuid: 54321\n# --\noh-la-la!") + ("a-beggar" . "# uuid: 0101\n# --\narrrgh!") + ("an-outcast" . "# uuid: 666\n# --\narrrgh!") + (".yas-setup.el" . , (pp-to-string + '(yas-define-menu 'fancy-mode + '((yas-ignore-item "0101") + (yas-item "999") + (yas-submenu "sirs" + ((yas-item "12345"))) + (yas-submenu "ladies" + ((yas-item "54321")))) + '("666"))))))) + ,@body))) + +(ert-deftest test-yas-define-menu () + (let ((yas-use-menu t)) + (yas-with-even-more-interesting-snippet-dirs + (yas-reload-all 'no-jit) + (let ((menu (cdr (gethash 'fancy-mode yas--menu-table)))) + (should (eql 4 (length menu))) + (dolist (item '("a-guy" "a-beggar")) + (should (find item menu :key #'third :test #'string=))) + (should-not (find "an-outcast" menu :key #'third :test #'string=)) + (dolist (submenu '("sirs" "ladies")) + (should (keymapp + (fourth + (find submenu menu :key #'third :test #'string=))))) + )))) + +(ert-deftest test-group-menus () + "Test group-based menus using .yas-make-groups and the group directive" + (let ((yas-use-menu t)) + (yas-with-even-more-interesting-snippet-dirs + (yas-reload-all 'no-jit) + ;; first the subdir-based groups + ;; + (let ((menu (cdr (gethash 'c-mode yas--menu-table)))) + (should (eql 3 (length menu))) + (dolist (item '("printf" "foo-group-a" "foo-group-b")) + (should (find item menu :key #'third :test #'string=))) + (dolist (submenu '("foo-group-a" "foo-group-b")) + (should (keymapp + (fourth + (find submenu menu :key #'third :test #'string=)))))) + ;; now group directives + ;; + (let ((menu (cdr (gethash 'lisp-interaction-mode yas--menu-table)))) + (should (eql 1 (length menu))) + (should (find "barbar" menu :key #'third :test #'string=)) + (should (keymapp + (fourth + (find "barbar" menu :key #'third :test #'string=)))))))) + +(ert-deftest test-group-menus-twisted () + "Same as similarly named test, but be mean. + +TODO: be meaner" + (let ((yas-use-menu t)) + (yas-with-even-more-interesting-snippet-dirs + ;; add a group directive conflicting with the subdir and watch + ;; behaviour + (with-temp-buffer + (insert "# group: foo-group-c\n# --\nstrecmp($1)") + (write-region nil nil (concat (first (yas-snippet-dirs)) + "/c-mode/foo-group-b/strcmp"))) + (yas-reload-all 'no-jit) + (let ((menu (cdr (gethash 'c-mode yas--menu-table)))) + (should (eql 4 (length menu))) + (dolist (item '("printf" "foo-group-a" "foo-group-b" "foo-group-c")) + (should (find item menu :key #'third :test #'string=))) + (dolist (submenu '("foo-group-a" "foo-group-b" "foo-group-c")) + (should (keymapp + (fourth + (find submenu menu :key #'third :test #'string=)))))) + ;; delete the .yas-make-groups file and watch behaviour + ;; + (delete-file (concat (first (yas-snippet-dirs)) + "/c-mode/.yas-make-groups")) + (yas-reload-all 'no-jit) + (let ((menu (cdr (gethash 'c-mode yas--menu-table)))) + (should (eql 5 (length menu)))) + ;; Change a group directive and reload + ;; + (let ((menu (cdr (gethash 'lisp-interaction-mode yas--menu-table)))) + (should (find "barbar" menu :key #'third :test #'string=))) + + (with-temp-buffer + (insert "# group: foofoo\n# --\n(ert-deftest ${1:name} () $0)") + (write-region nil nil (concat (first (yas-snippet-dirs)) + "/lisp-interaction-mode/ert-deftest"))) + (yas-reload-all 'no-jit) + (let ((menu (cdr (gethash 'lisp-interaction-mode yas--menu-table)))) + (should (eql 1 (length menu))) + (should (find "foofoo" menu :key #'third :test #'string=)) + (should (keymapp + (fourth + (find "foofoo" menu :key #'third :test #'string=)))))))) + + +;;; The infamous and problematic tab keybinding +;;; +(ert-deftest test-yas-tab-binding () + (with-temp-buffer + (yas-minor-mode -1) + (should (not (eq (key-binding (yas--read-keybinding "<tab>")) 'yas-expand))) + (yas-minor-mode 1) + (should (eq (key-binding (yas--read-keybinding "<tab>")) 'yas-expand)) + (yas-expand-snippet "$1 $2 $3") + (should (eq (key-binding [(tab)]) 'yas-next-field-or-maybe-expand)) + (should (eq (key-binding (kbd "TAB")) 'yas-next-field-or-maybe-expand)) + (should (eq (key-binding [(shift tab)]) 'yas-prev-field)) + (should (eq (key-binding [backtab]) 'yas-prev-field)))) + +(ert-deftest test-rebindings () + (unwind-protect + (progn + (define-key yas-minor-mode-map [tab] nil) + (define-key yas-minor-mode-map (kbd "TAB") nil) + (define-key yas-minor-mode-map (kbd "SPC") 'yas-expand) + (with-temp-buffer + (yas-minor-mode 1) + (should (not (eq (key-binding (yas--read-keybinding "TAB")) 'yas-expand))) + (should (eq (key-binding (yas--read-keybinding "SPC")) 'yas-expand)) + (yas-reload-all) + (should (not (eq (key-binding (yas--read-keybinding "TAB")) 'yas-expand))) + (should (eq (key-binding (yas--read-keybinding "SPC")) 'yas-expand)))) + (setcdr yas-minor-mode-map (cdr (yas--init-minor-keymap))))) + +(ert-deftest test-yas-in-org () + (with-temp-buffer + (org-mode) + (yas-minor-mode 1) + (should (eq (key-binding [(tab)]) 'yas-expand)) + (should (eq (key-binding (kbd "TAB")) 'yas-expand)))) + +(ert-deftest test-yas-activate-extra-modes () + "Given a symbol, `yas-activate-extra-mode' should be able to +add the snippets associated with the given mode." + (with-temp-buffer + (yas-saving-variables + (yas-with-snippet-dirs + '((".emacs.d/snippets" + ("markdown-mode" + ("_" . "_Text_ ")) + ("emacs-lisp-mode" + ("car" . "(car )")))) + (yas-reload-all) + (emacs-lisp-mode) + (yas-minor-mode-on) + (yas-activate-extra-mode 'markdown-mode) + (should (eq 'markdown-mode (car yas--extra-modes))) + (yas-should-expand '(("_" . "_Text_ "))) + (yas-should-expand '(("car" . "(car )"))) + (yas-deactivate-extra-mode 'markdown-mode) + (should-not (eq 'markdown-mode (car yas--extra-modes))) + (yas-should-not-expand '("_")) + (yas-should-expand '(("car" . "(car )"))))))) + + +;;; Helpers +;;; +(defun yas-should-expand (keys-and-expansions) + (dolist (key-and-expansion keys-and-expansions) + (yas-exit-all-snippets) + (erase-buffer) + (insert (car key-and-expansion)) + (let ((yas-fallback-behavior nil)) + (ert-simulate-command '(yas-expand))) + (should (string= (yas--buffer-contents) (cdr key-and-expansion)))) + (yas-exit-all-snippets)) + +(defun yas-should-not-expand (keys) + (dolist (key keys) + (yas-exit-all-snippets) + (erase-buffer) + (insert key) + (let ((yas-fallback-behavior nil)) + (ert-simulate-command '(yas-expand))) + (should (string= (yas--buffer-contents) key)))) + +(defun yas-mock-insert (string) + (interactive) + (do ((i 0 (1+ i))) + ((= i (length string))) + (insert (aref string i)))) + +(defun yas-make-file-or-dirs (ass) + (let ((file-or-dir-name (car ass)) + (content (cdr ass))) + (cond ((listp content) + (make-directory file-or-dir-name 'parents) + (let ((default-directory (concat default-directory "/" file-or-dir-name))) + (mapc #'yas-make-file-or-dirs content))) + ((stringp content) + (with-temp-buffer + (insert content) + (write-region nil nil file-or-dir-name nil 'nomessage))) + (t + (message "[yas] oops don't know this content"))))) + + +(defun yas-variables () + (let ((syms)) + (mapatoms #'(lambda (sym) + (if (and (string-match "^yas-[^/]" (symbol-name sym)) + (boundp sym)) + (push sym syms)))) + syms)) + +(defun yas-call-with-saving-variables (fn) + (let* ((vars (yas-variables)) + (saved-values (mapcar #'symbol-value vars))) + (unwind-protect + (funcall fn) + (loop for var in vars + for saved in saved-values + do (set var saved))))) + +(defmacro yas-saving-variables (&rest body) + `(yas-call-with-saving-variables #'(lambda () ,@body))) + + +(defun yas-call-with-snippet-dirs (dirs fn) + (let* ((default-directory (make-temp-file "yasnippet-fixture" t)) + (yas-snippet-dirs (mapcar #'car dirs))) + (with-temp-message "" + (unwind-protect + (progn + (mapc #'yas-make-file-or-dirs dirs) + (funcall fn)) + (when (>= emacs-major-version 24) + (delete-directory default-directory 'recursive)))))) + +(defmacro yas-with-snippet-dirs (dirs &rest body) + (declare (indent defun)) + `(yas-call-with-snippet-dirs ,dirs + #'(lambda () + ,@body))) + +;;; Older emacsen +;;; +(unless (fboundp 'special-mode) + ;; FIXME: Why provide this default definition here?!? + (defalias 'special-mode 'fundamental)) + +;;; btw to test this in emacs22 mac osx: +;;; curl -L -O https://github.com/mirrors/emacs/raw/master/lisp/emacs-lisp/ert.el +;;; curl -L -O https://github.com/mirrors/emacs/raw/master/lisp/emacs-lisp/ert-x.el +;;; /usr/bin/emacs -nw -Q -L . -l yasnippet-tests.el --batch -e ert + + +(put 'yas-saving-variables 'edebug-form-spec t) +(put 'yas-with-snippet-dirs 'edebug-form-spec t) +(put 'yas-with-overriden-buffer-list 'edebug-form-spec t) +(put 'yas-with-some-interesting-snippet-dirs 'edebug-form-spec t) + + +(put 'yas--with-temporary-redefinitions 'lisp-indent-function 1) +(put 'yas--with-temporary-redefinitions 'edebug-form-spec '((&rest (defun*)) cl-declarations body)) + + + + +(provide 'yasnippet-tests) +;;; yasnippet-tests.el ends here +;; Local Variables: +;; lexical-binding: t +;; byte-compile-warnings: (not cl-functions) +;; End: diff --git a/scripts/emacs/yasnippet/yasnippet.el b/scripts/emacs/yasnippet/yasnippet.el new file mode 100644 index 0000000000000000000000000000000000000000..269d0080d9f1d6fa8e1f463fe4441619f7d8b3af --- /dev/null +++ b/scripts/emacs/yasnippet/yasnippet.el @@ -0,0 +1,4593 @@ +;;; yasnippet.el --- Yet another snippet extension for Emacs. + +;; Copyright (C) 2008-2013 Free Software Foundation, Inc. +;; Authors: pluskid <pluskid@gmail.com>, João Távora <joaotavora@gmail.com> +;; Maintainer: João Távora <joaotavora@gmail.com> +;; Version: 0.8.1 +;; Package-version: 0.8.0 +;; X-URL: http://github.com/capitaomorte/yasnippet +;; Keywords: convenience, emulation +;; URL: http://github.com/capitaomorte/yasnippet +;; EmacsWiki: YaSnippetMode + +;; This program is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see <http://www.gnu.org/licenses/>. + +;;; Commentary: +;; +;; Basic steps to setup: +;; +;; (add-to-list 'load-path +;; "~/path-to-yasnippet") +;; (require 'yasnippet) +;; (yas-global-mode 1) +;; +;; +;; Interesting variables are: +;; +;; `yas-snippet-dirs' +;; +;; The directory where user-created snippets are to be +;; stored. Can also be a list of directories. In that case, +;; when used for bulk (re)loading of snippets (at startup or +;; via `yas-reload-all'), directories appearing earlier in +;; the list shadow other dir's snippets. Also, the first +;; directory is taken as the default for storing the user's +;; new snippets. +;; +;; The deprecated `yas/root-directory' aliases this variable +;; for backward-compatibility. +;; +;; +;; Major commands are: +;; +;; M-x yas-expand +;; +;; Try to expand snippets before point. In `yas-minor-mode', +;; this is normally bound to TAB, but you can customize it in +;; `yas-minor-mode-map'. +;; +;; M-x yas-load-directory +;; +;; Prompts you for a directory hierarchy of snippets to load. +;; +;; M-x yas-activate-extra-mode +;; +;; Prompts you for an extra mode to add snippets for in the +;; current buffer. +;; +;; M-x yas-insert-snippet +;; +;; Prompts you for possible snippet expansion if that is +;; possible according to buffer-local and snippet-local +;; expansion conditions. With prefix argument, ignore these +;; conditions. +;; +;; M-x yas-visit-snippet-file +;; +;; Prompts you for possible snippet expansions like +;; `yas-insert-snippet', but instead of expanding it, takes +;; you directly to the snippet definition's file, if it +;; exists. +;; +;; M-x yas-new-snippet +;; +;; Lets you create a new snippet file in the correct +;; subdirectory of `yas-snippet-dirs', according to the +;; active major mode. +;; +;; M-x yas-load-snippet-buffer +;; +;; When editing a snippet, this loads the snippet. This is +;; bound to "C-c C-c" while in the `snippet-mode' editing +;; mode. +;; +;; M-x yas-tryout-snippet +;; +;; When editing a snippet, this opens a new empty buffer, +;; sets it to the appropriate major mode and inserts the +;; snippet there, so you can see what it looks like. This is +;; bound to "C-c C-t" while in `snippet-mode'. +;; +;; M-x yas-describe-tables +;; +;; Lists known snippets in a separate buffer. User is +;; prompted as to whether only the currently active tables +;; are to be displayed, or all the tables for all major +;; modes. +;; +;; If you have `dropdown-list' installed, you can optionally use it +;; as the preferred "prompting method", putting in your .emacs file, +;; for example: +;; +;; (require 'dropdown-list) +;; (setq yas-prompt-functions '(yas-dropdown-prompt +;; yas-ido-prompt +;; yas-completing-prompt)) +;; +;; Also check out the customization group +;; +;; M-x customize-group RET yasnippet RET +;; +;; If you use the customization group to set variables +;; `yas-snippet-dirs' or `yas-global-mode', make sure the path to +;; "yasnippet.el" is present in the `load-path' *before* the +;; `custom-set-variables' is executed in your .emacs file. +;; +;; For more information and detailed usage, refer to the project page: +;; http://github.com/capitaomorte/yasnippet + +;;; Code: + +(require 'cl) +(eval-and-compile + (require 'cl-lib)) +(require 'easymenu) +(require 'help-mode) + +(defvar yas--editing-template) +(defvar yas--guessed-modes) +(defvar yas--indent-original-column) +(defvar yas--scheduled-jit-loads) +(defvar yas-keymap) +(defvar yas-selected-text) +(defvar yas-verbosity) +(defvar yas--current-template) + + +;;; User customizable variables + +(defgroup yasnippet nil + "Yet Another Snippet extension" + :group 'editing) + +(defvar yas--load-file-name load-file-name + "Store the filename that yasnippet.el was originally loaded from.") + +(defcustom yas-snippet-dirs (remove nil + (list "~/.emacs.d/snippets" + (when yas--load-file-name + (concat (file-name-directory yas--load-file-name) "snippets")))) + "Directory or list of snippet dirs for each major mode. + +The directory where user-created snippets are to be stored. Can +also be a list of directories. In that case, when used for +bulk (re)loading of snippets (at startup or via +`yas-reload-all'), directories appearing earlier in the list +shadow other dir's snippets. Also, the first directory is taken +as the default for storing the user's new snippets." + :type '(choice (string :tag "Single directory (string)") + (repeat :args (string) :tag "List of directories (strings)")) + :group 'yasnippet + :require 'yasnippet + :set #'(lambda (symbol new) + (let ((old (and (boundp symbol) + (symbol-value symbol)))) + (set-default symbol new) + (unless (or (not (fboundp 'yas-reload-all)) + (equal old new)) + (yas-reload-all))))) + +(defun yas-snippet-dirs () + "Return `yas-snippet-dirs' (which see) as a list." + (if (listp yas-snippet-dirs) yas-snippet-dirs (list yas-snippet-dirs))) + +(defvaralias 'yas/root-directory 'yas-snippet-dirs) + +(defcustom yas-new-snippet-default "\ +# -*- mode: snippet -*- +# name: $1 +# key: ${2:${1:$(yas--key-from-desc yas-text)}}${3: +# binding: ${4:direct-keybinding}}${5: +# expand-env: ((${6:some-var} ${7:some-value}))}${8: +# type: command} +# -- +$0" + "Default snippet to use when creating a new snippet. +If nil, don't use any snippet." + :type 'string + :group 'yasnippet) + +(defcustom yas-prompt-functions '(yas-x-prompt + yas-dropdown-prompt + yas-completing-prompt + yas-ido-prompt + yas-no-prompt) + "Functions to prompt for keys, templates, etc interactively. + +These functions are called with the following arguments: + +- PROMPT: A string to prompt the user + +- CHOICES: a list of strings or objects. + +- optional DISPLAY-FN : A function that, when applied to each of +the objects in CHOICES will return a string. + +The return value of any function you put here should be one of +the objects in CHOICES, properly formatted with DISPLAY-FN (if +that is passed). + +- To signal that your particular style of prompting is +unavailable at the moment, you can also have the function return +nil. + +- To signal that the user quit the prompting process, you can +signal `quit' with + + (signal 'quit \"user quit!\")." + :type '(repeat function) + :group 'yasnippet) + +(defcustom yas-indent-line 'auto + "Controls indenting applied to a recent snippet expansion. + +The following values are possible: + +- `fixed' Indent the snippet to the current column; + +- `auto' Indent each line of the snippet with `indent-according-to-mode' + +Every other value means don't apply any snippet-side indentation +after expansion (the manual per-line \"$>\" indentation still +applies)." + :type '(choice (const :tag "Nothing" nothing) + (const :tag "Fixed" fixed) + (const :tag "Auto" auto)) + :group 'yasnippet) + +(defcustom yas-also-auto-indent-first-line nil + "Non-nil means also auto indent first line according to mode. + +Naturally this is only valid when `yas-indent-line' is `auto'" + :type 'boolean + :group 'yasnippet) + +(defcustom yas-snippet-revival t + "Non-nil means re-activate snippet fields after undo/redo." + :type 'boolean + :group 'yasnippet) + +(defcustom yas-triggers-in-field nil + "If non-nil, allow stacked expansions (snippets inside snippets). + +Otherwise `yas-next-field-or-maybe-expand' just moves on to the +next field" + :type 'boolean + :group 'yasnippet) + +(defcustom yas-fallback-behavior 'call-other-command + "How to act when `yas-expand' does *not* expand a snippet. + +- `call-other-command' means try to temporarily disable YASnippet + and call the next command bound to whatever key was used to + invoke `yas-expand'. + +- nil or the symbol `return-nil' mean do nothing. (and + `yas-expand' returns nil) + +- A Lisp form (apply COMMAND . ARGS) means interactively call + COMMAND, if ARGS is non-nil, call COMMAND non-interactively + with ARGS as arguments." + :type '(choice (const :tag "Call previous command" call-other-command) + (const :tag "Do nothing" return-nil)) + :group 'yasnippet) + +(defcustom yas-choose-keys-first nil + "If non-nil, prompt for snippet key first, then for template. + +Otherwise prompts for all possible snippet names. + +This affects `yas-insert-snippet' and `yas-visit-snippet-file'." + :type 'boolean + :group 'yasnippet) + +(defcustom yas-choose-tables-first nil + "If non-nil, and multiple eligible snippet tables, prompts user for tables first. + +Otherwise, user chooses between the merging together of all +eligible tables. + +This affects `yas-insert-snippet', `yas-visit-snippet-file'" + :type 'boolean + :group 'yasnippet) + +(defcustom yas-use-menu 'abbreviate + "Display a YASnippet menu in the menu bar. + +When non-nil, submenus for each snippet table will be listed +under the menu \"Yasnippet\". + +- If set to `abbreviate', only the current major-mode +menu and the modes set in `yas--extra-modes' are listed. + +- If set to `full', every submenu is listed + +- It set to nil, don't display a menu at all (this requires a + `yas-reload-all' call if the menu is already visible). + +Any other non-nil value, every submenu is listed." + :type '(choice (const :tag "Full" full) + (const :tag "Abbreviate" abbreviate) + (const :tag "No menu" nil)) + :group 'yasnippet) + +(defcustom yas-trigger-symbol (or (and (eq window-system 'mac) + (ignore-errors + (char-to-string ?\x21E5))) ;; little ->| sign + " =>") + "The text that will be used in menu to represent the trigger." + :type 'string + :group 'yasnippet) + +(defcustom yas-wrap-around-region nil + "If non-nil, snippet expansion wraps around selected region. + +The wrapping occurs just before the snippet's exit marker. This +can be overridden on a per-snippet basis." + :type 'boolean + :group 'yasnippet) + +(defcustom yas-good-grace t + "If non-nil, don't raise errors in inline elisp evaluation. + +An error string \"[yas] error\" is returned instead." + :type 'boolean + :group 'yasnippet) + +(defcustom yas-visit-from-menu nil + "If non-nil visit snippets's files from menu, instead of expanding them. + +This can only work when snippets are loaded from files." + :type 'boolean + :group 'yasnippet) + +(defcustom yas-expand-only-for-last-commands nil + "List of `last-command' values to restrict tab-triggering to, or nil. + +Leave this set at nil (the default) to be able to trigger an +expansion simply by placing the cursor after a valid tab trigger, +using whichever commands. + +Optionally, set this to something like '(self-insert-command) if +you to wish restrict expansion to only happen when the last +letter of the snippet tab trigger was typed immediately before +the trigger key itself." + :type '(repeat function) + :group 'yasnippet) + +;; Only two faces, and one of them shouldn't even be used... +;; +(defface yas-field-highlight-face + '((t (:inherit 'region))) + "The face used to highlight the currently active field of a snippet" + :group 'yasnippet) + +(defface yas--field-debug-face + '() + "The face used for debugging some overlays normally hidden" + :group 'yasnippet) + + +;;; User-visible variables + +(defvar yas-keymap (let ((map (make-sparse-keymap))) + (define-key map [(tab)] 'yas-next-field-or-maybe-expand) + (define-key map (kbd "TAB") 'yas-next-field-or-maybe-expand) + (define-key map [(shift tab)] 'yas-prev-field) + (define-key map [backtab] 'yas-prev-field) + (define-key map (kbd "C-g") 'yas-abort-snippet) + (define-key map (kbd "C-d") 'yas-skip-and-clear-or-delete-char) + map) + "The active keymap while a snippet expansion is in progress.") + +(defvar yas-key-syntaxes (list "w" "w_" "w_." "w_.()" "^ ") + "List of character syntaxes used to find a trigger key before point. +The list is tried in the order while scanning characters +backwards from point. For example, if the list is '(\"w\" \"w_\") +first look for trigger keys which are composed exclusively of +\"word\"-syntax characters, and then, if that fails, look for +keys which are either of \"word\" or \"symbol\" +syntax. Triggering after + +foo-bar + +will, according to the \"w\" element first try \"bar\". If that +isn't a trigger key, \"foo-bar\" is tried, respecting a second +\"w_\" element.") + +(defvar yas-after-exit-snippet-hook + '() + "Hooks to run after a snippet exited. + +The hooks will be run in an environment where some variables bound to +proper values: + +`yas-snippet-beg' : The beginning of the region of the snippet. + +`yas-snippet-end' : Similar to beg. + +Attention: These hooks are not run when exiting nested/stacked snippet expansion!") + +(defvar yas-before-expand-snippet-hook + '() + "Hooks to run just before expanding a snippet.") + +(defvar yas-buffer-local-condition + '(if (and (or (fourth (syntax-ppss)) + (fifth (syntax-ppss))) + this-command + (eq this-command 'yas-expand-from-trigger-key)) + '(require-snippet-condition . force-in-comment) + t) + "Snippet expanding condition. + +This variable is a Lisp form which is evaluated every time a +snippet expansion is attempted: + + * If it evaluates to nil, no snippets can be expanded. + + * If it evaluates to the a cons (require-snippet-condition + . REQUIREMENT) + + * Snippets bearing no \"# condition:\" directive are not + considered + + * Snippets bearing conditions that evaluate to nil (or + produce an error) won't be considered. + + * If the snippet has a condition that evaluates to non-nil + RESULT: + + * If REQUIREMENT is t, the snippet is considered + + * If REQUIREMENT is `eq' RESULT, the snippet is + considered + + * Otherwise, the snippet is not considered. + + * If it evaluates to the symbol 'always, all snippets are + considered for expansion, regardless of any conditions. + + * If it evaluates to t or some other non-nil value + + * Snippet bearing no conditions, or conditions that + evaluate to non-nil, are considered for expansion. + + * Otherwise, the snippet is not considered. + +Here's an example preventing snippets from being expanded from +inside comments, in `python-mode' only, with the exception of +snippets returning the symbol 'force-in-comment in their +conditions. + + (add-hook 'python-mode-hook + '(lambda () + (setq yas-buffer-local-condition + '(if (python-in-string/comment) + '(require-snippet-condition . force-in-comment) + t)))) + +The default value is similar, it filters out potential snippet +expansions inside comments and string literals, unless the +snippet itself contains a condition that returns the symbol +`force-in-comment'.") + + +;;; Internal variables + +(defvar yas--version "0.8.0beta") + +(defvar yas--menu-table (make-hash-table) + "A hash table of MAJOR-MODE symbols to menu keymaps.") + +(defvar yas--known-modes + '(ruby-mode rst-mode markdown-mode) + "A list of mode which is well known but not part of Emacs.") + +(defvar yas--escaped-characters + '(?\\ ?` ?\" ?' ?$ ?} ?{ ?\( ?\)) + "List of characters which *might* need to be escaped.") + +(defconst yas--field-regexp + "${\\([0-9]+:\\)?\\([^}]*\\)}" + "A regexp to *almost* recognize a field.") + +(defconst yas--multi-dollar-lisp-expression-regexp + "$+[ \t\n]*\\(([^)]*)\\)" + "A regexp to *almost* recognize a \"$(...)\" expression.") + +(defconst yas--backquote-lisp-expression-regexp + "`\\([^`]*\\)`" + "A regexp to recognize a \"`lisp-expression`\" expression." ) + +(defconst yas--transform-mirror-regexp + "${\\(?:\\([0-9]+\\):\\)?$\\([ \t\n]*([^}]*\\)" + "A regexp to *almost* recognize a mirror with a transform.") + +(defconst yas--simple-mirror-regexp + "$\\([0-9]+\\)" + "A regexp to recognize a simple mirror.") + +(defvar yas--snippet-id-seed 0 + "Contains the next id for a snippet.") + +(defun yas--snippet-next-id () + (let ((id yas--snippet-id-seed)) + (incf yas--snippet-id-seed) + id)) + + +;;; Minor mode stuff + +;; XXX: `last-buffer-undo-list' is somehow needed in Carbon Emacs for MacOSX +(defvar last-buffer-undo-list nil) + +(defvar yas--minor-mode-menu nil + "Holds the YASnippet menu.") + +(defun yas--init-minor-keymap () + "Set up the `yas-minor-mode' keymap." + (let ((map (make-sparse-keymap))) + (when yas-use-menu + (easy-menu-define yas--minor-mode-menu + map + "Menu used when `yas-minor-mode' is active." + '("YASnippet" + "----" + ["Expand trigger" yas-expand + :help "Possibly expand tab trigger before point"] + ["Insert at point..." yas-insert-snippet + :help "Prompt for an expandable snippet and expand it at point"] + ["New snippet..." yas-new-snippet + :help "Create a new snippet in an appropriate directory"] + ["Visit snippet file..." yas-visit-snippet-file + :help "Prompt for an expandable snippet and find its file"] + "----" + ("Snippet menu behaviour" + ["Visit snippets" (setq yas-visit-from-menu t) + :help "Visit snippets from the menu" + :active t :style radio :selected yas-visit-from-menu] + ["Expand snippets" (setq yas-visit-from-menu nil) + :help "Expand snippets from the menu" + :active t :style radio :selected (not yas-visit-from-menu)] + "----" + ["Show all known modes" (setq yas-use-menu 'full) + :help "Show one snippet submenu for each loaded table" + :active t :style radio :selected (eq yas-use-menu 'full)] + ["Abbreviate according to current mode" (setq yas-use-menu 'abbreviate) + :help "Show only snippet submenus for the current active modes" + :active t :style radio :selected (eq yas-use-menu 'abbreviate)]) + ("Indenting" + ["Auto" (setq yas-indent-line 'auto) + :help "Indent each line of the snippet with `indent-according-to-mode'" + :active t :style radio :selected (eq yas-indent-line 'auto)] + ["Fixed" (setq yas-indent-line 'fixed) + :help "Indent the snippet to the current column" + :active t :style radio :selected (eq yas-indent-line 'fixed)] + ["None" (setq yas-indent-line 'none) + :help "Don't apply any particular snippet indentation after expansion" + :active t :style radio :selected (not (member yas-indent-line '(fixed auto)))] + "----" + ["Also auto indent first line" (setq yas-also-auto-indent-first-line + (not yas-also-auto-indent-first-line)) + :help "When auto-indenting also, auto indent the first line menu" + :active (eq yas-indent-line 'auto) + :style toggle :selected yas-also-auto-indent-first-line] + ) + ("Prompting method" + ["System X-widget" (setq yas-prompt-functions + (cons 'yas-x-prompt + (remove 'yas-x-prompt + yas-prompt-functions))) + :help "Use your windowing system's (gtk, mac, windows, etc...) default menu" + :active t :style radio :selected (eq (car yas-prompt-functions) + 'yas-x-prompt)] + ["Dropdown-list" (setq yas-prompt-functions + (cons 'yas-dropdown-prompt + (remove 'yas-dropdown-prompt + yas-prompt-functions))) + :help "Use a special dropdown list" + :active t :style radio :selected (eq (car yas-prompt-functions) + 'yas-dropdown-prompt)] + ["Ido" (setq yas-prompt-functions + (cons 'yas-ido-prompt + (remove 'yas-ido-prompt + yas-prompt-functions))) + :help "Use an ido-style minibuffer prompt" + :active t :style radio :selected (eq (car yas-prompt-functions) + 'yas-ido-prompt)] + ["Completing read" (setq yas-prompt-functions + (cons 'yas-completing-prompt + (remove 'yas-completing-prompt + yas-prompt-functions))) + :help "Use a normal minibuffer prompt" + :active t :style radio :selected (eq (car yas-prompt-functions) + 'yas-completing-prompt)] + ) + ("Misc" + ["Wrap region in exit marker" + (setq yas-wrap-around-region + (not yas-wrap-around-region)) + :help "If non-nil automatically wrap the selected text in the $0 snippet exit" + :style toggle :selected yas-wrap-around-region] + ["Allow stacked expansions " + (setq yas-triggers-in-field + (not yas-triggers-in-field)) + :help "If non-nil allow snippets to be triggered inside other snippet fields" + :style toggle :selected yas-triggers-in-field] + ["Revive snippets on undo " + (setq yas-snippet-revival + (not yas-snippet-revival)) + :help "If non-nil allow snippets to become active again after undo" + :style toggle :selected yas-snippet-revival] + ["Good grace " + (setq yas-good-grace + (not yas-good-grace)) + :help "If non-nil don't raise errors in bad embedded elisp in snippets" + :style toggle :selected yas-good-grace] + ) + "----" + ["Load snippets..." yas-load-directory + :help "Load snippets from a specific directory"] + ["Reload everything" yas-reload-all + :help "Cleanup stuff, reload snippets, rebuild menus"] + ["About" yas-about + :help "Display some information about YASnippet"]))) + + ;; Now for the stuff that has direct keybindings + ;; + (define-key map [(tab)] 'yas-expand) + (define-key map (kbd "TAB") 'yas-expand) + (define-key map "\C-c&\C-s" 'yas-insert-snippet) + (define-key map "\C-c&\C-n" 'yas-new-snippet) + (define-key map "\C-c&\C-v" 'yas-visit-snippet-file) + map)) + +(defvar yas-minor-mode-map (yas--init-minor-keymap) + "The keymap used when `yas-minor-mode' is active.") + +(defvar yas--extra-modes nil + "An internal list of modes for which to also lookup snippets. + +This variable probably makes more sense as buffer-local, so +ensure your use `make-local-variable' when you set it.") +(define-obsolete-variable-alias 'yas-extra-modes 'yas--extra-modes "0.8.1") + +(defvar yas--tables (make-hash-table) + "A hash table of mode symbols to `yas--table' objects.") + +(defvar yas--parents (make-hash-table) + "A hash table of mode symbols do lists of direct parent mode symbols. + +This list is populated when reading the \".yas-parents\" files +found when traversing snippet directories with +`yas-load-directory'. + +There might be additional parenting information stored in the +`derived-mode-parent' property of some mode symbols, but that is +not recorded here.") + +(defvar yas--direct-keymaps (list) + "Keymap alist supporting direct snippet keybindings. + +This variable is placed in `emulation-mode-map-alists'. + +Its elements looks like (TABLE-NAME . KEYMAP). They're +instantiated on `yas-reload-all' but KEYMAP is added to only when +loading snippets. `yas--direct-TABLE-NAME' is then a variable set +buffer-locally when entering `yas-minor-mode'. KEYMAP binds all +defined direct keybindings to the command +`yas-expand-from-keymap' which then which snippet to expand.") + +(defun yas-direct-keymaps-reload () + "Force reload the direct keybinding for active snippet tables." + (interactive) + (setq yas--direct-keymaps nil) + (maphash #'(lambda (name table) + (push (cons (intern (format "yas--direct-%s" name)) + (yas--table-direct-keymap table)) + yas--direct-keymaps)) + yas--tables)) + +(defun yas--modes-to-activate () + "Compute list of mode symbols that are active for `yas-expand' +and friends." + (let (dfs) + (setq dfs (lambda (mode &optional explored) + (push mode explored) + (cons mode + (loop for neighbour + in (remove nil (cons (get mode + 'derived-mode-parent) + (gethash mode yas--parents))) + + unless (memq neighbour explored) + append (funcall dfs neighbour explored))))) + (remove-duplicates (append yas--extra-modes + (funcall dfs major-mode))))) + +(defvar yas-minor-mode-hook nil + "Hook run when `yas-minor-mode' is turned on.") + +;;;###autoload +(define-minor-mode yas-minor-mode + "Toggle YASnippet mode. + +When YASnippet mode is enabled, `yas-expand', normally bound to +the TAB key, expands snippets of code depending on the major +mode. + +With no argument, this command toggles the mode. +positive prefix argument turns on the mode. +Negative prefix argument turns off the mode. + +Key bindings: +\\{yas-minor-mode-map}" + nil + ;; The indicator for the mode line. + " yas" + :group 'yasnippet + (cond (yas-minor-mode + ;; Install the direct keymaps in `emulation-mode-map-alists' + ;; (we use `add-hook' even though it's not technically a hook, + ;; but it works). Then define variables named after modes to + ;; index `yas--direct-keymaps'. + ;; + ;; Also install the post-command-hook. + ;; + (add-hook 'emulation-mode-map-alists 'yas--direct-keymaps) + (add-hook 'post-command-hook 'yas--post-command-handler nil t) + ;; Set the `yas--direct-%s' vars for direct keymap expansion + ;; + (dolist (mode (yas--modes-to-activate)) + (let ((name (intern (format "yas--direct-%s" mode)))) + (set-default name nil) + (set (make-local-variable name) t))) + ;; Perform JIT loads + ;; + (yas--load-pending-jits)) + (t + ;; Uninstall the direct keymaps and the post-command hook + ;; + (remove-hook 'post-command-hook 'yas--post-command-handler t) + (remove-hook 'emulation-mode-map-alists 'yas--direct-keymaps)))) + +(defun yas-activate-extra-mode (mode) + "Activates the snippets for the given `mode' in the buffer. + +The function can be called in the hook of a minor mode to +activate snippets associated with that mode." + (interactive + (let (modes + symbol) + (maphash (lambda (k _) + (setq modes (cons (list k) modes))) + yas--parents) + (setq symbol (completing-read + "Activate mode: " modes nil t)) + (list + (when (not (string= "" symbol)) + (intern symbol))))) + (when mode + (add-to-list (make-local-variable 'yas--extra-modes) mode) + (yas--load-pending-jits))) + +(defun yas-deactivate-extra-mode (mode) + "Deactivates the snippets for the given `mode' in the buffer." + (interactive + (list (intern + (completing-read + "Deactivate mode: " (mapcar #'list yas--extra-modes) nil t)))) + (set (make-local-variable 'yas--extra-modes) + (remove mode + yas--extra-modes))) + +(defvar yas-dont-activate '(minibufferp) + "If non-nil don't let `yas-global-mode' affect some buffers. + +If a function of zero arguments, then its result is used. + +If a list of functions, then all functions must return nil to +activate yas for this buffer. + +In Emacsen <= 23, this variable is buffer-local. Because +`yas-minor-mode-on' is called by `yas-global-mode' after +executing the buffer's major mode hook, setting this variable +there is an effective way to define exceptions to the \"global\" +activation behaviour. + +In Emacsen > 23, only the global value is used. To define +per-mode exceptions to the \"global\" activation behaviour, call +`yas-minor-mode' with a negative argument directily in the major +mode's hook.") +(unless (> emacs-major-version 23) + (with-no-warnings + (make-variable-buffer-local 'yas-dont-activate))) + + +(defun yas-minor-mode-on () + "Turn on YASnippet minor mode. + +Honour `yas-dont-activate', which see." + (interactive) + ;; Check `yas-dont-activate' + (unless (cond ((functionp yas-dont-activate) + (funcall yas-dont-activate)) + ((consp yas-dont-activate) + (some #'funcall yas-dont-activate)) + (yas-dont-activate)) + (yas-minor-mode 1))) + +;;;###autoload +(define-globalized-minor-mode yas-global-mode yas-minor-mode yas-minor-mode-on + :group 'yasnippet + :require 'yasnippet) + +(defun yas--global-mode-reload-with-jit-maybe () + "Run `yas-reload-all' when `yas-global-mode' is on." + (when yas-global-mode (yas-reload-all))) + +(add-hook 'yas-global-mode-hook 'yas--global-mode-reload-with-jit-maybe) + + +;;; Major mode stuff + +(defvar yas--font-lock-keywords + (append '(("^#.*$" . font-lock-comment-face)) + lisp-font-lock-keywords-2 + '(("$\\([0-9]+\\)" + (0 font-lock-keyword-face) + (1 font-lock-string-face t)) + ("${\\([0-9]+\\):?" + (0 font-lock-keyword-face) + (1 font-lock-warning-face t)) + ("${" . font-lock-keyword-face) + ("$[0-9]+?" . font-lock-preprocessor-face) + ("\\(\\$(\\)" 1 font-lock-preprocessor-face) + ("}" + (0 font-lock-keyword-face))))) + +(defvar snippet-mode-map + (let ((map (make-sparse-keymap))) + (easy-menu-define nil + map + "Menu used when snippet-mode is active." + (cons "Snippet" + (mapcar #'(lambda (ent) + (when (third ent) + (define-key map (third ent) (second ent))) + (vector (first ent) (second ent) t)) + '(("Load this snippet" yas-load-snippet-buffer "\C-c\C-l") + ("Load and quit window" yas-load-snippet-buffer-and-close "\C-c\C-c") + ("Try out this snippet" yas-tryout-snippet "\C-c\C-t"))))) + map) + "The keymap used when `snippet-mode' is active.") + + +(define-derived-mode snippet-mode text-mode "Snippet" + "A mode for editing yasnippets" + (setq font-lock-defaults '(yas--font-lock-keywords)) + (set (make-local-variable 'require-final-newline) nil) + (set (make-local-variable 'comment-start) "#") + (set (make-local-variable 'comment-start-skip) "#+[\t ]*")) + + + +;;; Internal structs for template management + +(defstruct (yas--template (:constructor yas--make-blank-template)) + "A template for a snippet." + key + content + name + condition + expand-env + file + keybinding + uuid + menu-binding-pair + group ;; as dictated by the #group: directive or .yas-make-groups + perm-group ;; as dictated by `yas-define-menu' + table + ) + +(defun yas--populate-template (template &rest args) + "Helper function to populate TEMPLATE with properties." + (while args + (aset template + (position (intern (substring (symbol-name (car args)) 1)) + (mapcar #'car (get 'yas--template 'cl-struct-slots))) + (second args)) + (setq args (cddr args))) + template) + +(defstruct (yas--table (:constructor yas--make-snippet-table (name))) + "A table to store snippets for a particular mode. + +Has the following fields: + +`yas--table-name' + + A symbol name normally corresponding to a major mode, but can + also be a pseudo major-mode to be used in + `yas-activate-extra-mode', for example. + +`yas--table-hash' + + A hash table (KEY . NAMEHASH), known as the \"keyhash\". KEY is + a string or a vector, where the former is the snippet's trigger + and the latter means it's a direct keybinding. NAMEHASH is yet + another hash of (NAME . TEMPLATE) where NAME is the snippet's + name and TEMPLATE is a `yas--template' object. + +`yas--table-direct-keymap' + + A keymap for the snippets in this table that have direct + keybindings. This is kept in sync with the keyhash, i.e., all + the elements of the keyhash that are vectors appear here as + bindings to `yas-expand-from-keymap'. + +`yas--table-uuidhash' + + A hash table mapping snippets uuid's to the same `yas--template' + objects. A snippet uuid defaults to the snippet's name." + name + (hash (make-hash-table :test 'equal)) + (uuidhash (make-hash-table :test 'equal)) + (parents nil) + (direct-keymap (make-sparse-keymap))) + +(defun yas--get-template-by-uuid (mode uuid) + "Find the snippet template in MODE by its UUID." + (let* ((table (gethash mode yas--tables mode))) + (when table + (gethash uuid (yas--table-uuidhash table))))) + +;; Apropos storing/updating in TABLE, this works in two steps: +;; +;; 1. `yas--remove-template-by-uuid' removes any +;; keyhash-namehash-template mappings from TABLE, grabbing the +;; snippet by its uuid. Also removes mappings from TABLE's +;; `yas--table-direct-keymap' (FIXME: and should probably take care +;; of potentially stale menu bindings right?.) +;; +;; 2. `yas--add-template' adds this all over again. +;; +;; Create a new or add to an existing keyhash-namehash mapping. +;; +;; For reference on understanding this, consider three snippet +;; definitions: +;; +;; A: # name: The Foo +;; # key: foo +;; # binding: C-c M-l +;; +;; B: # name: Mrs Foo +;; # key: foo +;; +;; C: # name: The Bar +;; # binding: C-c M-l +;; +;; D: # name: Baz +;; # key: baz +;; +;; keyhash namehashes(3) yas--template structs(4) +;; ----------------------------------------------------- +;; __________ +;; / \ +;; "foo" ---> "The Foo" ---> [yas--template A] | +;; "Mrs Foo" ---> [yas--template B] | +;; | +;; [C-c M-l] ---> "The Foo" -------------------------/ +;; "The Bar" ---> [yas--template C] +;; +;; "baz" ---> "Baz" ---> [yas--template D] +;; +;; Additionally, since uuid defaults to the name, we have a +;; `yas--table-uuidhash' for TABLE +;; +;; uuidhash yas--template structs +;; ------------------------------- +;; "The Foo" ---> [yas--template A] +;; "Mrs Foo" ---> [yas--template B] +;; "The Bar" ---> [yas--template C] +;; "Baz" ---> [yas--template D] +;; +;; FIXME: the more I look at this data-structure the more I think I'm +;; stupid. There has to be an easier way (but beware lots of code +;; depends on this). +;; +(defun yas--remove-template-by-uuid (table uuid) + "Remove from TABLE a template identified by UUID." + (let ((template (gethash uuid (yas--table-uuidhash table)))) + (when template + (let* ((name (yas--template-name template)) + (empty-keys nil)) + ;; Remove the name from each of the targeted namehashes + ;; + (maphash #'(lambda (k v) + (let ((template (gethash name v))) + (when (and template + (eq uuid (yas--template-uuid template))) + (remhash name v) + (when (zerop (hash-table-count v)) + (push k empty-keys))))) + (yas--table-hash table)) + ;; Remove the namehash themselves if they've become empty + ;; + (dolist (key empty-keys) + (when (vectorp key) + (define-key (yas--table-direct-keymap table) key nil)) + (remhash key (yas--table-hash table))) + + ;; Finally, remove the uuid from the uuidhash + ;; + (remhash uuid (yas--table-uuidhash table)))))) + +(defun yas--add-template (table template) + "Store in TABLE the snippet template TEMPLATE. + +KEY can be a string (trigger key) of a vector (direct +keybinding)." + (let ((name (yas--template-name template)) + (key (yas--template-key template)) + (keybinding (yas--template-keybinding template)) + (_menu-binding-pair (yas--template-menu-binding-pair-get-create template))) + (dolist (k (remove nil (list key keybinding))) + (puthash name + template + (or (gethash k + (yas--table-hash table)) + (puthash k + (make-hash-table :test 'equal) + (yas--table-hash table)))) + (when (vectorp k) + (define-key (yas--table-direct-keymap table) k 'yas-expand-from-keymap))) + + ;; Update TABLE's `yas--table-uuidhash' + (puthash (yas--template-uuid template) + template + (yas--table-uuidhash table)))) + +(defun yas--update-template (table template) + "Add or update TEMPLATE in TABLE. + +Also takes care of adding and updating to the associated menu." + ;; Remove from table by uuid + ;; + (yas--remove-template-by-uuid table (yas--template-uuid template)) + ;; Add to table again + ;; + (yas--add-template table template) + ;; Take care of the menu + ;; + (when yas-use-menu + (yas--update-template-menu table template))) + +(defun yas--update-template-menu (table template) + "Update every menu-related for TEMPLATE." + (let ((menu-binding-pair (yas--template-menu-binding-pair-get-create template)) + (key (yas--template-key template)) + (keybinding (yas--template-keybinding template))) + ;; The snippet might have changed name or keys, so update + ;; user-visible strings + ;; + (unless (eq (cdr menu-binding-pair) :none) + ;; the menu item name + ;; + (setf (cadar menu-binding-pair) (yas--template-name template)) + ;; the :keys information (also visible to the user) + (setf (getf (cdr (car menu-binding-pair)) :keys) + (or (and keybinding (key-description keybinding)) + (and key (concat key yas-trigger-symbol)))))) + (unless (yas--template-menu-managed-by-yas-define-menu template) + (let ((menu-keymap + (yas--menu-keymap-get-create (yas--table-mode table) + (mapcar #'yas--table-mode + (yas--table-parents table)))) + (group (yas--template-group template))) + ;; Remove from menu keymap + ;; + (assert menu-keymap) + (yas--delete-from-keymap menu-keymap (yas--template-uuid template)) + + ;; Add necessary subgroups as necessary. + ;; + (dolist (subgroup group) + (let ((subgroup-keymap (lookup-key menu-keymap (vector (make-symbol subgroup))))) + (unless (and subgroup-keymap + (keymapp subgroup-keymap)) + (setq subgroup-keymap (make-sparse-keymap)) + (define-key menu-keymap (vector (make-symbol subgroup)) + `(menu-item ,subgroup ,subgroup-keymap))) + (setq menu-keymap subgroup-keymap))) + + ;; Add this entry to the keymap + ;; + (define-key menu-keymap + (vector (make-symbol (yas--template-uuid template))) + (car (yas--template-menu-binding-pair template)))))) + +(defun yas--namehash-templates-alist (namehash) + "Return NAMEHASH as an alist." + (let (alist) + (maphash #'(lambda (k v) + (push (cons k v) alist)) + namehash) + alist)) + +(defun yas--fetch (table key) + "Fetch templates in TABLE by KEY. + +Return a list of cons (NAME . TEMPLATE) where NAME is a +string and TEMPLATE is a `yas--template' structure." + (let* ((keyhash (yas--table-hash table)) + (namehash (and keyhash (gethash key keyhash)))) + (when namehash + (yas--filter-templates-by-condition (yas--namehash-templates-alist namehash))))) + + +;;; Filtering/condition logic + +(defun yas--eval-condition (condition) + (condition-case err + (save-excursion + (save-restriction + (save-match-data + (eval condition)))) + (error (progn + (yas--message 1 "Error in condition evaluation: %s" (error-message-string err)) + nil)))) + + +(defun yas--filter-templates-by-condition (templates) + "Filter the templates using the applicable condition. + +TEMPLATES is a list of cons (NAME . TEMPLATE) where NAME is a +string and TEMPLATE is a `yas--template' structure. + +This function implements the rules described in +`yas-buffer-local-condition'. See that variables documentation." + (let ((requirement (yas--require-template-specific-condition-p))) + (if (eq requirement 'always) + templates + (remove-if-not #'(lambda (pair) + (yas--template-can-expand-p + (yas--template-condition (cdr pair)) requirement)) + templates)))) + +(defun yas--require-template-specific-condition-p () + "Decide if this buffer requests/requires snippet-specific +conditions to filter out potential expansions." + (if (eq 'always yas-buffer-local-condition) + 'always + (let ((local-condition (or (and (consp yas-buffer-local-condition) + (yas--eval-condition yas-buffer-local-condition)) + yas-buffer-local-condition))) + (when local-condition + (if (eq local-condition t) + t + (and (consp local-condition) + (eq 'require-snippet-condition (car local-condition)) + (symbolp (cdr local-condition)) + (cdr local-condition))))))) + +(defun yas--template-can-expand-p (condition requirement) + "Evaluate CONDITION and REQUIREMENT and return a boolean." + (let* ((result (or (null condition) + (yas--eval-condition condition)))) + (cond ((eq requirement t) + result) + (t + (eq requirement result))))) + +(defun yas--table-templates (table) + (when table + (let ((acc (list))) + (maphash #'(lambda (_key namehash) + (maphash #'(lambda (name template) + (push (cons name template) acc)) + namehash)) + (yas--table-hash table)) + (yas--filter-templates-by-condition acc)))) + +(defun yas--current-key () + "Get the key under current position. +A key is used to find the template of a snippet in the current snippet-table." + (let ((start (point)) + (end (point)) + (syntaxes yas-key-syntaxes) + syntax + done + templates) + (while (and (not done) syntaxes) + (setq syntax (car syntaxes)) + (setq syntaxes (cdr syntaxes)) + (save-excursion + (skip-syntax-backward syntax) + (setq start (point))) + (setq templates + (mapcan #'(lambda (table) + (yas--fetch table (buffer-substring-no-properties start end))) + (yas--get-snippet-tables))) + (if templates + (setq done t) + (setq start end))) + (list templates + start + end))) + + +(defun yas--table-all-keys (table) + "Get trigger keys of all active snippets in TABLE." + (let ((acc)) + (maphash #'(lambda (key namehash) + (when (yas--filter-templates-by-condition (yas--namehash-templates-alist namehash)) + (push key acc))) + (yas--table-hash table)) + acc)) + +(defun yas--table-mode (table) + (intern (yas--table-name table))) + + +;;; Internal functions and macros: + +(defun yas--real-mode? (mode) + "Try to find out if MODE is a real mode. + +The MODE bound to a function (like `c-mode') is considered real +mode. Other well known mode like `ruby-mode' which is not part of +Emacs might not bound to a function until it is loaded. So +yasnippet keeps a list of modes like this to help the judgment." + (or (fboundp mode) + (find mode yas--known-modes))) + +(defun yas--eval-lisp (form) + "Evaluate FORM and convert the result to string." + (let ((retval (catch 'yas--exception + (condition-case err + (save-excursion + (save-restriction + (save-match-data + (widen) + (let ((result (eval form))) + (when result + (format "%s" result)))))) + (error (if yas-good-grace + (yas--format "elisp error! %s" (error-message-string err)) + (error (yas--format "elisp error: %s" + (error-message-string err))))))))) + (when (and (consp retval) + (eq 'yas--exception (car retval))) + (error (cdr retval))) + retval)) + +(defun yas--eval-lisp-no-saves (form) + (condition-case err + (eval form) + (error (if yas-good-grace + (yas--format "elisp error! %s" (error-message-string err)) + (error (yas--format "elisp error: %s" + (error-message-string err))))))) + +(defun yas--read-lisp (string &optional nil-on-error) + "Read STRING as a elisp expression and return it. + +In case STRING in an invalid expression and NIL-ON-ERROR is nil, +return an expression that when evaluated will issue an error." + (condition-case err + (read string) + (error (and (not nil-on-error) + `(error (error-message-string ,err)))))) + +(defun yas--read-keybinding (keybinding) + "Read KEYBINDING as a snippet keybinding, return a vector." + (when (and keybinding + (not (string-match "keybinding" keybinding))) + (condition-case err + (let ((res (or (and (string-match "^\\[.*\\]$" keybinding) + (read keybinding)) + (read-kbd-macro keybinding 'need-vector)))) + res) + (error + (yas--message 3 "warning: keybinding \"%s\" invalid since %s." + keybinding (error-message-string err)) + nil)))) + +(defun yas--table-get-create (mode) + "Get or create the snippet table corresponding to MODE." + (let ((table (gethash mode + yas--tables))) + (unless table + (setq table (yas--make-snippet-table (symbol-name mode))) + (puthash mode table yas--tables) + (push (cons (intern (format "yas--direct-%s" mode)) + (yas--table-direct-keymap table)) + yas--direct-keymaps)) + table)) + +(defun yas--get-snippet-tables () + "Get snippet tables for current buffer. + +Return a list of `yas--table' objects. The list of modes to +consider is returned by `yas--modes-to-activate'" + (remove nil + (mapcar #'(lambda (name) + (gethash name yas--tables)) + (yas--modes-to-activate)))) + +(defun yas--menu-keymap-get-create (mode &optional parents) + "Get or create the menu keymap for MODE and its PARENTS. + +This may very well create a plethora of menu keymaps and arrange +them all in `yas--menu-table'" + (let* ((menu-keymap (or (gethash mode yas--menu-table) + (puthash mode (make-sparse-keymap) yas--menu-table)))) + (mapc #'yas--menu-keymap-get-create parents) + (define-key yas--minor-mode-menu (vector mode) + `(menu-item ,(symbol-name mode) ,menu-keymap + :visible (yas--show-menu-p ',mode))) + menu-keymap)) + + +(defmacro yas--called-interactively-p (&optional kind) + "A backward-compatible version of `called-interactively-p'. + +Optional KIND is as documented at `called-interactively-p' +in GNU Emacs 24.1 or higher." + (if (string< emacs-version "24.1") + '(called-interactively-p) + `(called-interactively-p ,kind))) + + +;;; Template-related and snippet loading functions + +(defun yas--parse-template (&optional file) + "Parse the template in the current buffer. + +Optional FILE is the absolute file name of the file being +parsed. + +Optional GROUP is the group where the template is to go, +otherwise we attempt to calculate it from FILE. + +Return a snippet-definition, i.e. a list + + (KEY TEMPLATE NAME CONDITION GROUP VARS FILE KEYBINDING UUID) + +If the buffer contains a line of \"# --\" then the contents above +this line are ignored. Directives can set most of these with the syntax: + +# directive-name : directive-value + +Here's a list of currently recognized directives: + + * type + * name + * contributor + * condition + * group + * key + * expand-env + * binding + * uuid" + (goto-char (point-min)) + (let* ((type 'snippet) + (name (and file + (file-name-nondirectory file))) + (key nil) + template + bound + condition + (group (and file + (yas--calculate-group file))) + expand-env + binding + uuid) + (if (re-search-forward "^# --\n" nil t) + (progn (setq template + (buffer-substring-no-properties (point) + (point-max))) + (setq bound (point)) + (goto-char (point-min)) + (while (re-search-forward "^# *\\([^ ]+?\\) *: *\\(.*\\)$" bound t) + (when (string= "uuid" (match-string-no-properties 1)) + (setq uuid (match-string-no-properties 2))) + (when (string= "type" (match-string-no-properties 1)) + (setq type (if (string= "command" (match-string-no-properties 2)) + 'command + 'snippet))) + (when (string= "key" (match-string-no-properties 1)) + (setq key (match-string-no-properties 2))) + (when (string= "name" (match-string-no-properties 1)) + (setq name (match-string-no-properties 2))) + (when (string= "condition" (match-string-no-properties 1)) + (setq condition (yas--read-lisp (match-string-no-properties 2)))) + (when (string= "group" (match-string-no-properties 1)) + (setq group (match-string-no-properties 2))) + (when (string= "expand-env" (match-string-no-properties 1)) + (setq expand-env (yas--read-lisp (match-string-no-properties 2) + 'nil-on-error))) + (when (string= "binding" (match-string-no-properties 1)) + (setq binding (match-string-no-properties 2))))) + (setq template + (buffer-substring-no-properties (point-min) (point-max)))) + (unless (or key binding) + (setq key (and file (file-name-nondirectory file)))) + (when (eq type 'command) + (setq template (yas--read-lisp (concat "(progn" template ")")))) + (when group + (setq group (split-string group "\\."))) + (list key template name condition group expand-env file binding uuid))) + +(defun yas--calculate-group (file) + "Calculate the group for snippet file path FILE." + (let* ((dominating-dir (locate-dominating-file file + ".yas-make-groups")) + (extra-path (and dominating-dir + (replace-regexp-in-string (concat "^" + (expand-file-name dominating-dir)) + "" + (expand-file-name file)))) + (extra-dir (and extra-path + (file-name-directory extra-path))) + (group (and extra-dir + (replace-regexp-in-string "/" + "." + (directory-file-name extra-dir))))) + group)) + +(defun yas--subdirs (directory &optional filep) + "Return subdirs or files of DIRECTORY according to FILEP." + (remove-if (lambda (file) + (or (string-match "^\\." + (file-name-nondirectory file)) + (string-match "^#.*#$" + (file-name-nondirectory file)) + (string-match "~$" + (file-name-nondirectory file)) + (if filep + (file-directory-p file) + (not (file-directory-p file))))) + (directory-files directory t))) + +(defun yas--make-menu-binding (template) + (let ((mode (yas--table-mode (yas--template-table template)))) + `(lambda () (interactive) (yas--expand-or-visit-from-menu ',mode ,(yas--template-uuid template))))) + +(defun yas--expand-or-visit-from-menu (mode uuid) + (let* ((table (yas--table-get-create mode)) + (yas--current-template (and table + (gethash uuid (yas--table-uuidhash table))))) + (when yas--current-template + (if yas-visit-from-menu + (yas--visit-snippet-file-1 yas--current-template) + (let ((where (if (region-active-p) + (cons (region-beginning) (region-end)) + (cons (point) (point))))) + (yas-expand-snippet (yas--template-content yas--current-template) + (car where) + (cdr where) + (yas--template-expand-env yas--current-template))))))) + +(defun yas--key-from-desc (text) + "Return a yasnippet key from a description string TEXT." + (replace-regexp-in-string "\\(\\w+\\).*" "\\1" text)) + + +;;; Popping up for keys and templates + +(defvar yas--x-pretty-prompt-templates nil + "If non-nil, attempt to prompt for templates like TextMate.") + + +(defun yas--prompt-for-template (templates &optional prompt) + "Interactively choose a template from the list TEMPLATES. + +TEMPLATES is a list of `yas--template'. + +Optional PROMPT sets the prompt to use." + (when templates + (setq templates + (sort templates #'(lambda (t1 t2) + (< (length (yas--template-name t1)) + (length (yas--template-name t2)))))) + (if yas--x-pretty-prompt-templates + (yas--x-pretty-prompt-templates "Choose a snippet" templates) + (some #'(lambda (fn) + (funcall fn (or prompt "Choose a snippet: ") + templates + #'yas--template-name)) + yas-prompt-functions)))) + +(defun yas--prompt-for-keys (keys &optional prompt) + "Interactively choose a template key from the list KEYS. + +Optional PROMPT sets the prompt to use." + (when keys + (some #'(lambda (fn) + (funcall fn (or prompt "Choose a snippet key: ") keys)) + yas-prompt-functions))) + +(defun yas--prompt-for-table (tables &optional prompt) + "Interactively choose a table from the list TABLES. + +Optional PROMPT sets the prompt to use." + (when tables + (some #'(lambda (fn) + (funcall fn (or prompt "Choose a snippet table: ") + tables + #'yas--table-name)) + yas-prompt-functions))) + +(defun yas-x-prompt (prompt choices &optional display-fn) + "Display choices in a x-window prompt." + ;; FIXME: HACK: if we notice that one of the objects in choices is + ;; actually a `yas--template', defer to `yas--x-prompt-pretty-templates' + ;; + ;; This would be better implemented by passing CHOICES as a + ;; structured tree rather than a list. Modifications would go as far + ;; up as `yas--all-templates' I think. + ;; + (when (and window-system choices) + (let ((chosen + (let (menu d) ;; d for display + (dolist (c choices) + (setq d (or (and display-fn (funcall display-fn c)) + c)) + (cond ((stringp d) + (push (cons (concat " " d) c) menu)) + ((listp d) + (push (car d) menu)))) + (setq menu (list prompt (push "title" menu))) + (x-popup-menu (if (fboundp 'posn-at-point) + (let ((x-y (posn-x-y (posn-at-point (point))))) + (list (list (+ (car x-y) 10) + (+ (cdr x-y) 20)) + (selected-window))) + t) + menu)))) + (or chosen + (keyboard-quit))))) + +(defun yas--x-pretty-prompt-templates (prompt templates) + "Display TEMPLATES, grouping neatly by table name." + (let ((organized (make-hash-table :test #'equal)) + menu + more-than-one-table + prefix) + (dolist (tl templates) + (puthash (yas--template-table tl) + (cons tl + (gethash (yas--template-table tl) organized)) + organized)) + (setq more-than-one-table (> (hash-table-count organized) 1)) + (setq prefix (if more-than-one-table + " " "")) + (if more-than-one-table + (maphash #'(lambda (table templates) + (push (yas--table-name table) menu) + (dolist (tl templates) + (push (cons (concat prefix (yas--template-name tl)) tl) menu))) organized) + (setq menu (mapcar #'(lambda (tl) (cons (concat prefix (yas--template-name tl)) tl)) templates))) + + (setq menu (nreverse menu)) + (or (x-popup-menu (if (fboundp 'posn-at-point) + (let ((x-y (posn-x-y (posn-at-point (point))))) + (list (list (+ (car x-y) 10) + (+ (cdr x-y) 20)) + (selected-window))) + t) + (list prompt (push "title" menu))) + (keyboard-quit)))) + +(defun yas-ido-prompt (prompt choices &optional display-fn) + (when (and (fboundp 'ido-completing-read) + (or (>= emacs-major-version 24) + ido-mode)) + (yas-completing-prompt prompt choices display-fn #'ido-completing-read))) + +(defun yas-dropdown-prompt (_prompt choices &optional display-fn) + (when (fboundp 'dropdown-list) + (let (formatted-choices + filtered-choices + d + n) + (dolist (choice choices) + (setq d (or (and display-fn (funcall display-fn choice)) + choice)) + (when (stringp d) + (push d formatted-choices) + (push choice filtered-choices))) + + (setq n (and formatted-choices (dropdown-list formatted-choices))) + (if n + (nth n filtered-choices) + (keyboard-quit))))) + +(defun yas-completing-prompt (prompt choices &optional display-fn completion-fn) + (let (formatted-choices + filtered-choices + chosen + d + (completion-fn (or completion-fn + #'completing-read))) + (dolist (choice choices) + (setq d (or (and display-fn (funcall display-fn choice)) + choice)) + (when (stringp d) + (push d formatted-choices) + (push choice filtered-choices))) + (setq chosen (and formatted-choices + (funcall completion-fn prompt + formatted-choices + nil + 'require-match + nil + nil))) + (let ((position (or (and chosen + (position chosen formatted-choices :test #'string=)) + 0))) + (nth position filtered-choices)))) + +(defun yas-no-prompt (_prompt choices &optional _display-fn) + (first choices)) + + +;;; Defining snippets +;; This consists of creating and registering `yas--template' objects in the +;; correct tables. +;; + +(defvar yas--creating-compiled-snippets nil) + +(defun yas--define-snippets-1 (snippet snippet-table) + "Helper for `yas-define-snippets'." + ;; X) Calculate some more defaults on the values returned by + ;; `yas--parse-template'. + ;; + (let* ((file (seventh snippet)) + (key (car snippet)) + (name (or (third snippet) + (and file + (file-name-directory file)))) + (condition (fourth snippet)) + (group (fifth snippet)) + (keybinding (yas--read-keybinding (eighth snippet))) + (uuid (or (ninth snippet) + name)) + (template (or (gethash uuid (yas--table-uuidhash snippet-table)) + (yas--make-blank-template)))) + ;; X) populate the template object + ;; + (yas--populate-template template + :table snippet-table + :key key + :content (second snippet) + :name (or name key) + :group group + :condition condition + :expand-env (sixth snippet) + :file (seventh snippet) + :keybinding keybinding + :uuid uuid) + ;; X) Update this template in the appropriate table. This step + ;; also will take care of adding the key indicators in the + ;; templates menu entry, if any + ;; + (yas--update-template snippet-table template) + ;; X) Return the template + ;; + ;; + template)) + +(defun yas-define-snippets (mode snippets) + "Define SNIPPETS for MODE. + +SNIPPETS is a list of snippet definitions, each taking the +following form + + (KEY TEMPLATE NAME CONDITION GROUP EXPAND-ENV FILE KEYBINDING UUID) + +Within these, only KEY and TEMPLATE are actually mandatory. + +TEMPLATE might be a Lisp form or a string, depending on whether +this is a snippet or a snippet-command. + +CONDITION, EXPAND-ENV and KEYBINDING are Lisp forms, they have +been `yas--read-lisp'-ed and will eventually be +`yas--eval-lisp'-ed. + +The remaining elements are strings. + +FILE is probably of very little use if you're programatically +defining snippets. + +UUID is the snippets \"unique-id\". Loading a second snippet file +with the same uuid replaced the previous snippet. + +You can use `yas--parse-template' to return such lists based on +the current buffers contents." + (if yas--creating-compiled-snippets + (progn + (insert ";;; Snippet definitions:\n;;;\n") + (let ((literal-snippets (list)) + (print-length nil)) + (dolist (snippet snippets) + (let ((key (nth 0 snippet)) + (template-content (nth 1 snippet)) + (name (nth 2 snippet)) + (condition (nth 3 snippet)) + (group (nth 4 snippet)) + (expand-env (nth 5 snippet)) + (file nil) ;; omit on purpose + (binding (nth 7 snippet)) + (uuid (nth 8 snippet))) + (push `(,key + ,template-content + ,name + ,condition + ,group + ,expand-env + ,file + ,binding + ,uuid) + literal-snippets))) + (insert (pp-to-string + `(yas-define-snippets ',mode ',literal-snippets))) + (insert "\n\n"))) + ;; Normal case. + (let ((snippet-table (yas--table-get-create mode)) + (template nil)) + (dolist (snippet snippets) + (setq template (yas--define-snippets-1 snippet + snippet-table))) + template))) + + +;;; Loading snippets from files + +(defun yas--load-yas-setup-file (file) + (if (not yas--creating-compiled-snippets) + ;; Normal case. + (load file 'noerror) + (let ((elfile (concat file ".el"))) + (when (file-exists-p elfile) + (insert ";;; .yas-setup.el support file if any:\n;;;\n") + (insert-file-contents elfile) + (goto-char (point-max)))))) + +(defun yas--define-parents (mode parents) + "Add PARENTS to the list of MODE's parents." + (puthash mode (remove-duplicates + (append parents + (gethash mode yas--parents))) + yas--parents)) + +(defun yas-load-directory (top-level-dir &optional use-jit interactive) + "Load snippets in directory hierarchy TOP-LEVEL-DIR. + +Below TOP-LEVEL-DIR each directory should be a mode name. + +Optional USE-JIT use jit-loading of snippets." + (interactive "DSelect the root directory: ni\np") + (unless yas-snippet-dirs + (setq yas-snippet-dirs top-level-dir)) + (dolist (dir (yas--subdirs top-level-dir)) + (let* ((major-mode-and-parents (yas--compute-major-mode-and-parents + (concat dir "/dummy"))) + (mode-sym (car major-mode-and-parents)) + (parents (cdr major-mode-and-parents))) + ;; Attention: The parents and the menus are already defined + ;; here, even if the snippets are later jit-loaded. + ;; + ;; * We need to know the parents at this point since entering a + ;; given mode should jit load for its parents + ;; immediately. This could be reviewed, the parents could be + ;; discovered just-in-time-as well + ;; + ;; * We need to create the menus here to support the `full' + ;; option to `yas-use-menu' (all known snippet menus are shown to the user) + ;; + (yas--define-parents mode-sym parents) + (yas--menu-keymap-get-create mode-sym) + (let ((fun `(lambda () ;; FIXME: Simulating lexical-binding. + (yas--load-directory-1 ',dir ',mode-sym)))) + (if (and use-jit + (not (some #'(lambda (buffer) + (with-current-buffer buffer + ;; FIXME: Shouldn't this use derived-mode-p? + (when (eq major-mode mode-sym) + (yas--message 3 "Discovered there was already %s in %s" buffer mode-sym) + t))) + (buffer-list)))) + (yas--schedule-jit mode-sym fun) + (funcall fun))))) + (when interactive + (yas--message 3 "Loaded snippets from %s." top-level-dir))) + +(defun yas--load-directory-1 (directory mode-sym) + "Recursively load snippet templates from DIRECTORY." + (if yas--creating-compiled-snippets + (let ((output-file (expand-file-name ".yas-compiled-snippets.el" + directory))) + (with-temp-file output-file + (insert (format ";;; Compiled snippets and support files for `%s'\n" + mode-sym)) + (yas--load-directory-2 directory mode-sym) + (insert (format ";;; Do not edit! File generated at %s\n" + (current-time-string))))) + ;; Normal case. + (unless (file-exists-p (concat directory "/" ".yas-skip")) + (if (and (progn (yas--message 2 "Loading compiled snippets from %s" directory) t) + (load (expand-file-name ".yas-compiled-snippets" directory) 'noerror (<= yas-verbosity 3))) + (yas--message 2 "Loading snippet files from %s" directory) + (yas--load-directory-2 directory mode-sym))))) + +(defun yas--load-directory-2 (directory mode-sym) + ;; Load .yas-setup.el files wherever we find them + ;; + (yas--load-yas-setup-file (expand-file-name ".yas-setup" directory)) + (let* ((default-directory directory) + (snippet-defs nil)) + ;; load the snippet files + ;; + (with-temp-buffer + (dolist (file (yas--subdirs directory 'no-subdirs-just-files)) + (when (file-readable-p file) + (insert-file-contents file nil nil nil t) + (push (yas--parse-template file) + snippet-defs)))) + (when snippet-defs + (yas-define-snippets mode-sym + snippet-defs)) + ;; now recurse to a lower level + ;; + (dolist (subdir (yas--subdirs directory)) + (yas--load-directory-2 subdir + mode-sym)))) + +(defun yas--load-snippet-dirs (&optional nojit) + "Reload the directories listed in `yas-snippet-dirs' or +prompt the user to select one." + (let (errors) + (if yas-snippet-dirs + (dolist (directory (reverse (yas-snippet-dirs))) + (cond ((file-directory-p directory) + (yas-load-directory directory (not nojit)) + (if nojit + (yas--message 3 "Loaded %s" directory) + (yas--message 3 "Prepared just-in-time loading for %s" directory))) + (t + (push (yas--message 0 "Check your `yas-snippet-dirs': %s is not a directory" directory) errors)))) + (call-interactively 'yas-load-directory)) + errors)) + +(defun yas-reload-all (&optional interactive) + "Reload all snippets and rebuild the YASnippet menu. + +When called interactively force immediate reload of all known +snippets under `yas-snippet-dirs', otherwise use just-in-time +loading." + (interactive "p") + (catch 'abort + (let ((errors) + (snippet-editing-buffers + (remove-if-not #'(lambda (buffer) + (with-current-buffer buffer yas--editing-template)) + (buffer-list)))) + ;; Warn if there are buffers visiting snippets, since reloading will break + ;; any on-line editing of those buffers. + ;; + (when snippet-editing-buffers + (if interactive + (if (y-or-n-p "Some buffers editing live snippets, close them and proceed with reload? ") + (mapc #'kill-buffer snippet-editing-buffers) + (yas--message 1 "Aborted reload...") + (throw 'abort nil)) + ;; in a non-interactive use, at least set + ;; `yas--editing-template' to nil, make it guess it next time around + (mapc #'(lambda (buffer) + (with-current-buffer buffer + (kill-local-variable 'yas--editing-template))) + (buffer-list)))) + + ;; Empty all snippet tables and parenting info + ;; + (setq yas--tables (make-hash-table)) + (setq yas--parents (make-hash-table)) + + ;; Before killing `yas--menu-table' use its keys to cleanup the + ;; mode menu parts of `yas--minor-mode-menu' (thus also cleaning + ;; up `yas-minor-mode-map', which points to it) + ;; + (maphash #'(lambda (menu-symbol _keymap) + (define-key yas--minor-mode-menu (vector menu-symbol) nil)) + yas--menu-table) + ;; Now empty `yas--menu-table' as well + (setq yas--menu-table (make-hash-table)) + + ;; Cancel all pending 'yas--scheduled-jit-loads' + ;; + (setq yas--scheduled-jit-loads (make-hash-table)) + + ;; Reload the directories listed in `yas-snippet-dirs' or prompt + ;; the user to select one. + ;; + (setq errors (yas--load-snippet-dirs interactive)) + ;; Reload the direct keybindings + ;; + (yas-direct-keymaps-reload) + + (run-hooks 'yas-after-reload-hook) + (yas--message 3 "Reloaded everything%s...%s." + (if interactive "" " (snippets will load just-in-time)") + (if errors " (some errors, check *Messages*)" ""))))) + +(defvar yas-after-reload-hook nil + "Hooks run after `yas-reload-all'.") + +(defun yas--load-pending-jits () + (dolist (mode (yas--modes-to-activate)) + (let ((funs (reverse (gethash mode yas--scheduled-jit-loads)))) + ;; must reverse to maintain coherence with `yas-snippet-dirs' + (dolist (fun funs) + (yas--message 3 "Loading for `%s', just-in-time: %s!" mode fun) + (funcall fun)) + (remhash mode yas--scheduled-jit-loads)))) + +;; (when (<= emacs-major-version 22) +;; (add-hook 'after-change-major-mode-hook 'yas--load-pending-jits)) + +(defun yas--quote-string (string) + "Escape and quote STRING. +foo\"bar\\! -> \"foo\\\"bar\\\\!\"" + (concat "\"" + (replace-regexp-in-string "[\\\"]" + "\\\\\\&" + string + t) + "\"")) + +;;; Snippet compilation function + +(defun yas--initialize () + "For backward compatibility, enable `yas-minor-mode' globally." + (yas-global-mode 1)) + +(defun yas-compile-directory (top-level-dir) + "Create .yas-compiled-snippets.el files under subdirs of TOP-LEVEL-DIR. + +This works by stubbing a few functions, then calling +`yas-load-directory'." + (interactive "DTop level snippet directory?") + (let ((yas--creating-compiled-snippets t)) + (yas-load-directory top-level-dir nil))) + +(defun yas-recompile-all () + "Compile every dir in `yas-snippet-dirs'." + (interactive) + (mapc #'yas-compile-directory (yas-snippet-dirs))) + + +;;; JIT loading +;;; + +(defvar yas--scheduled-jit-loads (make-hash-table) + "Alist of mode-symbols to forms to be evaled when `yas-minor-mode' kicks in.") + +(defun yas--schedule-jit (mode fun) + (push fun (gethash mode yas--scheduled-jit-loads))) + + + +;;; Some user level functions + +(defun yas-about () + (interactive) + (message (concat "yasnippet (version " + yas--version + ") -- pluskid <pluskid@gmail.com>/joaotavora <joaotavora@gmail.com>"))) + + +;;; Apropos snippet menu: +;; +;; The snippet menu keymaps are store by mode in hash table called +;; `yas--menu-table'. They are linked to the main menu in +;; `yas--menu-keymap-get-create' and are initially created empty, +;; reflecting the table hierarchy. +;; +;; They can be populated in two mutually exclusive ways: (1) by +;; reading `yas--template-group', which in turn is populated by the "# +;; group:" directives of the snippets or the ".yas-make-groups" file +;; or (2) by using a separate `yas-define-menu' call, which declares a +;; menu structure based on snippets uuids. +;; +;; Both situations are handled in `yas--update-template-menu', which +;; uses the predicate `yas--template-menu-managed-by-yas-define-menu' +;; that can tell between the two situations. +;; +;; Note: +;; +;; * if `yas-define-menu' is used it must run before +;; `yas-define-snippets' and the UUIDS must match, otherwise we get +;; duplicate entries. The `yas--template' objects are created in +;; `yas-define-menu', holding nothing but the menu entry, +;; represented by a pair of ((menu-item NAME :keys KEYS) TYPE) and +;; stored in `yas--template-menu-binding-pair'. The (menu-item ...) +;; part is then stored in the menu keymap itself which make the item +;; appear to the user. These limitations could probably be revised. +;; +;; * The `yas--template-perm-group' slot is only used in +;; `yas-describe-tables'. +;; +(defun yas--template-menu-binding-pair-get-create (template &optional type) + "Get TEMPLATE's menu binding or assign it a new one. + +TYPE may be `:stay', signaling this menu binding should be +static in the menu." + (or (yas--template-menu-binding-pair template) + (let (;; (key (yas--template-key template)) + ;; (keybinding (yas--template-keybinding template)) + ) + (setf (yas--template-menu-binding-pair template) + (cons `(menu-item ,(or (yas--template-name template) + (yas--template-uuid template)) + ,(yas--make-menu-binding template) + :keys ,nil) + type))))) +(defun yas--template-menu-managed-by-yas-define-menu (template) + "Non-nil if TEMPLATE's menu entry was included in a `yas-define-menu' call." + (cdr (yas--template-menu-binding-pair template))) + + +(defun yas--show-menu-p (mode) + (cond ((eq yas-use-menu 'abbreviate) + (find mode + (mapcar #'(lambda (table) + (yas--table-mode table)) + (yas--get-snippet-tables)))) + ((eq yas-use-menu 'full) + t) + ((eq yas-use-menu t) + t))) + +(defun yas--delete-from-keymap (keymap uuid) + "Recursively delete items with UUID from KEYMAP and its submenus." + + ;; XXX: This used to skip any submenus named \"parent mode\" + ;; + ;; First of all, recursively enter submenus, i.e. the tree is + ;; searched depth first so that stale submenus can be found in the + ;; higher passes. + ;; + (mapc #'(lambda (item) + (when (and (listp (cdr item)) + (keymapp (third (cdr item)))) + (yas--delete-from-keymap (third (cdr item)) uuid))) + (rest keymap)) + ;; Set the uuid entry to nil + ;; + (define-key keymap (vector (make-symbol uuid)) nil) + ;; Destructively modify keymap + ;; + (setcdr keymap (delete-if #'(lambda (item) + (or (null (cdr item)) + (and (keymapp (third (cdr item))) + (null (cdr (third (cdr item))))))) + (rest keymap)))) + +(defun yas-define-menu (mode menu &optional omit-items) + "Define a snippet menu for MODE according to MENU, omitting OMIT-ITEMS. + +MENU is a list, its elements can be: + +- (yas-item UUID) : Creates an entry the snippet identified with + UUID. The menu entry for a snippet thus identified is + permanent, i.e. it will never move (be reordered) in the menu. + +- (yas-separator) : Creates a separator + +- (yas-submenu NAME SUBMENU) : Creates a submenu with NAME, + SUBMENU has the same form as MENU. NAME is also added to the + list of groups of the snippets defined thereafter. + +OMIT-ITEMS is a list of snippet uuid's that will always be +omitted from MODE's menu, even if they're manually loaded. + +This function does nothing if `yas-use-menu' is nil." + (when yas-use-menu + (let* ((table (yas--table-get-create mode)) + (hash (yas--table-uuidhash table))) + (yas--define-menu-1 table + (yas--menu-keymap-get-create mode) + menu + hash) + (dolist (uuid omit-items) + (let ((template (or (gethash uuid hash) + (yas--populate-template (puthash uuid + (yas--make-blank-template) + hash) + :table table + :uuid uuid)))) + (setf (yas--template-menu-binding-pair template) (cons nil :none))))))) + +(defun yas--define-menu-1 (table menu-keymap menu uuidhash &optional group-list) + "Helper for `yas-define-menu'." + (dolist (e (reverse menu)) + (cond ((eq (first e) 'yas-item) + (let ((template (or (gethash (second e) uuidhash) + (yas--populate-template (puthash (second e) + (yas--make-blank-template) + uuidhash) + :table table + :perm-group group-list + :uuid (second e))))) + (define-key menu-keymap (vector (gensym)) + (car (yas--template-menu-binding-pair-get-create template :stay))))) + ((eq (first e) 'yas-submenu) + (let ((subkeymap (make-sparse-keymap))) + (define-key menu-keymap (vector (gensym)) + `(menu-item ,(second e) ,subkeymap)) + (yas--define-menu-1 table + subkeymap + (third e) + uuidhash + (append group-list (list (second e)))))) + ((eq (first e) 'yas-separator) + (define-key menu-keymap (vector (gensym)) + '(menu-item "----"))) + (t + (yas--message 3 "Don't know anything about menu entry %s" (first e)))))) + +(defun yas--define (mode key template &optional name condition group) + "Define a snippet. Expanding KEY into TEMPLATE. + +NAME is a description to this template. Also update the menu if +`yas-use-menu' is t. CONDITION is the condition attached to +this snippet. If you attach a condition to a snippet, then it +will only be expanded when the condition evaluated to non-nil." + (yas-define-snippets mode + (list (list key template name condition group)))) + +(defun yas-hippie-try-expand (first-time?) + "Integrate with hippie expand. + +Just put this function in `hippie-expand-try-functions-list'." + (when yas-minor-mode + (if (not first-time?) + (let ((yas-fallback-behavior 'return-nil)) + (yas-expand)) + (undo 1) + nil))) + + +;;; Apropos condition-cache: +;;; +;;; +;;; +;;; +(defvar yas--condition-cache-timestamp nil) +(defmacro yas-define-condition-cache (func doc &rest body) + "Define a function FUNC with doc DOC and body BODY. +BODY is executed at most once every snippet expansion attempt, to check +expansion conditions. + +It doesn't make any sense to call FUNC programatically." + `(defun ,func () ,(if (and doc + (stringp doc)) + (concat doc +"\n\nFor use in snippets' conditions. Within each +snippet-expansion routine like `yas-expand', computes actual +value for the first time then always returns a cached value.") + (setq body (cons doc body)) + nil) + (let ((timestamp-and-value (get ',func 'yas--condition-cache))) + (if (equal (car timestamp-and-value) yas--condition-cache-timestamp) + (cdr timestamp-and-value) + (let ((new-value (progn + ,@body + ))) + (put ',func 'yas--condition-cache (cons yas--condition-cache-timestamp new-value)) + new-value))))) + +(defalias 'yas-expand 'yas-expand-from-trigger-key) +(defun yas-expand-from-trigger-key (&optional field) + "Expand a snippet before point. + +If no snippet expansion is possible, fall back to the behaviour +defined in `yas-fallback-behavior'. + +Optional argument FIELD is for non-interactive use and is an +object satisfying `yas--field-p' to restrict the expansion to." + (interactive) + (setq yas--condition-cache-timestamp (current-time)) + (let (templates-and-pos) + (unless (and yas-expand-only-for-last-commands + (not (member last-command yas-expand-only-for-last-commands))) + (setq templates-and-pos (if field + (save-restriction + (narrow-to-region (yas--field-start field) + (yas--field-end field)) + (yas--current-key)) + (yas--current-key)))) + (if (and templates-and-pos + (first templates-and-pos)) + (yas--expand-or-prompt-for-template (first templates-and-pos) + (second templates-and-pos) + (third templates-and-pos)) + (yas--fallback)))) + +(defun yas-expand-from-keymap () + "Directly expand some snippets, searching `yas--direct-keymaps'. + +If expansion fails, execute the previous binding for this key" + (interactive) + (setq yas--condition-cache-timestamp (current-time)) + (let* ((vec (subseq (this-command-keys-vector) (if current-prefix-arg + (length (this-command-keys)) + 0))) + (templates (mapcan #'(lambda (table) + (yas--fetch table vec)) + (yas--get-snippet-tables)))) + (if templates + (yas--expand-or-prompt-for-template templates) + (let ((yas-fallback-behavior 'call-other-command)) + (yas--fallback))))) + +(defun yas--expand-or-prompt-for-template (templates &optional start end) + "Expand one of TEMPLATES from START to END. + +Prompt the user if TEMPLATES has more than one element, else +expand immediately. Common gateway for +`yas-expand-from-trigger-key' and `yas-expand-from-keymap'." + (let ((yas--current-template (or (and (rest templates) ;; more than one + (yas--prompt-for-template (mapcar #'cdr templates))) + (cdar templates)))) + (when yas--current-template + (yas-expand-snippet (yas--template-content yas--current-template) + start + end + (yas--template-expand-env yas--current-template))))) + +;; Apropos the trigger key and the fallback binding: +;; +;; When `yas-minor-mode-map' binds <tab>, that correctly overrides +;; org-mode's <tab>, for example and searching for fallbacks correctly +;; returns `org-cycle'. However, most other modes bind "TAB". TODO, +;; improve this explanation. +;; +(defun yas--fallback () + "Fallback after expansion has failed. + +Common gateway for `yas-expand-from-trigger-key' and +`yas-expand-from-keymap'." + (cond ((eq yas-fallback-behavior 'return-nil) + ;; return nil + nil) + ((eq yas-fallback-behavior 'call-other-command) + (let* ((beyond-yasnippet (yas--keybinding-beyond-yasnippet))) + (yas--message 4 "Falling back to %s" beyond-yasnippet) + (assert (or (null beyond-yasnippet) (commandp beyond-yasnippet))) + (setq this-original-command beyond-yasnippet) + (when beyond-yasnippet + (call-interactively beyond-yasnippet)))) + ((and (listp yas-fallback-behavior) + (cdr yas-fallback-behavior) + (eq 'apply (car yas-fallback-behavior))) + (if (cddr yas-fallback-behavior) + (apply (cadr yas-fallback-behavior) + (cddr yas-fallback-behavior)) + (when (commandp (cadr yas-fallback-behavior)) + (setq this-command (cadr yas-fallback-behavior)) + (call-interactively (cadr yas-fallback-behavior))))) + (t + ;; also return nil if all the other fallbacks have failed + nil))) + +(defun yas--keybinding-beyond-yasnippet () + "Return the ??" + (let* ((yas-minor-mode nil) + (yas--direct-keymaps nil) + (keys (this-single-command-keys))) + (or (key-binding keys t) + (key-binding (yas--fallback-translate-input keys) t)))) + +(defun yas--fallback-translate-input (keys) + "Emulate `read-key-sequence', at least what I think it does. + +Keys should be an untranslated key vector. Returns a translated +vector of keys. FIXME not thoroughly tested." + (let ((retval []) + (i 0)) + (while (< i (length keys)) + (let ((j i) + (translated local-function-key-map)) + (while (and (< j (length keys)) + translated + (keymapp translated)) + (setq translated (cdr (assoc (aref keys j) (remove 'keymap translated))) + j (1+ j))) + (setq retval (vconcat retval (cond ((symbolp translated) + `[,translated]) + ((vectorp translated) + translated) + (t + (substring keys i j))))) + (setq i j))) + retval)) + + +;;; Utils for snippet development: + +(defun yas--all-templates (tables) + "Get `yas--template' objects in TABLES, applicable for buffer and point. + +Honours `yas-choose-tables-first', `yas-choose-keys-first' and +`yas-buffer-local-condition'" + (when yas-choose-tables-first + (setq tables (list (yas--prompt-for-table tables)))) + (mapcar #'cdr + (if yas-choose-keys-first + (let ((key (yas--prompt-for-keys + (mapcan #'yas--table-all-keys tables)))) + (when key + (mapcan #'(lambda (table) + (yas--fetch table key)) + tables))) + (remove-duplicates (mapcan #'yas--table-templates tables) + :test #'equal)))) + +(defun yas-insert-snippet (&optional no-condition) + "Choose a snippet to expand, pop-up a list of choices according +to `yas-prompt-functions'. + +With prefix argument NO-CONDITION, bypass filtering of snippets +by condition." + (interactive "P") + (setq yas--condition-cache-timestamp (current-time)) + (let* ((yas-buffer-local-condition (or (and no-condition + 'always) + yas-buffer-local-condition)) + (templates (yas--all-templates (yas--get-snippet-tables))) + (yas--current-template (and templates + (or (and (rest templates) ;; more than one template for same key + (yas--prompt-for-template templates)) + (car templates)))) + (where (if (region-active-p) + (cons (region-beginning) (region-end)) + (cons (point) (point))))) + (if yas--current-template + (yas-expand-snippet (yas--template-content yas--current-template) + (car where) + (cdr where) + (yas--template-expand-env yas--current-template)) + (yas--message 3 "No snippets can be inserted here!")))) + +(defun yas-visit-snippet-file () + "Choose a snippet to edit, selection like `yas-insert-snippet'. + +Only success if selected snippet was loaded from a file. Put the +visited file in `snippet-mode'." + (interactive) + (let* ((yas-buffer-local-condition 'always) + (templates (yas--all-templates (yas--get-snippet-tables))) + (yas-prompt-functions '(yas-ido-prompt yas-completing-prompt)) + (template (and templates + (or (yas--prompt-for-template templates + "Choose a snippet template to edit: ") + (car templates))))) + + (if template + (yas--visit-snippet-file-1 template) + (message "No snippets tables active!")))) + +(defun yas--visit-snippet-file-1 (template) + "Helper for `yas-visit-snippet-file'." + (let ((file (yas--template-file template))) + (cond ((and file (file-readable-p file)) + (find-file-other-window file) + (snippet-mode) + (set (make-local-variable 'yas--editing-template) template)) + (file + (message "Original file %s no longer exists!" file)) + (t + (switch-to-buffer (format "*%s*"(yas--template-name template))) + (let ((type 'snippet)) + (when (listp (yas--template-content template)) + (insert (format "# type: command\n")) + (setq type 'command)) + (insert (format "# key: %s\n" (yas--template-key template))) + (insert (format "# name: %s\n" (yas--template-name template))) + (when (yas--template-keybinding template) + (insert (format "# binding: %s\n" (yas--template-keybinding template)))) + (when (yas--template-expand-env template) + (insert (format "# expand-env: %s\n" (yas--template-expand-env template)))) + (when (yas--template-condition template) + (insert (format "# condition: %s\n" (yas--template-condition template)))) + (insert "# --\n") + (insert (if (eq type 'command) + (pp-to-string (yas--template-content template)) + (yas--template-content template)))) + (snippet-mode) + (set (make-local-variable 'yas--editing-template) template))))) + +(defun yas--guess-snippet-directories-1 (table) + "Guess possible snippet subdirectories for TABLE." + (cons (yas--table-name table) + (mapcan #'(lambda (parent) + (yas--guess-snippet-directories-1 + parent)) + (yas--table-parents table)))) + +(defun yas--guess-snippet-directories (&optional table) + "Try to guess suitable directories based on the current active +tables (or optional TABLE). + +Returns a list of elements (TABLE . DIRS) where TABLE is a +`yas--table' object and DIRS is a list of all possible directories +where snippets of table might exist." + (let ((main-dir (replace-regexp-in-string + "/+$" "" + (or (first (or (yas-snippet-dirs) + (setq yas-snippet-dirs '("~/.emacs.d/snippets"))))))) + (tables (or (and table + (list table)) + (yas--get-snippet-tables)))) + ;; HACK! the snippet table created here is actually registered! + ;; + (unless (or table (gethash major-mode yas--tables)) + (push (yas--table-get-create major-mode) + tables)) + + (mapcar #'(lambda (table) + (cons table + (mapcar #'(lambda (subdir) + (concat main-dir "/" subdir)) + (yas--guess-snippet-directories-1 table)))) + tables))) + +(defun yas--make-directory-maybe (table-and-dirs &optional main-table-string) + "Return a dir inside TABLE-AND-DIRS, prompts for creation if none exists." + (or (some #'(lambda (dir) (when (file-directory-p dir) dir)) (cdr table-and-dirs)) + (let ((candidate (first (cdr table-and-dirs)))) + (unless (file-writable-p (file-name-directory candidate)) + (error (yas--format "%s is not writable." candidate))) + (if (y-or-n-p (format "Guessed directory (%s) for%s%s table \"%s\" does not exist! Create? " + candidate + (if (gethash (yas--table-mode (car table-and-dirs)) + yas--tables) + "" + " brand new") + (or main-table-string + "") + (yas--table-name (car table-and-dirs)))) + (progn + (make-directory candidate 'also-make-parents) + ;; create the .yas-parents file here... + candidate))))) + +(defun yas-new-snippet (&optional no-template) + "Pops a new buffer for writing a snippet. + +Expands a snippet-writing snippet, unless the optional prefix arg +NO-TEMPLATE is non-nil." + (interactive "P") + (let ((guessed-directories (yas--guess-snippet-directories))) + + (switch-to-buffer "*new snippet*") + (erase-buffer) + (kill-all-local-variables) + (snippet-mode) + (yas-minor-mode 1) + (set (make-local-variable 'yas--guessed-modes) (mapcar #'(lambda (d) + (yas--table-mode (car d))) + guessed-directories)) + (if (and (not no-template) yas-new-snippet-default) + (yas-expand-snippet yas-new-snippet-default)))) + +(defun yas--compute-major-mode-and-parents (file) + "Given FILE, find the nearest snippet directory for a given mode. + +Returns a list (MODE-SYM PARENTS), the mode's symbol and a list +representing one or more of the mode's parents. + +Note that MODE-SYM need not be the symbol of a real major mode, +neither do the elements of PARENTS." + (let* ((file-dir (and file + (directory-file-name (or (some #'(lambda (special) + (locate-dominating-file file special)) + '(".yas-setup.el" + ".yas-make-groups" + ".yas-parents")) + (directory-file-name (file-name-directory file)))))) + (parents-file-name (concat file-dir "/.yas-parents")) + (major-mode-name (and file-dir + (file-name-nondirectory file-dir))) + (major-mode-sym (or (and major-mode-name + (intern major-mode-name)))) + (parents (when (file-readable-p parents-file-name) + (mapcar #'intern + (split-string + (with-temp-buffer + (insert-file-contents parents-file-name) + (buffer-substring-no-properties (point-min) + (point-max)))))))) + (when major-mode-sym + (cons major-mode-sym (remove major-mode-sym parents))))) + +(defvar yas--editing-template nil + "Supporting variable for `yas-load-snippet-buffer' and `yas--visit-snippet'.") + +(defvar yas--current-template nil + "Holds the current template being expanded into a snippet.") + +(defvar yas--guessed-modes nil + "List of guessed modes supporting `yas-load-snippet-buffer'.") + +(defun yas--read-table () + "Ask user for a snippet table, help with some guessing." + (let ((prompt (if (and (featurep 'ido) + ido-mode) + 'ido-completing-read 'completing-read))) + (unless yas--guessed-modes + (set (make-local-variable 'yas--guessed-modes) + (or (yas--compute-major-mode-and-parents buffer-file-name)))) + (intern + (funcall prompt (format "Choose or enter a table (yas guesses %s): " + (if yas--guessed-modes + (first yas--guessed-modes) + "nothing")) + (mapcar #'symbol-name yas--guessed-modes) + nil + nil + nil + nil + (if (first yas--guessed-modes) + (symbol-name (first yas--guessed-modes))))))) + +(defun yas-load-snippet-buffer (table &optional interactive) + "Parse and load current buffer's snippet definition into TABLE. + +TABLE is a symbol naming a passed to `yas--table-get-create'. + +When called interactively, prompt for the table name." + (interactive (list (yas--read-table) t)) + (cond + ;; We have `yas--editing-template', this buffer's content comes from a + ;; template which is already loaded and neatly positioned,... + ;; + (yas--editing-template + (yas--define-snippets-1 (yas--parse-template (yas--template-file yas--editing-template)) + (yas--template-table yas--editing-template))) + ;; Try to use `yas--guessed-modes'. If we don't have that use the + ;; value from `yas--compute-major-mode-and-parents' + ;; + (t + (unless yas--guessed-modes + (set (make-local-variable 'yas--guessed-modes) (or (yas--compute-major-mode-and-parents buffer-file-name)))) + (let* ((table (yas--table-get-create table))) + (set (make-local-variable 'yas--editing-template) + (yas--define-snippets-1 (yas--parse-template buffer-file-name) + table))))) + (when interactive + (yas--message 3 "Snippet \"%s\" loaded for %s." + (yas--template-name yas--editing-template) + (yas--table-name (yas--template-table yas--editing-template))))) + +(defun yas-load-snippet-buffer-and-close (table &optional kill) + "Load the snippet with `yas-load-snippet-buffer', possibly + save, then `quit-window' if saved. + +If the snippet is new, ask the user whether (and where) to save +it. If the snippet already has a file, just save it. + +The prefix argument KILL is passed to `quit-window'. + +Don't use this from a Lisp program, call `yas-load-snippet-buffer' +and `kill-buffer' instead." + (interactive (list (yas--read-table) current-prefix-arg)) + (yas-load-snippet-buffer table t) + (when (and (or + ;; Only offer to save this if it looks like a library or new + ;; snippet (loaded from elisp, from a dir in `yas-snippet-dirs' + ;; which is not the first, or from an unwritable file) + ;; + (not (yas--template-file yas--editing-template)) + (not (file-writable-p (yas--template-file yas--editing-template))) + (and (listp yas-snippet-dirs) + (second yas-snippet-dirs) + (not (string-match (expand-file-name (first yas-snippet-dirs)) + (yas--template-file yas--editing-template))))) + (y-or-n-p (yas--format "Looks like a library or new snippet. Save to new file? "))) + (let* ((option (first (yas--guess-snippet-directories (yas--template-table yas--editing-template)))) + (chosen (and option + (yas--make-directory-maybe option)))) + (when chosen + (let ((default-file-name (or (and (yas--template-file yas--editing-template) + (file-name-nondirectory (yas--template-file yas--editing-template))) + (yas--template-name yas--editing-template)))) + (write-file (concat chosen "/" + (read-from-minibuffer (format "File name to create in %s? " chosen) + default-file-name))) + (setf (yas--template-file yas--editing-template) buffer-file-name))))) + (when buffer-file-name + (save-buffer) + (quit-window kill))) + +(defun yas-tryout-snippet (&optional debug) + "Test current buffer's snippet template in other buffer." + (interactive "P") + (let* ((major-mode-and-parent (yas--compute-major-mode-and-parents buffer-file-name)) + (parsed (yas--parse-template)) + (test-mode (or (and (car major-mode-and-parent) + (fboundp (car major-mode-and-parent)) + (car major-mode-and-parent)) + (first yas--guessed-modes) + (intern (read-from-minibuffer (yas--format "Please input a mode: "))))) + (yas--current-template + (and parsed + (fboundp test-mode) + (yas--populate-template (yas--make-blank-template) + :table nil ;; no tables for ephemeral snippets + :key (first parsed) + :content (second parsed) + :name (third parsed) + :expand-env (sixth parsed))))) + (cond (yas--current-template + (let ((buffer-name (format "*testing snippet: %s*" (yas--template-name yas--current-template)))) + (kill-buffer (get-buffer-create buffer-name)) + (switch-to-buffer (get-buffer-create buffer-name)) + (setq buffer-undo-list nil) + (condition-case nil (funcall test-mode) (error nil)) + (yas-minor-mode 1) + (setq buffer-read-only nil) + (yas-expand-snippet (yas--template-content yas--current-template) + (point-min) + (point-max) + (yas--template-expand-env yas--current-template)) + (when (and debug + (require 'yasnippet-debug nil t)) + (add-hook 'post-command-hook 'yas-debug-snippet-vars nil t)))) + (t + (yas--message 3 "Cannot test snippet for unknown major mode"))))) + +(defun yas-active-keys () + "Return all active trigger keys for current buffer and point." + (remove-duplicates + (remove-if-not #'stringp (mapcan #'yas--table-all-keys (yas--get-snippet-tables))) + :test #'string=)) + +(defun yas--template-fine-group (template) + (car (last (or (yas--template-group template) + (yas--template-perm-group template))))) + +(defun yas-describe-tables (&optional choose) + "Display snippets for each table." + (interactive "P") + (let* ((by-name-hash (and choose + (y-or-n-p "Show by namehash? "))) + (buffer (get-buffer-create "*YASnippet tables*")) + (active-tables (yas--get-snippet-tables)) + (remain-tables (let ((all)) + (maphash #'(lambda (_k v) + (unless (find v active-tables) + (push v all))) + yas--tables) + all)) + (table-lists (list active-tables remain-tables)) + (original-buffer (current-buffer)) + (continue t) + (yas--condition-cache-timestamp (current-time))) + (with-current-buffer buffer + (setq buffer-read-only nil) + (erase-buffer) + (cond ((not by-name-hash) + (insert "YASnippet tables: \n") + (while (and table-lists + continue) + (dolist (table (car table-lists)) + (yas--describe-pretty-table table original-buffer)) + (setq table-lists (cdr table-lists)) + (when table-lists + (yas--create-snippet-xrefs) + (display-buffer buffer) + (setq continue (and choose (y-or-n-p "Show also non-active tables? "))))) + (yas--create-snippet-xrefs) + (help-mode) + (goto-char 1)) + (t + (insert "\n\nYASnippet tables by NAMEHASH: \n") + (dolist (table (append active-tables remain-tables)) + (insert (format "\nSnippet table `%s':\n\n" (yas--table-name table))) + (let ((keys)) + (maphash #'(lambda (k _v) + (push k keys)) + (yas--table-hash table)) + (dolist (key keys) + (insert (format " key %s maps snippets: %s\n" key + (let ((names)) + (maphash #'(lambda (k _v) + (push k names)) + (gethash key (yas--table-hash table))) + names)))))))) + (goto-char 1) + (setq buffer-read-only t)) + (display-buffer buffer))) + +(defun yas--describe-pretty-table (table &optional original-buffer) + (insert (format "\nSnippet table `%s'" + (yas--table-name table))) + (if (yas--table-parents table) + (insert (format " parents: %s\n" + (mapcar #'yas--table-name + (yas--table-parents table)))) + (insert "\n")) + (insert (make-string 100 ?-) "\n") + (insert "group state name key binding\n") + (let ((groups-hash (make-hash-table :test #'equal))) + (maphash #'(lambda (_k v) + (let ((group (or (yas--template-fine-group v) + "(top level)"))) + (when (yas--template-name v) + (puthash group + (cons v (gethash group groups-hash)) + groups-hash)))) + (yas--table-uuidhash table)) + (maphash + #'(lambda (group templates) + (setq group (truncate-string-to-width group 25 0 ? "...")) + (insert (make-string 100 ?-) "\n") + (dolist (p templates) + (let ((name (truncate-string-to-width (propertize (format "\\\\snippet `%s'" (yas--template-name p)) + 'yasnippet p) + 50 0 ? "...")) + (group (prog1 group + (setq group (make-string (length group) ? )))) + (condition-string (let ((condition (yas--template-condition p))) + (if (and condition + original-buffer) + (with-current-buffer original-buffer + (if (yas--eval-condition condition) + "(y)" + "(s)")) + "(a)")))) + (insert group " ") + (insert condition-string " ") + (insert name + (if (string-match "\\.\\.\\.$" name) + "'" + " ") + " ") + (insert (truncate-string-to-width (or (yas--template-key p) "") + 15 0 ? "...") " ") + (insert (truncate-string-to-width (key-description (yas--template-keybinding p)) + 15 0 ? "...") " ") + (insert "\n")))) + groups-hash))) + + + +;;; User convenience functions, for using in snippet definitions + +(defvar yas-modified-p nil + "Non-nil if field has been modified by user or transformation.") + +(defvar yas-moving-away-p nil + "Non-nil if user is about to exit field.") + +(defvar yas-text nil + "Contains current field text.") + +(defun yas-substr (str pattern &optional subexp) + "Search PATTERN in STR and return SUBEXPth match. + +If found, the content of subexp group SUBEXP (default 0) is + returned, or else the original STR will be returned." + (let ((grp (or subexp 0))) + (save-match-data + (if (string-match pattern str) + (match-string-no-properties grp str) + str)))) + +(defun yas-choose-value (&rest possibilities) + "Prompt for a string in POSSIBILITIES and return it. + +The last element of POSSIBILITIES may be a list of strings." + (unless (or yas-moving-away-p + yas-modified-p) + (setq possibilities (nreverse possibilities)) + (setq possibilities (if (listp (car possibilities)) + (append (reverse (car possibilities)) (rest possibilities)) + possibilities)) + (some #'(lambda (fn) + (funcall fn "Choose: " possibilities)) + yas-prompt-functions))) + +(defun yas-key-to-value (alist) + (unless (or yas-moving-away-p + yas-modified-p) + (let ((key (read-key-sequence ""))) + (when (stringp key) + (or (cdr (find key alist :key #'car :test #'string=)) + key))))) + +(defun yas-throw (text) + "Throw a yas--exception with TEXT as the reason." + (throw 'yas--exception (cons 'yas--exception text))) + +(defun yas-verify-value (possibilities) + "Verify that the current field value is in POSSIBILITIES. + +Otherwise throw exception." + (when (and yas-moving-away-p (notany #'(lambda (pos) (string= pos yas-text)) possibilities)) + (yas-throw (yas--format "Field only allows %s" possibilities)))) + +(defun yas-field-value (number) + "Get the string for field with NUMBER. + +Use this in primary and mirror transformations to tget." + (let* ((snippet (car (yas--snippets-at-point))) + (field (and snippet + (yas--snippet-find-field snippet number)))) + (when field + (yas--field-text-for-display field)))) + +(defun yas-text () + "Return `yas-text' if that exists and is non-empty, else nil." + (if (and yas-text + (not (string= "" yas-text))) + yas-text)) + +(defun yas-selected-text () + "Return `yas-selected-text' if that exists and is non-empty, else nil." + (if (and yas-selected-text + (not (string= "" yas-selected-text))) + yas-selected-text)) + +(defun yas--get-field-once (number &optional transform-fn) + (unless yas-modified-p + (if transform-fn + (funcall transform-fn (yas-field-value number)) + (yas-field-value number)))) + +(defun yas-default-from-field (number) + (unless yas-modified-p + (yas-field-value number))) + +(defun yas-inside-string () + "Return non-nil if the point is inside a string according to font-lock." + (equal 'font-lock-string-face (get-char-property (1- (point)) 'face))) + +(defun yas-unimplemented (&optional missing-feature) + (if yas--current-template + (if (y-or-n-p (format "This snippet is unimplemented (missing %s) Visit the snippet definition? " + (or missing-feature + "something"))) + (yas--visit-snippet-file-1 yas--current-template)) + (message "No implementation. Missing %s" (or missing-feature "something")))) + + +;;; Snippet expansion and field management + +(defvar yas--active-field-overlay nil + "Overlays the currently active field.") + +(defvar yas--field-protection-overlays nil + "Two overlays protect the current active field.") + +(defvar yas-selected-text nil + "The selected region deleted on the last snippet expansion.") + +(defvar yas--start-column nil + "The column where the snippet expansion started.") + +(make-variable-buffer-local 'yas--active-field-overlay) +(make-variable-buffer-local 'yas--field-protection-overlays) +(put 'yas--active-field-overlay 'permanent-local t) +(put 'yas--field-protection-overlays 'permanent-local t) + +(defstruct (yas--snippet (:constructor yas--make-snippet ())) + "A snippet. + +..." + (fields '()) + (exit nil) + (id (yas--snippet-next-id) :read-only t) + (control-overlay nil) + active-field + ;; stacked expansion: the `previous-active-field' slot saves the + ;; active field where the child expansion took place + previous-active-field + force-exit) + +(defstruct (yas--field (:constructor yas--make-field (number start end parent-field))) + "A field. + +NUMBER is the field number. +START and END are mostly buffer markers, but see \"apropos markers-to-points\". +PARENT-FIELD is a `yas--field' this field is nested under, or nil. +MIRRORS is a list of `yas--mirror's +TRANSFORM is a lisp form. +MODIFIED-P is a boolean set to true once user inputs text. +NEXT is another `yas--field' or `yas--mirror' or `yas--exit'. +" + number + start end + parent-field + (mirrors '()) + (transform nil) + (modified-p nil) + next) + + +(defstruct (yas--mirror (:constructor yas--make-mirror (start end transform))) + "A mirror. + +START and END are mostly buffer markers, but see \"apropos markers-to-points\". +TRANSFORM is a lisp form. +PARENT-FIELD is a `yas--field' this mirror is nested under, or nil. +NEXT is another `yas--field' or `yas--mirror' or `yas--exit' +DEPTH is a count of how many nested mirrors can affect this mirror" + start end + (transform nil) + parent-field + next + depth) + +(defstruct (yas--exit (:constructor yas--make-exit (marker))) + marker + next) + +(defun yas--apply-transform (field-or-mirror field &optional empty-on-nil-p) + "Calculate transformed string for FIELD-OR-MIRROR from FIELD. + +If there is no transform for ht field, return nil. + +If there is a transform but it returns nil, return the empty +string iff EMPTY-ON-NIL-P is true." + (let* ((yas-text (yas--field-text-for-display field)) + (yas-modified-p (yas--field-modified-p field)) + (yas-moving-away-p nil) + (transform (if (yas--mirror-p field-or-mirror) + (yas--mirror-transform field-or-mirror) + (yas--field-transform field-or-mirror))) + (start-point (if (yas--mirror-p field-or-mirror) + (yas--mirror-start field-or-mirror) + (yas--field-start field-or-mirror))) + (transformed (and transform + (save-excursion + (goto-char start-point) + (let ((ret (yas--eval-lisp transform))) + (or ret (and empty-on-nil-p ""))))))) + transformed)) + +(defsubst yas--replace-all (from to &optional text) + "Replace all occurrences from FROM to TO. + +With optional string TEXT do it in that string." + (if text + (replace-regexp-in-string (regexp-quote from) to text t t) + (goto-char (point-min)) + (while (search-forward from nil t) + (replace-match to t t text)))) + +(defun yas--snippet-find-field (snippet number) + (find-if #'(lambda (field) + (eq number (yas--field-number field))) + (yas--snippet-fields snippet))) + +(defun yas--snippet-sort-fields (snippet) + "Sort the fields of SNIPPET in navigation order." + (setf (yas--snippet-fields snippet) + (sort (yas--snippet-fields snippet) + #'yas--snippet-field-compare))) + +(defun yas--snippet-field-compare (field1 field2) + "Compare FIELD1 and FIELD2. + +The field with a number is sorted first. If they both have a +number, compare through the number. If neither have, compare +through the field's start point" + (let ((n1 (yas--field-number field1)) + (n2 (yas--field-number field2))) + (if n1 + (if n2 + (or (zerop n2) (and (not (zerop n1)) + (< n1 n2))) + (not (zerop n1))) + (if n2 + (zerop n2) + (< (yas--field-start field1) + (yas--field-start field2)))))) + +(defun yas--field-probably-deleted-p (snippet field) + "Guess if SNIPPET's FIELD should be skipped." + (and + ;; field must be zero length + ;; + (zerop (- (yas--field-start field) (yas--field-end field))) + ;; skip if: + (or + ;; 1) is a nested field and it's been modified + ;; + (and (yas--field-parent-field field) + (yas--field-modified-p field)) + ;; 2) ends just before the snippet end + ;; + (and (eq field (car (last (yas--snippet-fields snippet)))) + (= (yas--field-start field) (overlay-end (yas--snippet-control-overlay snippet))))) + ;; the field numbered 0, just before the exit marker, should + ;; never be skipped + ;; + (not (zerop (yas--field-number field))))) + +(defun yas--snippets-at-point (&optional all-snippets) + "Return a sorted list of snippets at point. + +The most recently-inserted snippets are returned first." + (sort + (remove nil (remove-duplicates (mapcar #'(lambda (ov) + (overlay-get ov 'yas--snippet)) + (if all-snippets + (overlays-in (point-min) (point-max)) + (nconc (overlays-at (point)) (overlays-at (1- (point)))))))) + #'(lambda (s1 s2) + (<= (yas--snippet-id s2) (yas--snippet-id s1))))) + +(defun yas-next-field-or-maybe-expand () + "Try to expand a snippet at a key before point. + +Otherwise delegate to `yas-next-field'." + (interactive) + (if yas-triggers-in-field + (let ((yas-fallback-behavior 'return-nil) + (active-field (overlay-get yas--active-field-overlay 'yas--field))) + (when active-field + (unless (yas-expand-from-trigger-key active-field) + (yas-next-field)))) + (yas-next-field))) + +(defun yas-next-field (&optional arg) + "Navigate to the ARGth next field. + +If there's none, exit the snippet." + (interactive) + (let* ((arg (or arg + 1)) + (snippet (first (yas--snippets-at-point))) + (active-field (overlay-get yas--active-field-overlay 'yas--field)) + (live-fields (remove-if #'(lambda (field) + (and (not (eq field active-field)) + (yas--field-probably-deleted-p snippet field))) + (yas--snippet-fields snippet))) + (active-field-pos (position active-field live-fields)) + (target-pos (and active-field-pos (+ arg active-field-pos))) + (target-field (and target-pos (nth target-pos live-fields)))) + ;; First check if we're moving out of a field with a transform + ;; + (when (and active-field + (yas--field-transform active-field)) + (let* ((yas-moving-away-p t) + (yas-text (yas--field-text-for-display active-field)) + (yas-modified-p (yas--field-modified-p active-field))) + ;; primary field transform: exit call to field-transform + (yas--eval-lisp (yas--field-transform active-field)))) + ;; Now actually move... + (cond ((and target-pos (>= target-pos (length live-fields))) + (yas-exit-snippet snippet)) + (target-field + (yas--move-to-field snippet target-field)) + (t + nil)))) + +(defun yas--place-overlays (snippet field) + "Correctly place overlays for SNIPPET's FIELD." + (yas--make-move-field-protection-overlays snippet field) + (yas--make-move-active-field-overlay snippet field)) + +(defun yas--move-to-field (snippet field) + "Update SNIPPET to move to field FIELD. + +Also create some protection overlays" + (goto-char (yas--field-start field)) + (yas--place-overlays snippet field) + (overlay-put yas--active-field-overlay 'yas--field field) + (let ((number (yas--field-number field))) + ;; check for the special ${0: ...} field + (if (and number (zerop number)) + (progn + (set-mark (yas--field-end field)) + (setf (yas--snippet-force-exit snippet) + (or (yas--field-transform field) + t))) + ;; make this field active + (setf (yas--snippet-active-field snippet) field) + ;; primary field transform: first call to snippet transform + (unless (yas--field-modified-p field) + (if (yas--field-update-display field) + (yas--update-mirrors snippet) + (setf (yas--field-modified-p field) nil)))))) + +(defun yas-prev-field () + "Navigate to prev field. If there's none, exit the snippet." + (interactive) + (yas-next-field -1)) + +(defun yas-abort-snippet (&optional snippet) + (interactive) + (let ((snippet (or snippet + (car (yas--snippets-at-point))))) + (when snippet + (setf (yas--snippet-force-exit snippet) t)))) + +(defun yas-exit-snippet (snippet) + "Goto exit-marker of SNIPPET." + (interactive (list (first (yas--snippets-at-point)))) + (when snippet + (setf (yas--snippet-force-exit snippet) t) + (goto-char (if (yas--snippet-exit snippet) + (yas--exit-marker (yas--snippet-exit snippet)) + (overlay-end (yas--snippet-control-overlay snippet)))))) + +(defun yas-exit-all-snippets () + "Exit all snippets." + (interactive) + (mapc #'(lambda (snippet) + (yas-exit-snippet snippet) + (yas--check-commit-snippet)) + (yas--snippets-at-point 'all-snippets))) + + +;;; Some low level snippet-routines: + +(defvar yas--inhibit-overlay-hooks nil + "Bind this temporarily to non-nil to prevent running `yas--on-*-modification'.") + +(defmacro yas--inhibit-overlay-hooks (&rest body) + "Run BODY with `yas--inhibit-overlay-hooks' set to t." + (declare (indent 0)) + `(let ((yas--inhibit-overlay-hooks t)) + ,@body)) + +(defvar yas-snippet-beg nil "Beginning position of the last snippet committed.") +(defvar yas-snippet-end nil "End position of the last snippet committed.") + +(defun yas--commit-snippet (snippet) + "Commit SNIPPET, but leave point as it is. + +This renders the snippet as ordinary text." + + (let ((control-overlay (yas--snippet-control-overlay snippet))) + ;; + ;; Save the end of the moribund snippet in case we need to revive it + ;; its original expansion. + ;; + (when (and control-overlay + (overlay-buffer control-overlay)) + (setq yas-snippet-beg (overlay-start control-overlay)) + (setq yas-snippet-end (overlay-end control-overlay)) + (delete-overlay control-overlay)) + + (yas--inhibit-overlay-hooks + (when yas--active-field-overlay + (delete-overlay yas--active-field-overlay)) + (when yas--field-protection-overlays + (mapc #'delete-overlay yas--field-protection-overlays))) + + ;; stacked expansion: if the original expansion took place from a + ;; field, make sure we advance it here at least to + ;; `yas-snippet-end'... + ;; + (let ((previous-field (yas--snippet-previous-active-field snippet))) + (when (and yas-snippet-end previous-field) + (yas--advance-end-maybe previous-field yas-snippet-end))) + + ;; Convert all markers to points, + ;; + (yas--markers-to-points snippet) + + ;; Take care of snippet revival + ;; + (if yas-snippet-revival + (push `(apply yas--snippet-revive ,yas-snippet-beg ,yas-snippet-end ,snippet) + buffer-undo-list) + ;; Dismember the snippet... this is useful if we get called + ;; again from `yas--take-care-of-redo'.... + (setf (yas--snippet-fields snippet) nil))) + + (yas--message 3 "Snippet %s exited." (yas--snippet-id snippet))) + +(defun yas--safely-run-hooks (hook-var) + (condition-case error + (run-hooks hook-var) + (error + (yas--message 3 "%s error: %s" hook-var (error-message-string error))))) + + +(defun yas--check-commit-snippet () + "Check if point exited the currently active field of the snippet. + +If so cleans up the whole snippet up." + (let* ((snippets (yas--snippets-at-point 'all-snippets)) + (snippets-left snippets) + (snippet-exit-transform)) + (dolist (snippet snippets) + (let ((active-field (yas--snippet-active-field snippet))) + (setq snippet-exit-transform (yas--snippet-force-exit snippet)) + (cond ((or snippet-exit-transform + (not (and active-field (yas--field-contains-point-p active-field)))) + (setq snippets-left (delete snippet snippets-left)) + (setf (yas--snippet-force-exit snippet) nil) + (yas--commit-snippet snippet)) + ((and active-field + (or (not yas--active-field-overlay) + (not (overlay-buffer yas--active-field-overlay)))) + ;; + ;; stacked expansion: this case is mainly for recent + ;; snippet exits that place us back int the field of + ;; another snippet + ;; + (save-excursion + (yas--move-to-field snippet active-field) + (yas--update-mirrors snippet))) + (t + nil)))) + (unless (or (null snippets) snippets-left) + (if snippet-exit-transform + (yas--eval-lisp-no-saves snippet-exit-transform)) + (yas--safely-run-hooks 'yas-after-exit-snippet-hook)))) + +;; Apropos markers-to-points: +;; +;; This was found useful for performance reasons, so that an +;; excessive number of live markers aren't kept around in the +;; `buffer-undo-list'. However, in `markers-to-points', the +;; set-to-nil markers can't simply be discarded and replaced with +;; fresh ones in `points-to-markers'. The original marker that was +;; just set to nil has to be reused. +;; +;; This shouldn't bring horrible problems with undo/redo, but it +;; you never know +;; +(defun yas--markers-to-points (snippet) + "Convert all markers in SNIPPET to a cons (POINT . MARKER) +where POINT is the original position of the marker and MARKER is +the original marker object with the position set to nil." + (dolist (field (yas--snippet-fields snippet)) + (let ((start (marker-position (yas--field-start field))) + (end (marker-position (yas--field-end field)))) + (set-marker (yas--field-start field) nil) + (set-marker (yas--field-end field) nil) + (setf (yas--field-start field) (cons start (yas--field-start field))) + (setf (yas--field-end field) (cons end (yas--field-end field)))) + (dolist (mirror (yas--field-mirrors field)) + (let ((start (marker-position (yas--mirror-start mirror))) + (end (marker-position (yas--mirror-end mirror)))) + (set-marker (yas--mirror-start mirror) nil) + (set-marker (yas--mirror-end mirror) nil) + (setf (yas--mirror-start mirror) (cons start (yas--mirror-start mirror))) + (setf (yas--mirror-end mirror) (cons end (yas--mirror-end mirror)))))) + (let ((snippet-exit (yas--snippet-exit snippet))) + (when snippet-exit + (let ((exit (marker-position (yas--exit-marker snippet-exit)))) + (set-marker (yas--exit-marker snippet-exit) nil) + (setf (yas--exit-marker snippet-exit) (cons exit (yas--exit-marker snippet-exit))))))) + +(defun yas--points-to-markers (snippet) + "Convert all cons (POINT . MARKER) in SNIPPET to markers. + +This is done by setting MARKER to POINT with `set-marker'." + (dolist (field (yas--snippet-fields snippet)) + (setf (yas--field-start field) (set-marker (cdr (yas--field-start field)) + (car (yas--field-start field)))) + (setf (yas--field-end field) (set-marker (cdr (yas--field-end field)) + (car (yas--field-end field)))) + (dolist (mirror (yas--field-mirrors field)) + (setf (yas--mirror-start mirror) (set-marker (cdr (yas--mirror-start mirror)) + (car (yas--mirror-start mirror)))) + (setf (yas--mirror-end mirror) (set-marker (cdr (yas--mirror-end mirror)) + (car (yas--mirror-end mirror)))))) + (let ((snippet-exit (yas--snippet-exit snippet))) + (when snippet-exit + (setf (yas--exit-marker snippet-exit) (set-marker (cdr (yas--exit-marker snippet-exit)) + (car (yas--exit-marker snippet-exit))))))) + +(defun yas--field-contains-point-p (field &optional point) + (let ((point (or point + (point)))) + (and (>= point (yas--field-start field)) + (<= point (yas--field-end field))))) + +(defun yas--field-text-for-display (field) + "Return the propertized display text for field FIELD." + (buffer-substring (yas--field-start field) (yas--field-end field))) + +(defun yas--undo-in-progress () + "True if some kind of undo is in progress." + (or undo-in-progress + (eq this-command 'undo) + (eq this-command 'redo))) + +(defun yas--make-control-overlay (snippet start end) + "Create the control overlay that surrounds the snippet and +holds the keymap." + (let ((overlay (make-overlay start + end + nil + nil + t))) + (overlay-put overlay 'keymap yas-keymap) + (overlay-put overlay 'priority 100) + (overlay-put overlay 'yas--snippet snippet) + overlay)) + +(defun yas-skip-and-clear-or-delete-char (&optional field) + "Clears unmodified field if at field start, skips to next tab. + +Otherwise deletes a character normally by calling `delete-char'." + (interactive) + (let ((field (or field + (and yas--active-field-overlay + (overlay-buffer yas--active-field-overlay) + (overlay-get yas--active-field-overlay 'yas--field))))) + (cond ((and field + (not (yas--field-modified-p field)) + (eq (point) (marker-position (yas--field-start field)))) + (yas--skip-and-clear field) + (yas-next-field 1)) + (t + (call-interactively 'delete-char))))) + +(defun yas--skip-and-clear (field) + "Deletes the region of FIELD and sets it's modified state to t." + ;; Just before skipping-and-clearing the field, mark its children + ;; fields as modified, too. If the children have mirrors-in-fields + ;; this prevents them from updating erroneously (we're skipping and + ;; deleting!). + ;; + (yas--mark-this-and-children-modified field) + (delete-region (yas--field-start field) (yas--field-end field))) + +(defun yas--mark-this-and-children-modified (field) + (setf (yas--field-modified-p field) t) + (let ((fom (yas--field-next field))) + (while (and fom + (yas--fom-parent-field fom)) + (when (and (eq (yas--fom-parent-field fom) field) + (yas--field-p fom)) + (yas--mark-this-and-children-modified fom)) + (setq fom (yas--fom-next fom))))) + +(defun yas--make-move-active-field-overlay (snippet field) + "Place the active field overlay in SNIPPET's FIELD. + +Move the overlay, or create it if it does not exit." + (if (and yas--active-field-overlay + (overlay-buffer yas--active-field-overlay)) + (move-overlay yas--active-field-overlay + (yas--field-start field) + (yas--field-end field)) + (setq yas--active-field-overlay + (make-overlay (yas--field-start field) + (yas--field-end field) + nil nil t)) + (overlay-put yas--active-field-overlay 'priority 100) + (overlay-put yas--active-field-overlay 'face 'yas-field-highlight-face) + (overlay-put yas--active-field-overlay 'yas--snippet snippet) + (overlay-put yas--active-field-overlay 'modification-hooks '(yas--on-field-overlay-modification)) + (overlay-put yas--active-field-overlay 'insert-in-front-hooks + '(yas--on-field-overlay-modification)) + (overlay-put yas--active-field-overlay 'insert-behind-hooks + '(yas--on-field-overlay-modification)))) + +(defun yas--on-field-overlay-modification (overlay after? _beg _end &optional _length) + "Clears the field and updates mirrors, conditionally. + +Only clears the field if it hasn't been modified and it point it +at field start. This hook doesn't do anything if an undo is in +progress." + (unless (or yas--inhibit-overlay-hooks + (yas--undo-in-progress)) + (let* ((field (overlay-get overlay 'yas--field)) + (snippet (overlay-get yas--active-field-overlay 'yas--snippet))) + (cond (after? + (yas--advance-end-maybe field (overlay-end overlay)) + (save-excursion + (yas--field-update-display field)) + (yas--update-mirrors snippet)) + (field + (when (and (not after?) + (not (yas--field-modified-p field)) + (eq (point) (if (markerp (yas--field-start field)) + (marker-position (yas--field-start field)) + (yas--field-start field)))) + (yas--skip-and-clear field)) + (setf (yas--field-modified-p field) t)))))) + +;;; Apropos protection overlays: +;; +;; These exist for nasty users who will try to delete parts of the +;; snippet outside the active field. Actual protection happens in +;; `yas--on-protection-overlay-modification'. +;; +;; Currently this signals an error which inhibits the command. For +;; commands that move point (like `kill-line'), point is restored in +;; the `yas--post-command-handler' using a global +;; `yas--protection-violation' variable. +;; +;; Alternatively, I've experimented with an implementation that +;; commits the snippet before actually calling `this-command' +;; interactively, and then signals an error, which is ignored. but +;; blocks all other million modification hooks. This presented some +;; problems with stacked expansion. +;; +(defun yas--make-move-field-protection-overlays (snippet field) + "Place protection overlays surrounding SNIPPET's FIELD. + +Move the overlays, or create them if they do not exit." + (let ((start (yas--field-start field)) + (end (yas--field-end field))) + ;; First check if the (1+ end) is contained in the buffer, + ;; otherwise we'll have to do a bit of cheating and silently + ;; insert a newline. the `(1+ (buffer-size))' should prevent this + ;; when using stacked expansion + ;; + (when (< (buffer-size) end) + (save-excursion + (yas--inhibit-overlay-hooks + (goto-char (point-max)) + (newline)))) + ;; go on to normal overlay creation/moving + ;; + (cond ((and yas--field-protection-overlays + (every #'overlay-buffer yas--field-protection-overlays)) + (move-overlay (first yas--field-protection-overlays) (1- start) start) + (move-overlay (second yas--field-protection-overlays) end (1+ end))) + (t + (setq yas--field-protection-overlays + (list (make-overlay (1- start) start nil t nil) + (make-overlay end (1+ end) nil t nil))) + (dolist (ov yas--field-protection-overlays) + (overlay-put ov 'face 'yas--field-debug-face) + (overlay-put ov 'yas--snippet snippet) + ;; (overlay-put ov 'evaporate t) + (overlay-put ov 'modification-hooks '(yas--on-protection-overlay-modification))))))) + +(defvar yas--protection-violation nil + "When non-nil, signals attempts to erroneously exit or modify the snippet. + +Functions in the `post-command-hook', for example +`yas--post-command-handler' can check it and reset its value to +nil. The variables value is the point where the violation +originated") + +(defun yas--on-protection-overlay-modification (_overlay after? _beg _end &optional _length) + "Signals a snippet violation, then issues error. + +The error should be ignored in `debug-ignored-errors'" + (unless yas--inhibit-overlay-hooks + (cond ((not (or after? + (yas--undo-in-progress))) + (setq yas--protection-violation (point)) + (error "Exit the snippet first!"))))) + +(add-to-list 'debug-ignored-errors "^Exit the snippet first!$") + + +;;; Snippet expansion and "stacked" expansion: +;; +;; Stacked expansion is when you try to expand a snippet when already +;; inside a snippet expansion. +;; +;; The parent snippet does not run its fields modification hooks +;; (`yas--on-field-overlay-modification' and +;; `yas--on-protection-overlay-modification') while the child snippet +;; is active. This means, among other things, that the mirrors of the +;; parent snippet are not updated, this only happening when one exits +;; the child snippet. +;; +;; Unfortunately, this also puts some ugly (and not fully-tested) +;; bits of code in `yas-expand-snippet' and +;; `yas--commit-snippet'. I've tried to mark them with "stacked +;; expansion:". +;; +;; This was thought to be safer in an undo/redo perspective, but +;; maybe the correct implementation is to make the globals +;; `yas--active-field-overlay' and `yas--field-protection-overlays' be +;; snippet-local and be active even while the child snippet is +;; running. This would mean a lot of overlay modification hooks +;; running, but if managed correctly (including overlay priorities) +;; they should account for all situations... +;; +(defun yas-expand-snippet (content &optional start end expand-env) + "Expand snippet CONTENT at current point. + +Text between START and END will be deleted before inserting +template. EXPAND-ENV is are let-style variable to value bindings +considered when expanding the snippet." + (run-hooks 'yas-before-expand-snippet-hook) + + ;; + (let* ((yas-selected-text (or yas-selected-text + (and (region-active-p) + (buffer-substring-no-properties (region-beginning) + (region-end))))) + (start (or start + (and (region-active-p) + (region-beginning)) + (point))) + (end (or end + (and (region-active-p) + (region-end)) + (point))) + (to-delete (and start + end + (buffer-substring-no-properties start end))) + snippet) + (goto-char start) + (setq yas--indent-original-column (current-column)) + ;; Delete the region to delete, this *does* get undo-recorded. + ;; + (when (and to-delete + (> end start)) + (delete-region start end)) + + (cond ((listp content) + ;; x) This is a snippet-command + ;; + (yas--eval-lisp-no-saves content)) + (t + ;; x) This is a snippet-snippet :-) + ;; + ;; Narrow the region down to the content, shoosh the + ;; `buffer-undo-list', and create the snippet, the new + ;; snippet updates its mirrors once, so we are left with + ;; some plain text. The undo action for deleting this + ;; plain text will get recorded at the end. + ;; + ;; stacked expansion: also shoosh the overlay modification hooks + (let ((buffer-undo-list t)) + ;; snippet creation might evaluate users elisp, which + ;; might generate errors, so we have to be ready to catch + ;; them mostly to make the undo information + ;; + (setq yas--start-column (current-column)) + (yas--inhibit-overlay-hooks + (setq snippet + (if expand-env + (eval `(let* ,expand-env + (insert content) + (yas--snippet-create start (point)))) + (insert content) + (yas--snippet-create start (point)))))) + + ;; stacked-expansion: This checks for stacked expansion, save the + ;; `yas--previous-active-field' and advance its boundary. + ;; + (let ((existing-field (and yas--active-field-overlay + (overlay-buffer yas--active-field-overlay) + (overlay-get yas--active-field-overlay 'yas--field)))) + (when existing-field + (setf (yas--snippet-previous-active-field snippet) existing-field) + (yas--advance-end-maybe existing-field (overlay-end yas--active-field-overlay)))) + + ;; Exit the snippet immediately if no fields + ;; + (unless (yas--snippet-fields snippet) + (yas-exit-snippet snippet)) + + ;; Push two undo actions: the deletion of the inserted contents of + ;; the new snippet (without the "key") followed by an apply of + ;; `yas--take-care-of-redo' on the newly inserted snippet boundaries + ;; + ;; A small exception, if `yas-also-auto-indent-first-line' + ;; is t and `yas--indent' decides to indent the line to a + ;; point before the actual expansion point, undo would be + ;; messed up. We call the early point "newstart"". case, + ;; and attempt to fix undo. + ;; + (let ((newstart (overlay-start (yas--snippet-control-overlay snippet))) + (end (overlay-end (yas--snippet-control-overlay snippet)))) + (when (< newstart start) + (push (cons (make-string (- start newstart) ? ) newstart) buffer-undo-list)) + (push (cons newstart end) buffer-undo-list) + (push `(apply yas--take-care-of-redo ,start ,end ,snippet) + buffer-undo-list)) + ;; Now, schedule a move to the first field + ;; + (let ((first-field (car (yas--snippet-fields snippet)))) + (when first-field + (sit-for 0) ;; fix issue 125 + (yas--move-to-field snippet first-field))) + (yas--message 3 "snippet expanded.") + t)))) + +(defun yas--take-care-of-redo (_beg _end snippet) + "Commits SNIPPET, which in turn pushes an undo action for reviving it. + +Meant to exit in the `buffer-undo-list'." + ;; slightly optimize: this action is only needed for snippets with + ;; at least one field + (when (yas--snippet-fields snippet) + (yas--commit-snippet snippet))) + +(defun yas--snippet-revive (beg end snippet) + "Revives SNIPPET and creates a control overlay from BEG to END. + +BEG and END are, we hope, the original snippets boundaries. +All the markers/points exiting existing inside SNIPPET should point +to their correct locations *at the time the snippet is revived*. + +After revival, push the `yas--take-care-of-redo' in the +`buffer-undo-list'" + ;; Reconvert all the points to markers + ;; + (yas--points-to-markers snippet) + ;; When at least one editable field existed in the zombie snippet, + ;; try to revive the whole thing... + ;; + (let ((target-field (or (yas--snippet-active-field snippet) + (car (yas--snippet-fields snippet))))) + (when target-field + (setf (yas--snippet-control-overlay snippet) (yas--make-control-overlay snippet beg end)) + (overlay-put (yas--snippet-control-overlay snippet) 'yas--snippet snippet) + + (yas--move-to-field snippet target-field) + + (push `(apply yas--take-care-of-redo ,beg ,end ,snippet) + buffer-undo-list)))) + +(defun yas--snippet-create (begin end) + "Create a snippet from a template inserted at BEGIN to END. + +Returns the newly created snippet." + (save-restriction + (narrow-to-region begin end) + (let ((snippet (yas--make-snippet))) + (goto-char begin) + (yas--snippet-parse-create snippet) + + ;; Sort and link each field + (yas--snippet-sort-fields snippet) + + ;; Create keymap overlay for snippet + (setf (yas--snippet-control-overlay snippet) + (yas--make-control-overlay snippet (point-min) (point-max))) + + ;; Move to end + (goto-char (point-max)) + + snippet))) + + +;;; Apropos adjacencies and "fom's": +;; +;; Once the $-constructs bits like "$n" and "${:n" are deleted in the +;; recently expanded snippet, we might actually have many fields, +;; mirrors (and the snippet exit) in the very same position in the +;; buffer. Therefore we need to single-link the +;; fields-or-mirrors-or-exit (which I have abbreviated to "fom") +;; according to their original positions in the buffer. +;; +;; Then we have operation `yas--advance-end-maybe' and +;; `yas--advance-start-maybe', which conditionally push the starts and +;; ends of these foms down the chain. +;; +;; This allows for like the printf with the magic ",": +;; +;; printf ("${1:%s}\\n"${1:$(if (string-match "%" text) "," "\);")} \ +;; $2${1:$(if (string-match "%" text) "\);" "")}$0 +;; +(defun yas--fom-start (fom) + (cond ((yas--field-p fom) + (yas--field-start fom)) + ((yas--mirror-p fom) + (yas--mirror-start fom)) + (t + (yas--exit-marker fom)))) + +(defun yas--fom-end (fom) + (cond ((yas--field-p fom) + (yas--field-end fom)) + ((yas--mirror-p fom) + (yas--mirror-end fom)) + (t + (yas--exit-marker fom)))) + +(defun yas--fom-next (fom) + (cond ((yas--field-p fom) + (yas--field-next fom)) + ((yas--mirror-p fom) + (yas--mirror-next fom)) + (t + (yas--exit-next fom)))) + +(defun yas--fom-parent-field (fom) + (cond ((yas--field-p fom) + (yas--field-parent-field fom)) + ((yas--mirror-p fom) + (yas--mirror-parent-field fom)) + (t + nil))) + +(defun yas--calculate-adjacencies (snippet) + "Calculate adjacencies for fields or mirrors of SNIPPET. + +This is according to their relative positions in the buffer, and +has to be called before the $-constructs are deleted." + (let* ((fom-set-next-fom + (lambda (fom nextfom) + (cond ((yas--field-p fom) + (setf (yas--field-next fom) nextfom)) + ((yas--mirror-p fom) + (setf (yas--mirror-next fom) nextfom)) + (t + (setf (yas--exit-next fom) nextfom))))) + (compare-fom-begs + (lambda (fom1 fom2) + (if (= (yas--fom-start fom2) (yas--fom-start fom1)) + (yas--mirror-p fom2) + (>= (yas--fom-start fom2) (yas--fom-start fom1))))) + (link-foms fom-set-next-fom)) + ;; make some yas--field, yas--mirror and yas--exit soup + (let ((soup)) + (when (yas--snippet-exit snippet) + (push (yas--snippet-exit snippet) soup)) + (dolist (field (yas--snippet-fields snippet)) + (push field soup) + (dolist (mirror (yas--field-mirrors field)) + (push mirror soup))) + (setq soup + (sort soup compare-fom-begs)) + (when soup + (reduce link-foms soup))))) + +(defun yas--calculate-mirrors-in-fields (snippet mirror) + "Attempt to assign a parent field of SNIPPET to the mirror MIRROR. + +Use the tightest containing field if more than one field contains +the mirror. Intended to be called *before* the dollar-regions are +deleted." + (let ((min (point-min)) + (max (point-max))) + (dolist (field (yas--snippet-fields snippet)) + (when (and (<= (yas--field-start field) (yas--mirror-start mirror)) + (<= (yas--mirror-end mirror) (yas--field-end field)) + (< min (yas--field-start field)) + (< (yas--field-end field) max)) + (setq min (yas--field-start field) + max (yas--field-end field)) + (setf (yas--mirror-parent-field mirror) field))))) + +(defun yas--advance-end-maybe (fom newend) + "Maybe advance FOM's end to NEWEND if it needs it. + +If it does, also: + +* call `yas--advance-start-maybe' on FOM's next fom. + +* in case FOM is field call `yas--advance-end-maybe' on its parent + field + +Also, if FOM is an exit-marker, always call +`yas--advance-start-maybe' on its next fom. This is because +exit-marker have identical start and end markers." + (cond ((and fom (< (yas--fom-end fom) newend)) + (set-marker (yas--fom-end fom) newend) + (yas--advance-start-maybe (yas--fom-next fom) newend) + (yas--advance-end-of-parents-maybe (yas--fom-parent-field fom) newend)) + ((yas--exit-p fom) + (yas--advance-start-maybe (yas--fom-next fom) newend)))) + +(defun yas--advance-start-maybe (fom newstart) + "Maybe advance FOM's start to NEWSTART if it needs it. + +If it does, also call `yas--advance-end-maybe' on FOM." + (when (and fom (< (yas--fom-start fom) newstart)) + (set-marker (yas--fom-start fom) newstart) + (yas--advance-end-maybe fom newstart))) + +(defun yas--advance-end-of-parents-maybe (field newend) + "Like `yas--advance-end-maybe' but for parent fields. + +Only works for fields and doesn't care about the start of the +next FOM. Works its way up recursively for parents of parents." + (when (and field + (< (yas--field-end field) newend)) + (set-marker (yas--field-end field) newend) + (yas--advance-end-of-parents-maybe (yas--field-parent-field field) newend))) + +(defvar yas--dollar-regions nil + "When expanding the snippet the \"parse-create\" functions add +cons cells to this var.") + +(defvar yas--backquote-markers-and-strings nil + "List of (MARKER . STRING) marking where the values from +backquoted Lisp expressions should be inserted at the end of +expansion.") + +(defun yas--snippet-parse-create (snippet) + "Parse a recently inserted snippet template, creating all +necessary fields, mirrors and exit points. + +Meant to be called in a narrowed buffer, does various passes" + (let ((parse-start (point))) + ;; Reset the yas--dollar-regions + ;; + (setq yas--dollar-regions nil) + ;; protect just the backquotes + ;; + (yas--protect-escapes nil '(?`)) + ;; replace all backquoted expressions + ;; + (goto-char parse-start) + (yas--save-backquotes) + ;; protect escaped characters + ;; + (yas--protect-escapes) + ;; parse fields with {} + ;; + (goto-char parse-start) + (yas--field-parse-create snippet) + ;; parse simple mirrors and fields + ;; + (goto-char parse-start) + (yas--simple-mirror-parse-create snippet) + ;; parse mirror transforms + ;; + (goto-char parse-start) + (yas--transform-mirror-parse-create snippet) + ;; calculate adjacencies of fields and mirrors + ;; + (yas--calculate-adjacencies snippet) + ;; Delete $-constructs + ;; + (save-restriction (widen) (yas--delete-regions yas--dollar-regions)) + ;; restore backquoted expression values + ;; + (yas--restore-backquotes) + ;; restore escapes + ;; + (goto-char parse-start) + (yas--restore-escapes) + ;; update mirrors for the first time + ;; + (yas--update-mirrors snippet) + ;; indent the best we can + ;; + (goto-char parse-start) + (yas--indent snippet))) + +(defun yas--indent-according-to-mode (snippet-markers) + "Indent current line according to mode, preserving SNIPPET-MARKERS." + ;;; Apropos indenting problems.... + ;; + ;; `indent-according-to-mode' uses whatever `indent-line-function' + ;; is available. Some implementations of these functions delete text + ;; before they insert. If there happens to be a marker just after + ;; the text being deleted, the insertion actually happens after the + ;; marker, which misplaces it. + ;; + ;; This would also happen if we had used overlays with the + ;; `front-advance' property set to nil. + ;; + ;; This is why I have these `trouble-markers', they are the ones at + ;; they are the ones at the first non-whitespace char at the line + ;; (i.e. at `yas--real-line-beginning'. After indentation takes place + ;; we should be at the correct to restore them to. All other + ;; non-trouble-markers have been *pushed* and don't need special + ;; attention. + ;; + (goto-char (yas--real-line-beginning)) + (let ((trouble-markers (remove-if-not #'(lambda (marker) + (= marker (point))) + snippet-markers))) + (save-restriction + (widen) + (condition-case _ + (indent-according-to-mode) + (error (yas--message 3 "Warning: `yas--indent-according-to-mode' having problems running %s" indent-line-function) + nil))) + (mapc #'(lambda (marker) + (set-marker marker (point))) + trouble-markers))) + +(defvar yas--indent-original-column nil) +(defun yas--indent (snippet) + (let ((snippet-markers (yas--collect-snippet-markers snippet))) + ;; Look for those $> + (save-excursion + (while (re-search-forward "$>" nil t) + (delete-region (match-beginning 0) (match-end 0)) + (when (not (eq yas-indent-line 'auto)) + (yas--indent-according-to-mode snippet-markers)))) + ;; Now do stuff for 'fixed and 'auto + (save-excursion + (cond ((eq yas-indent-line 'fixed) + (while (and (zerop (forward-line)) + (zerop (current-column))) + (indent-to-column yas--indent-original-column))) + ((eq yas-indent-line 'auto) + (let ((end (set-marker (make-marker) (point-max))) + (indent-first-line-p yas-also-auto-indent-first-line)) + (while (and (zerop (if indent-first-line-p + (prog1 + (forward-line 0) + (setq indent-first-line-p nil)) + (forward-line 1))) + (not (eobp)) + (<= (point) end)) + (yas--indent-according-to-mode snippet-markers)))) + (t + nil))))) + +(defun yas--collect-snippet-markers (snippet) + "Make a list of all the markers used by SNIPPET." + (let (markers) + (dolist (field (yas--snippet-fields snippet)) + (push (yas--field-start field) markers) + (push (yas--field-end field) markers) + (dolist (mirror (yas--field-mirrors field)) + (push (yas--mirror-start mirror) markers) + (push (yas--mirror-end mirror) markers))) + (let ((snippet-exit (yas--snippet-exit snippet))) + (when (and snippet-exit + (marker-buffer (yas--exit-marker snippet-exit))) + (push (yas--exit-marker snippet-exit) markers))) + markers)) + +(defun yas--real-line-beginning () + (let ((c (char-after (line-beginning-position))) + (n (line-beginning-position))) + (while (or (eql c ?\ ) + (eql c ?\t)) + (incf n) + (setq c (char-after n))) + n)) + +(defun yas--escape-string (escaped) + (concat "YASESCAPE" (format "%d" escaped) "PROTECTGUARD")) + +(defun yas--protect-escapes (&optional text escaped) + "Protect all escaped characters with their numeric ASCII value. + +With optional string TEXT do it in string instead of buffer." + (let ((changed-text text) + (text-provided-p text)) + (mapc #'(lambda (escaped) + (setq changed-text + (yas--replace-all (concat "\\" (char-to-string escaped)) + (yas--escape-string escaped) + (when text-provided-p changed-text)))) + (or escaped yas--escaped-characters)) + changed-text)) + +(defun yas--restore-escapes (&optional text escaped) + "Restore all escaped characters from their numeric ASCII value. + +With optional string TEXT do it in string instead of the buffer." + (let ((changed-text text) + (text-provided-p text)) + (mapc #'(lambda (escaped) + (setq changed-text + (yas--replace-all (yas--escape-string escaped) + (char-to-string escaped) + (when text-provided-p changed-text)))) + (or escaped yas--escaped-characters)) + changed-text)) + +(defun yas--save-backquotes () + "Save all the \"`(lisp-expression)`\"-style expressions +with their evaluated value into `yas--backquote-markers-and-strings'." + (while (re-search-forward yas--backquote-lisp-expression-regexp nil t) + (let ((current-string (match-string-no-properties 1)) transformed) + (delete-region (match-beginning 0) (match-end 0)) + (setq transformed (yas--eval-lisp (yas--read-lisp (yas--restore-escapes current-string '(?`))))) + (goto-char (match-beginning 0)) + (when transformed + (let ((marker (make-marker))) + (insert "Y") ;; quite horrendous, I love it :) + (set-marker marker (point)) + (insert "Y") + (push (cons marker transformed) yas--backquote-markers-and-strings)))))) + +(defun yas--restore-backquotes () + "Replace markers in `yas--backquote-markers-and-strings' with their values." + (while yas--backquote-markers-and-strings + (let* ((marker-and-string (pop yas--backquote-markers-and-strings)) + (marker (car marker-and-string)) + (string (cdr marker-and-string))) + (save-excursion + (goto-char marker) + (delete-char -1) + (insert string) + (delete-char 1) + (set-marker marker nil))))) + +(defun yas--scan-sexps (from count) + (condition-case _ + (with-syntax-table (standard-syntax-table) + (scan-sexps from count)) + (error + nil))) + +(defun yas--make-marker (pos) + "Create a marker at POS with nil `marker-insertion-type'." + (let ((marker (set-marker (make-marker) pos))) + (set-marker-insertion-type marker nil) + marker)) + +(defun yas--field-parse-create (snippet &optional parent-field) + "Parse most field expressions in SNIPPET, except for the simple one \"$n\". + +The following count as a field: + +* \"${n: text}\", for a numbered field with default text, as long as N is not 0; + +* \"${n: text$(expression)}, the same with a Lisp expression; + this is caught with the curiously named `yas--multi-dollar-lisp-expression-regexp' + +* the same as above but unnumbered, (no N:) and number is calculated automatically. + +When multiple expressions are found, only the last one counts." + ;; + (save-excursion + (while (re-search-forward yas--field-regexp nil t) + (let* ((real-match-end-0 (yas--scan-sexps (1+ (match-beginning 0)) 1)) + (number (and (match-string-no-properties 1) + (string-to-number (match-string-no-properties 1)))) + (brand-new-field (and real-match-end-0 + ;; break if on "$(" immediately + ;; after the ":", this will be + ;; caught as a mirror with + ;; transform later. + (not (save-match-data + (eq (string-match "$[ \t\n]*(" + (match-string-no-properties 2)) 0))) + ;; allow ${0: some exit text} + ;; (not (and number (zerop number))) + (yas--make-field number + (yas--make-marker (match-beginning 2)) + (yas--make-marker (1- real-match-end-0)) + parent-field)))) + (when brand-new-field + (goto-char real-match-end-0) + (push (cons (1- real-match-end-0) real-match-end-0) + yas--dollar-regions) + (push (cons (match-beginning 0) (match-beginning 2)) + yas--dollar-regions) + (push brand-new-field (yas--snippet-fields snippet)) + (save-excursion + (save-restriction + (narrow-to-region (yas--field-start brand-new-field) (yas--field-end brand-new-field)) + (goto-char (point-min)) + (yas--field-parse-create snippet brand-new-field))))))) + ;; if we entered from a parent field, now search for the + ;; `yas--multi-dollar-lisp-expression-regexp'. This is used for + ;; primary field transformations + ;; + (when parent-field + (save-excursion + (while (re-search-forward yas--multi-dollar-lisp-expression-regexp nil t) + (let* ((real-match-end-1 (yas--scan-sexps (match-beginning 1) 1))) + ;; commit the primary field transformation if: + ;; + ;; 1. we don't find it in yas--dollar-regions (a subnested + ;; field) might have already caught it. + ;; + ;; 2. we really make sure we have either two '$' or some + ;; text and a '$' after the colon ':'. This is a FIXME: work + ;; my regular expressions and end these ugly hacks. + ;; + (when (and real-match-end-1 + (not (member (cons (match-beginning 0) + real-match-end-1) + yas--dollar-regions)) + (not (eq ?: + (char-before (1- (match-beginning 1)))))) + (let ((lisp-expression-string (buffer-substring-no-properties (match-beginning 1) + real-match-end-1))) + (setf (yas--field-transform parent-field) + (yas--read-lisp (yas--restore-escapes lisp-expression-string)))) + (push (cons (match-beginning 0) real-match-end-1) + yas--dollar-regions))))))) + +(defun yas--transform-mirror-parse-create (snippet) + "Parse the \"${n:$(lisp-expression)}\" mirror transformations in SNIPPET." + (while (re-search-forward yas--transform-mirror-regexp nil t) + (let* ((real-match-end-0 (yas--scan-sexps (1+ (match-beginning 0)) 1)) + (number (string-to-number (match-string-no-properties 1))) + (field (and number + (not (zerop number)) + (yas--snippet-find-field snippet number))) + (brand-new-mirror + (and real-match-end-0 + field + (yas--make-mirror (yas--make-marker (match-beginning 0)) + (yas--make-marker (match-beginning 0)) + (yas--read-lisp + (yas--restore-escapes + (buffer-substring-no-properties (match-beginning 2) + (1- real-match-end-0)))))))) + (when brand-new-mirror + (push brand-new-mirror + (yas--field-mirrors field)) + (yas--calculate-mirrors-in-fields snippet brand-new-mirror) + (push (cons (match-beginning 0) real-match-end-0) yas--dollar-regions))))) + +(defun yas--simple-mirror-parse-create (snippet) + "Parse the simple \"$n\" fields/mirrors/exitmarkers in SNIPPET." + (while (re-search-forward yas--simple-mirror-regexp nil t) + (let ((number (string-to-number (match-string-no-properties 1)))) + (cond ((zerop number) + + (setf (yas--snippet-exit snippet) + (yas--make-exit (yas--make-marker (match-end 0)))) + (save-excursion + (goto-char (match-beginning 0)) + (when yas-wrap-around-region + (cond (yas-selected-text + (insert yas-selected-text)) + ((and (eq yas-wrap-around-region 'cua) + cua-mode + (get-register ?0)) + (insert (prog1 (get-register ?0) + (set-register ?0 nil)))))) + (push (cons (point) (yas--exit-marker (yas--snippet-exit snippet))) + yas--dollar-regions))) + (t + (let ((field (yas--snippet-find-field snippet number))) + (if field + (let ((brand-new-mirror (yas--make-mirror + (yas--make-marker (match-beginning 0)) + (yas--make-marker (match-beginning 0)) + nil))) + (push brand-new-mirror + (yas--field-mirrors field)) + (yas--calculate-mirrors-in-fields snippet brand-new-mirror)) + (push (yas--make-field number + (yas--make-marker (match-beginning 0)) + (yas--make-marker (match-beginning 0)) + nil) + (yas--snippet-fields snippet)))) + (push (cons (match-beginning 0) (match-end 0)) + yas--dollar-regions)))))) + +(defun yas--delete-regions (regions) + "Sort disjuct REGIONS by start point, then delete from the back." + (mapc #'(lambda (reg) + (delete-region (car reg) (cdr reg))) + (sort regions + #'(lambda (r1 r2) + (>= (car r1) (car r2)))))) + +(defun yas--calculate-mirror-depth (mirror &optional traversed) + (let* ((parent (yas--mirror-parent-field mirror)) + (parents-mirrors (and parent + (yas--field-mirrors parent)))) + (or (yas--mirror-depth mirror) + (setf (yas--mirror-depth mirror) + (cond ((memq mirror traversed) + 0) + ((and parent parents-mirrors) + (1+ (reduce #'max + (mapcar #'(lambda (m) + (yas--calculate-mirror-depth m + (cons mirror + traversed))) + parents-mirrors)))) + (parent + 1) + (t + 0)))))) + +(defun yas--update-mirrors (snippet) + "Update all the mirrors of SNIPPET." + (save-excursion + (dolist (field-and-mirror (sort + ;; make a list of ((F1 . M1) (F1 . M2) (F2 . M3) (F2 . M4) ...) + ;; where F is the field that M is mirroring + ;; + (mapcan #'(lambda (field) + (mapcar #'(lambda (mirror) + (cons field mirror)) + (yas--field-mirrors field))) + (yas--snippet-fields snippet)) + ;; then sort this list so that entries with mirrors with parent + ;; fields appear before. This was important for fixing #290, and + ;; luckily also handles the case where a mirror in a field causes + ;; another mirror to need reupdating + ;; + #'(lambda (field-and-mirror1 field-and-mirror2) + (> (yas--calculate-mirror-depth (cdr field-and-mirror1)) + (yas--calculate-mirror-depth (cdr field-and-mirror2)))))) + (let* ((field (car field-and-mirror)) + (mirror (cdr field-and-mirror)) + (parent-field (yas--mirror-parent-field mirror))) + ;; before updating a mirror with a parent-field, maybe advance + ;; its start (#290) + ;; + (when parent-field + (yas--advance-start-maybe mirror (yas--fom-start parent-field))) + ;; update this mirror + ;; + (yas--mirror-update-display mirror field) + ;; `yas--place-overlays' is needed if the active field and + ;; protected overlays have been changed because of insertions + ;; in `yas--mirror-update-display' + ;; + (when (eq field (yas--snippet-active-field snippet)) + (yas--place-overlays snippet field)))))) + +(defun yas--mirror-update-display (mirror field) + "Update MIRROR according to FIELD (and mirror transform)." + + (let* ((mirror-parent-field (yas--mirror-parent-field mirror)) + (reflection (and (not (and mirror-parent-field + (yas--field-modified-p mirror-parent-field))) + (or (yas--apply-transform mirror field 'empty-on-nil) + (yas--field-text-for-display field))))) + (when (and reflection + (not (string= reflection (buffer-substring-no-properties (yas--mirror-start mirror) + (yas--mirror-end mirror))))) + (goto-char (yas--mirror-start mirror)) + (yas--inhibit-overlay-hooks + (insert reflection)) + (if (> (yas--mirror-end mirror) (point)) + (delete-region (point) (yas--mirror-end mirror)) + (set-marker (yas--mirror-end mirror) (point)) + (yas--advance-start-maybe (yas--mirror-next mirror) (point)) + ;; super-special advance + (yas--advance-end-of-parents-maybe mirror-parent-field (point)))))) + +(defun yas--field-update-display (field) + "Much like `yas--mirror-update-display', but for fields." + (when (yas--field-transform field) + (let ((transformed (and (not (eq (yas--field-number field) 0)) + (yas--apply-transform field field)))) + (when (and transformed + (not (string= transformed (buffer-substring-no-properties (yas--field-start field) + (yas--field-end field))))) + (setf (yas--field-modified-p field) t) + (goto-char (yas--field-start field)) + (yas--inhibit-overlay-hooks + (insert transformed) + (if (> (yas--field-end field) (point)) + (delete-region (point) (yas--field-end field)) + (set-marker (yas--field-end field) (point)) + (yas--advance-start-maybe (yas--field-next field) (point))) + t))))) + + +;;; Post-command hook: +;; +(defun yas--post-command-handler () + "Handles various yasnippet conditions after each command." + (cond (yas--protection-violation + (goto-char yas--protection-violation) + (setq yas--protection-violation nil)) + ((eq 'undo this-command) + ;; + ;; After undo revival the correct field is sometimes not + ;; restored correctly, this condition handles that + ;; + (let* ((snippet (car (yas--snippets-at-point))) + (target-field (and snippet + (find-if-not #'(lambda (field) + (yas--field-probably-deleted-p snippet field)) + (remove nil + (cons (yas--snippet-active-field snippet) + (yas--snippet-fields snippet))))))) + (when target-field + (yas--move-to-field snippet target-field)))) + ((not (yas--undo-in-progress)) + ;; When not in an undo, check if we must commit the snippet + ;; (user exited it). + (yas--check-commit-snippet)))) + +;;; Fancy docs: +;; +;; The docstrings for some functions are generated dynamically +;; depending on the context. +;; +(put 'yas-expand 'function-documentation + '(yas--expand-from-trigger-key-doc t)) +(defun yas--expand-from-trigger-key-doc (context) + "A doc synthesizer for `yas--expand-from-trigger-key-doc'." + (let* ((yas-fallback-behavior (and context yas-fallback-behavior)) + (fallback-description + (cond ((eq yas-fallback-behavior 'call-other-command) + (let* ((fallback (yas--keybinding-beyond-yasnippet))) + (or (and fallback + (format "call command `%s'." + (pp-to-string fallback))) + "do nothing (`yas-expand' doesn't shadow\nanything)."))) + ((eq yas-fallback-behavior 'return-nil) + "do nothing.") + (t "defer to `yas-fallback-behavior' (which see).")))) + (concat "Expand a snippet before point. If no snippet +expansion is possible, " + fallback-description + "\n\nOptional argument FIELD is for non-interactive use and is an +object satisfying `yas--field-p' to restrict the expansion to."))) + +(put 'yas-expand-from-keymap 'function-documentation + '(yas--expand-from-keymap-doc t)) +(defun yas--expand-from-keymap-doc (context) + "A doc synthesizer for `yas--expand-from-keymap-doc'." + (add-hook 'temp-buffer-show-hook 'yas--snippet-description-finish-runonce) + (concat "Expand/run snippets from keymaps, possibly falling back to original binding.\n" + (when (and context (eq this-command 'describe-key)) + (let* ((vec (this-single-command-keys)) + (templates (mapcan #'(lambda (table) + (yas--fetch table vec)) + (yas--get-snippet-tables))) + (yas--direct-keymaps nil) + (fallback (key-binding vec))) + (concat "In this case, " + (when templates + (concat "these snippets are bound to this key:\n" + (yas--template-pretty-list templates) + "\n\nIf none of these expands, ")) + (or (and fallback + (format "fallback `%s' will be called." (pp-to-string fallback))) + "no fallback keybinding is called.")))))) + +(defun yas--template-pretty-list (templates) + (let ((acc) + (yas-buffer-local-condition 'always)) + (dolist (plate templates) + (setq acc (concat acc "\n*) " + (propertize (concat "\\\\snippet `" (car plate) "'") + 'yasnippet (cdr plate))))) + acc)) + +(define-button-type 'help-snippet-def + :supertype 'help-xref + 'help-function (lambda (template) (yas--visit-snippet-file-1 template)) + 'help-echo (purecopy "mouse-2, RET: find snippets's definition")) + +(defun yas--snippet-description-finish-runonce () + "Final adjustments for the help buffer when snippets are concerned." + (yas--create-snippet-xrefs) + (remove-hook 'temp-buffer-show-hook 'yas--snippet-description-finish-runonce)) + +(defun yas--create-snippet-xrefs () + (save-excursion + (goto-char (point-min)) + (while (search-forward-regexp "\\\\\\\\snippet[ \s\t]+`\\([^']+\\)'" nil t) + (let ((template (get-text-property (match-beginning 1) + 'yasnippet))) + (when template + (help-xref-button 1 'help-snippet-def template) + (kill-region (match-end 1) (match-end 0)) + (kill-region (match-beginning 0) (match-beginning 1))))))) + +;;; Utils + +(defvar yas-verbosity 4 + "Log level for `yas--message' 4 means trace most anything, 0 means nothing.") + +(defun yas--message (level message &rest args) + "When LEVEL is above `yas-verbosity-level', log MESSAGE and ARGS." + (when (> yas-verbosity level) + (message "%s" (apply #'yas--format message args)))) + +(defun yas--format (format-control &rest format-args) + (apply #'format (concat "[yas] " format-control) format-args)) + + +;;; Some hacks: +;; +;; The functions +;; +;; `locate-dominating-file' +;; `region-active-p' +;; +;; added for compatibility in emacsen < 23 +(unless (>= emacs-major-version 23) + (unless (fboundp 'region-active-p) + (defun region-active-p () (and transient-mark-mode mark-active))) + + (unless (fboundp 'locate-dominating-file) + (defvar locate-dominating-stop-dir-regexp + "\\`\\(?:[\\/][\\/][^\\/]+[\\/]\\|/\\(?:net\\|afs\\|\\.\\.\\.\\)/\\)\\'" + "Regexp of directory names which stop the search in `locate-dominating-file'. +Any directory whose name matches this regexp will be treated like +a kind of root directory by `locate-dominating-file' which will stop its search +when it bumps into it. +The default regexp prevents fruitless and time-consuming attempts to find +special files in directories in which filenames are interpreted as hostnames, +or mount points potentially requiring authentication as a different user.") + + (defun locate-dominating-file (file name) + "Look up the directory hierarchy from FILE for a file named NAME. +Stop at the first parent directory containing a file NAME, +and return the directory. Return nil if not found." + ;; We used to use the above locate-dominating-files code, but the + ;; directory-files call is very costly, so we're much better off doing + ;; multiple calls using the code in here. + ;; + ;; Represent /home/luser/foo as ~/foo so that we don't try to look for + ;; `name' in /home or in /. + (setq file (abbreviate-file-name file)) + (let ((root nil) + try) + (while (not (or root + (null file) + ;; FIXME: Disabled this heuristic because it is sometimes + ;; inappropriate. + ;; As a heuristic, we stop looking up the hierarchy of + ;; directories as soon as we find a directory belonging + ;; to another user. This should save us from looking in + ;; things like /net and /afs. This assumes that all the + ;; files inside a project belong to the same user. + ;; (let ((prev-user user)) + ;; (setq user (nth 2 (file-attributes file))) + ;; (and prev-user (not (equal user prev-user)))) + (string-match locate-dominating-stop-dir-regexp file))) + (setq try (file-exists-p (expand-file-name name file))) + (cond (try (setq root file)) + ((equal file (setq file (file-name-directory + (directory-file-name file)))) + (setq file nil)))) + root)))) + + +;;; Backward compatibility to yasnippet <= 0.7 + +(defvar yas--backported-syms '(;; `defcustom's + ;; + yas-snippet-dirs + yas-prompt-functions + yas-indent-line + yas-also-auto-indent-first-line + yas-snippet-revival + yas-triggers-in-field + yas-fallback-behavior + yas-choose-keys-first + yas-choose-tables-first + yas-use-menu + yas-trigger-symbol + yas-wrap-around-region + yas-good-grace + yas-visit-from-menu + yas-expand-only-for-last-commands + yas-field-highlight-face + + ;; these vars can be customized as well + ;; + yas-keymap + yas-verbosity + yas-extra-modes + yas-key-syntaxes + yas-after-exit-snippet-hook + yas-before-expand-snippet-hook + yas-buffer-local-condition + yas-dont-activate + + ;; prompting functions + ;; + yas-x-prompt + yas-ido-prompt + yas-no-prompt + yas-completing-prompt + yas-dropdown-prompt + + ;; interactive functions + ;; + yas-expand + yas-minor-mode + yas-global-mode + yas-direct-keymaps-reload + yas-minor-mode-on + yas-load-directory + yas-reload-all + yas-compile-directory + yas-recompile-all + yas-about + yas-expand-from-trigger-key + yas-expand-from-keymap + yas-insert-snippet + yas-visit-snippet-file + yas-new-snippet + yas-load-snippet-buffer + yas-tryout-snippet + yas-describe-tables + yas-next-field-or-maybe-expand + yas-next-field + yas-prev-field + yas-abort-snippet + yas-exit-snippet + yas-exit-all-snippets + yas-skip-and-clear-or-delete-char + + ;; symbols that I "exported" for use + ;; in snippets and hookage + ;; + yas-expand-snippet + yas-define-snippets + yas-define-menu + yas-snippet-beg + yas-snippet-end + yas-modified-p + yas-moving-away-p + yas-substr + yas-choose-value + yas-key-to-value + yas-throw + yas-verify-value + yas-field-value + yas-text + yas-selected-text + yas-default-from-field + yas-inside-string + yas-unimplemented + yas-define-condition-cache + yas-hippie-try-expand + + ;; debug definitions + ;; yas-debug-snippet-vars + ;; yas-exterminate-package + ;; yas-debug-test + + ;; testing definitions + ;; yas-should-expand + ;; yas-should-not-expand + ;; yas-mock-insert + ;; yas-make-file-or-dirs + ;; yas-variables + ;; yas-saving-variables + ;; yas-call-with-snippet-dirs + ;; yas-with-snippet-dirs +) + "Backported yasnippet symbols. + +They are mapped to \"yas/*\" variants.") + +(dolist (sym yas--backported-syms) + (let ((backported (intern (replace-regexp-in-string "^yas-" "yas/" (symbol-name sym))))) + (when (boundp sym) + (make-obsolete-variable backported sym "yasnippet 0.8") + (defvaralias backported sym)) + (when (fboundp sym) + (make-obsolete backported sym "yasnippet 0.8") + (defalias backported sym)))) + +(defvar yas--exported-syms + (let (exported) + (mapatoms (lambda (atom) + (if (and (or (and (boundp atom) + (not (get atom 'byte-obsolete-variable))) + (and (fboundp atom) + (not (get atom 'byte-obsolete-info)))) + (string-match-p "^yas-[^-]" (symbol-name atom))) + (push atom exported)))) + exported) + "Exported yasnippet symbols. + +i.e. the ones with \"yas-\" single dash prefix. I will try to +keep them in future yasnippet versions and other elisp libraries +can more or less safely rely upon them.") + + +(provide 'yasnippet) + +;;; yasnippet.el ends here +;; Local Variables: +;; coding: utf-8 +;; byte-compile-warnings: (not cl-functions) +;; End: