summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicholas D Steeves <nsteeves@gmail.com>2018-08-01 23:10:25 -0300
committerNicholas D Steeves <nsteeves@gmail.com>2018-08-01 23:10:25 -0300
commit0913a7ff75ade004aa51310eff025489fd58a3fc (patch)
tree3b95abacd25a033525f7d1f544dd1cef183fd51c
parentec01048fe1a07293c3a161b48249e5d6ce8c6824 (diff)
parent1381a75f856ddacf80eb5b6d35fa405af7d92a02 (diff)
Record yasnippet (0.13.0-2) in archive suite sid
-rw-r--r--.gitmodules7
-rw-r--r--.travis.yml23
-rw-r--r--CONTRIBUTING.md66
-rw-r--r--NEWS170
-rw-r--r--README.mdown42
-rw-r--r--Rakefile32
-rw-r--r--debian/.gitignore4
-rw-r--r--debian/changelog74
-rw-r--r--debian/compat2
-rw-r--r--debian/control28
-rw-r--r--debian/copyright5
-rw-r--r--debian/patches/0001-typos-and-grammar.patch45
-rw-r--r--debian/patches/0002-Avoiding-having-git-as-a-building-dependency.patch25
-rw-r--r--debian/patches/0003-Debian-yas-installed-snippets-dir.patch41
-rw-r--r--debian/patches/0004-no-timestamp-in-html-head.patch24
-rw-r--r--debian/patches/series4
-rwxr-xr-xdebian/rules2
-rw-r--r--debian/yasnippet.maintscript1
-rw-r--r--doc/faq.org76
-rw-r--r--doc/nav-menu.html.inc4
-rw-r--r--doc/snippet-development.org36
-rw-r--r--doc/snippet-expansion.org88
-rw-r--r--doc/snippet-menu.org2
-rw-r--r--doc/snippet-organization.org7
-rw-r--r--doc/stylesheets/manual.css33
-rw-r--r--doc/stylesheets/styles.css93
-rw-r--r--doc/yas-doc-helper.el196
-rw-r--r--yasnippet-debug.el414
-rw-r--r--yasnippet-tests.el801
-rw-r--r--yasnippet.el1774
30 files changed, 2694 insertions, 1425 deletions
diff --git a/.gitmodules b/.gitmodules
index a8cc391..e69de29 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,7 +0,0 @@
-[submodule "snippets"]
- path = snippets
- url = https://github.com/AndreaCrotti/yasnippet-snippets.git
- branch = master
-[submodule "yasmate"]
- path = yasmate
- url = https://github.com/capitaomorte/yasmate.git
diff --git a/.travis.yml b/.travis.yml
index b43d358..9a999ec 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,10 +4,19 @@ git:
submodules: false
env:
- - EMACS_VERSION=23.4
- - EMACS_VERSION=24.3
- - EMACS_VERSION=24.5
- - EMACS_VERSION=25-prerelease
+ global:
+ - Wlexical=t
+ - Werror=t
+ - tests_Werror=t # For yasnippet-tests.el
+ matrix:
+ - EMACS_VERSION=23.4
+ # 24.3 gives a bunch of 'value returned from (car value-N) is
+ # unused' warnings.
+ - EMACS_VERSION=24.3 tests_Werror=nil
+ - EMACS_VERSION=24.5
+ - EMACS_VERSION=25.3
+ - EMACS_VERSION=26-prerelease
+
install:
- curl -LO https://github.com/npostavs/emacs-travis/releases/download/bins/emacs-bin-${EMACS_VERSION}.tar.gz
@@ -15,7 +24,7 @@ install:
# Configure $PATH: Emacs installed to /tmp/emacs
- export PATH=/tmp/emacs/bin:${PATH}
- if ! emacs -Q --batch --eval "(require 'cl-lib)" ; then
- curl -Lo cl-lib.el http://elpa.gnu.org/packages/cl-lib-0.5.el ;
+ curl -Lo cl-lib.el http://elpa.gnu.org/packages/cl-lib-0.6.1.el ;
export warnings="'(not cl-functions)" ;
fi
- if ! emacs -Q --batch --eval "(require 'ert)" ; then
@@ -25,7 +34,9 @@ install:
- emacs --version
script:
- - rake compile
+ - rake yasnippet.elc
+ - rake yasnippet-debug.elc
+ - rake yasnippet-tests.elc Werror=$tests_Werror
- rake tests
notifications:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 81c0a1f..bf3b2d3 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,29 +1,37 @@
-# Submitting Bug Reports
-
-Please read [Important note regarding bug reporting][bugnote].
-
-# Contributing to Yasnippet
-
-## Copyright Assignment
-
-Yasnippet is part of GNU ELPA, so it falls under the same copyright
-assignment policy as the rest of Emacs (see "Copyright Assignment" in
-https://www.gnu.org/software/emacs/CONTRIBUTE). A copyright assignment
-for Emacs also covers Yasnippet.
-
-## Commit message format
-
-The commit message format roughly follows Emacs conventions, although
-there is no separate Changelog file.
-
- The commit message's first sentence should be capitalized, no period
-
- It may be followed by a paragraph with a longer explanation. The
- changelog style entry goes at the end of the message.
-
- * foo.el (a-function): Terse summary of per-function changes.
-
-For trivial changes, a message consisting of just the changelog entry
-(the `* foo.el ...` part) is fine.
-
-[bugnote]: https://github.com/joaotavora/yasnippet#important-note-regarding-bug-reporting
+# Submitting Bug Reports or Patches
+
+As a GNU ELPA package, bugs or patches may be submitted to the main
+Emacs bug list, bug-gnu-emacs@gnu.org. Alternatively, you may use the
+[Github issue tracker][issues].
+
+Please read [Important note regarding bug reporting][bugnote].
+
+# Contributing to Yasnippet
+
+## Copyright Assignment
+
+Yasnippet is part of GNU ELPA, so it falls under the same copyright
+assignment policy as the rest of Emacs (see "Copyright Assignment" in
+https://www.gnu.org/software/emacs/CONTRIBUTE). A copyright assignment
+for Emacs also covers Yasnippet.
+
+## Commit message format
+
+The commit message format roughly follows Emacs conventions. There is
+no separate Changelog file.
+
+ Capitalize the first sentence, no period at the end
+
+ Please make sure the summary line can be understood without having
+ to lookup bug numbers. It may be followed by a paragraph with a
+ longer explanation. The changelog style entry goes at the end of
+ the message.
+ * foo.el (a-function): Terse summary of per-function changes. Use
+ double spacing between sentences (set `sentence-end-double-space'
+ to t).
+
+For trivial changes, a message consisting of just the changelog entry
+(e.g., `* foo.el (a-function): Fix docstring typo.`) is fine.
+
+[bugnote]: https://github.com/joaotavora/yasnippet#important-note-regarding-bug-reporting
+[issues]: https://github.com/joaotavora/yasnippet/issues
diff --git a/NEWS b/NEWS
index 6e6629d..6bea12f 100644
--- a/NEWS
+++ b/NEWS
@@ -1,10 +1,178 @@
Yasnippet NEWS -- history of user-visible changes.
-Copyright (C) 2016 Free Software Foundation, Inc.
+Copyright (C) 2017-2018 Free Software Foundation, Inc.
See the end of the file for license conditions.
+* 0.13.0 (May 13, 2018)
+
+** Changes
+
+*** Snippets for Yasnippet must now be installed separately. The
+submodule linking to yasnippet-snippets was removed, as were the
+"classic" snippets that came with the GNU ELPA package. The latter
+can now be installed via the 'yasnippet-classic-snippets' package from
+GNU ELPA.
+See Github #848, #858, #834, #775.
+
+*** 'snippet-mode' no longer derives from 'text-mode'.
+It will derive from 'prog-mode' where available (Emacs 24.1 and newer)
+or 'fundamental-mode' otherwise. See Github #826.
+
+*** The default value of 'yas-key-syntaxes' is changed
+Longer snippet abbrev keys are now preferred over shorter ones.
+See Github #805.
+
+*** New snippets are now created for the current major mode by default
+Previously, extra activated modes could be guessed first.
+See Github #875.
+
+*** Yasnippet supports 'unload-feature' via 'yasnippet-unload-function'
+See Github #753, #891.
+
+*** New command 'yas-skip-and-clear-field' conditionally bound to 'C-d'
+replaces obsoleted 'yas-skip-and-clear-or-delete-char'. The new
+function may be bound to any key via the conditional binding value
+'yas-maybe-skip-and-clear-field', instead of hardcoding the
+'delete-char' fallback action. See Github #408, #892.
+
+*** 'yas-lookup-snippet' now returns a struct
+This allows 'yas-expand-snippet' to take looked up snippet's
+environment into account. 'yas-expand-snippet' handles both
+structured snippets, and plain text snippet bodies.
+See Github #897.
+
+** Fixed bugs
+
+*** Avoid crashing due to Emacs Bug#30931
+This prevents yasnippet's routines from triggering the bug, although
+it is still possible to trigger it independently.
+
+*** Don't enable undo when it's disabled
+
+*** yas-also-auto-indent-first-line is once again respected
+Yasnippet was behaving as if it was always t for single line snippets.
+See Github #912.
+
+*** Fixed handling of fixed indent with fields at beginning of line
+See Github #906, #908.
+
+*** Fixed incorrect snippets leaving "bad memory"
+and possibly corrupting future expansions.
+See Github #800.
+
+*** 'global-whitespace-mode' now functions in new snippet buffers.
+To fix this, the buffer name for new snippet buffers is now '+new
+snippet+' instead of '*new snippet*'. See Github #842.
+
+*** Nest snippet expansion may clear default field text
+See Github #844.
+
+*** Fixed undo list corruption snippet expand+indent.
+See Github #869.
+
+*** The '# --' marker in snippets now allows trailing whitespace.
+See Github #862.
+
+*** Fixed handling of nested simple $n fields
+See Github #824, #894.
+
+* 0.12.2 (Aug 28, 2017)
+
+** The new option 'yas-also-auto-indent-empty-lines' allows restoring
+the old indent behavior. See Github #850, #710, #685, #679.
+
+** Keybinding triggered snippets once again deactivate the mark.
+See Github #840.
+
+
+* 0.12.1 (Jul 23, 2017)
+
+This is a quick bugfix release.
+
+** Compilation errors in yasnippet-tests.el and yasnippet-debug.el are fixed.
+
+** A snippet-local setting of 'yas-indent-line' is now respected
+during indentation triggered by auto-fill as well. See Github #838.
+
+* 0.12.0 (Jul 17, 2017)
+
+** Changes and New Features
+
+*** Snippets can now expand in strings & comments by default again.
+'yas-buffer-local-condition' is now a defcustom See Github #774.
+
+*** 'yas-after-exit-snippet-hook' can now be bound in 'expand-env' of
+snippets. See Github #28, #702, #779, #786.
+
+*** Snippets under directories in 'yas-snippet-dirs' are now in
+snippet-mode automatically.
+
+*** Snippets can now be expanded in org source blocks, if
+'org-src-tab-acts-natively' and 'org-src-fontify-natively' are set.
+See Github #761.
+
+*** 'yas-fallback-behavior' is now obsolete, 'yas-expand' is now bound
+conditionally with an extended menu item, 'yas-maybe-expand'.
+Therefore users wanting to bind 'yas-expand' to a different key, SPC
+for example, should do
+
+ (define-key yas-minor-mode-map (kbd "SPC") yas-maybe-expand)
+
+See Github #760, #808.
+
+*** The documentation build output is now reproducible. The timestamp
+now depends on the commit date, or the environment variable
+SOURCE_DATE_EPOCH is that is set.
+
+*** 'yas-indent-line' and 'expand-env' are now respected during mirror
+updates. See Github #743.
+
+*** New function 'yas-active-snippets'. Renamed from
+'yas--snippets-at-point', which remains as an obsolete alias. See
+Github #727.
+
+*** New custom option 'yas-overlay-priority'. This is can be used to
+give the snippet navigation keymaps higher priority than keymaps from
+overlays created by other packages, like 'auto-complete'. See Github
+#828.
+
+** Fixed bugs
+
+*** Snippets having ${0:soon-to-be-deleted} with no other fields now
+correctly put the field 0 text in the active region after exiting.
+See Github #653.
+
+*** Fix undo of snippet insertion which also triggers indentation.
+See Github #821.
+
+*** Fixed a bug causing whitespace loss between mirrors.
+
+*** Fixed several bugs causing problems when combining Yasnippet with
+other modes and packages, like 'auto-fill-mode', 'c++-mode',
+'rust-mode', and 'lentic'.
+
+**** Fix another bug with auto-fill-mode.
+See Github #784, #794.
+
+**** Fix a bug in parsing of snippet fields for modes that use the
+'syntax-table' text property, 'c++-mode' is one example of this. See
+Github #815.
+
+**** 'syntax-propertize-function' is now restored before indenting the
+snippet. This improves compatibility with modes which rely on it for
+indentation, like 'rust-mode'. See Github #782, #818.
+
+**** Avoid trying to delete a snippet which is already deleted. This
+prevents an error when using 'rust-mode's 'rust-format-buffer'
+command.
+
+**** Ensure inhibit-modification-hooks is nil while modifying buffer.
+This fixes problems for packages relying on modification hooks, like
+'lentic'. See Github #756, #712.
+
+
* 0.11.0 (Oct 26, 2016)
** Changes and New Features
diff --git a/README.mdown b/README.mdown
index f4e0594..76772c8 100644
--- a/README.mdown
+++ b/README.mdown
@@ -55,25 +55,18 @@ where you want YASnippet enabled.
<a name="import"></a>
-Yasnippet no longer bundles snippets directly, but it's very easy to
+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] the snippet collection of
+1. [yasnippet-snippets] - a snippet collection package maintained by
[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/`
+ It can be installed with `M-x install-package RET
+ yasnippet-snippets` if you have added MELPA to your package
+ sources.
- Points to a github repo of the [yasmate] tool, which is dedicated
- to converting textmate bundles into yasnippet snippets.
+2. [yasmate] a 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`
@@ -96,16 +89,22 @@ should be added like this to `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.
# Manual, issues etc
-Please refer to the comprehensive [documentation][docs] for full
-customisation and support. If you find a bug in the code or in the
-documentation, please report it on [the GitHub issue tracker][issues].
+There's comprehensive [documentation][docs] on using and customising
+YASnippet.
+
+There's a [list of support issues][support-issues], with solutions to
+common problems and practical snippet examples.
+
+The [Github issue tracker][issues] is where most YASnippet-related
+discussion happens. Nevertheless, since YASnippet is a part of Emacs,
+you may alternatively report bugs to the main Emacs bug list,
+bug-gnu-emacs@gnu.org, putting "yasnippet" somewhere in the subject.
## Important note regarding bug reporting
@@ -153,16 +152,11 @@ 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, unless we ask for it.
-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 it's better to create a
-github issue clearly marking your intent (user support/bug/feature
-request).
-
Finally, thank you very much for using YASnippet!
[docs]: http://joaotavora.github.io/yasnippet/
[issues]: https://github.com/joaotavora/yasnippet/issues
+[support-issues]: https://github.com/joaotavora/yasnippet/issues?q=label%3Asupport
[googlecode tracker]: http://code.google.com/p/yasnippet/issues/list
[forum]: http://groups.google.com/group/smart-snippet
[melpa]: http://melpa.milkbox.net/
diff --git a/Rakefile b/Rakefile
index 48a2086..c63d269 100644
--- a/Rakefile
+++ b/Rakefile
@@ -20,6 +20,12 @@ task :tests do
" --batch -f ert-run-tests-batch-and-exit"
end
+desc "run test in interactive mode"
+task :itests do
+ sh "#{$EMACS} -Q -L . -l yasnippet-tests.el" +
+ " --eval \"(call-interactively 'ert)\""
+end
+
desc "create a release package"
task :package do
release_dir = "pkg/yasnippet-#{$version}"
@@ -44,6 +50,10 @@ task :release => [:package, 'doc:archive'] do
raise "Not implemented for github yet!"
end
+# rake doc[../htmlize]
+#
+# To do this interactively, load doc/yas-doc-helper, open one of the
+# org files, and do `C-c C-e P'.
desc "Generate document"
task :doc, [:htmlize] do |t, args|
load_path = '-L .'
@@ -76,16 +86,12 @@ namespace :doc do
Dir.glob("doc/stylesheets/*.css").each do |file|
FileUtils.cp file, 'doc/gh-pages/stylesheets'
end
- curRev = `git rev-parse --verify HEAD`.chomp()
+ curRev = `git describe`.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()}'"
@@ -98,14 +104,22 @@ end
desc "Compile yasnippet.el into yasnippet.elc"
rule '.elc' => '.el' do |t|
- set_warnings = ""
+ cmdline = $EMACS + ' --batch -L .'
if ENV['warnings']
- set_warnings = " --eval \"(setq byte-compile-warnings #{ENV['warnings']})\""
+ cmdline += " --eval \"(setq byte-compile-warnings #{ENV['warnings']})\""
end
- sh "#{$EMACS} --batch -L . --eval \"(setq byte-compile-error-on-warn t)\"" +
- "#{set_warnings} -f batch-byte-compile #{t.source}"
+ if ENV['Werror']
+ cmdline += " --eval \"(setq byte-compile-error-on-warn #{ENV['Werror']})\""
+ end
+ if ENV['Wlexical']
+ cmdline += " --eval \"(setq byte-compile-force-lexical-warnings #{ENV['Wlexical']})\""
+ end
+ cmdline +=" -f batch-byte-compile #{t.source}"
+
+ sh cmdline
end
task :compile => FileList["yasnippet.el"].ext('elc')
+task :compile_all => FileList["*.el"].ext('elc')
task :default => :doc
diff --git a/debian/.gitignore b/debian/.gitignore
deleted file mode 100644
index adbea6e..0000000
--- a/debian/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-/*.debhelper.log
-/*.substvars
-/files
-/yasnippet/
diff --git a/debian/changelog b/debian/changelog
index ff7b9ba..b686761 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,77 @@
+yasnippet (0.13.0-2) unstable; urgency=medium
+
+ * Team upload.
+ * Depend on elpa-htmlize instead of emacs-goodies-el. (Closes: #898579)
+ * Lowercase short descriptions
+ * Declare Standards-Version: 4.1.5. (No additional changes needed)
+ * debian/rules: Load htmlize before attempting to build docs.
+ - Needed to fontify src blocks.
+ * Restore build-dep on yasnippet-snippets for self-tests.
+ - Solves "Prepared just-in-time loading of snippets
+ (but no snippets found)"
+ - While it is not needed for tests to pass, elpa-yasnippet-snippets
+ enhances the quality of those tests.
+ * debian/control: Add minor enhancements to the comments.
+
+ -- Nicholas D Steeves <nsteeves@gmail.com> Wed, 01 Aug 2018 22:10:25 -0400
+
+yasnippet (0.13.0-1) unstable; urgency=medium
+
+ * Team upload.
+ * New upstream release.
+ * debian/control:
+ - Recommend elpa-yasnippet-snippets rather than yasnippet-snippets.
+ - Drop yasnippet-snippets from Build-Depends; it does not seem to be
+ needed for self-tests to pass.
+ - Switch Vcs from alioth to salsa.
+ - Switch to debhelper 11.
+ - Update Maintainer team name and email address.
+ - Drop emacs24 from Enhances.
+ * Declare compat level 11.
+ * Drop 0003-Debian-yas-installed-snippets-dir.patch
+ - Uses obsolete yas-installed-snippets-dir.
+ - Now elpa-yasnippets-snippets defines yasnippet-snippets-dir.
+ * Declare Standards-Version: 4.1.4.
+ - debian/control: Drop Built-Using because §7.8 says it should be used
+ "exactly when there are license or DFSG requirements to retain full
+ source code", and such requirements are not present in yasnippet.
+
+ -- Nicholas D Steeves <nsteeves@gmail.com> Sun, 13 May 2018 15:22:01 -0400
+
+yasnippet (0.12.2-2) unstable; urgency=medium
+
+ * Remove obsolete conffile /etc/emacs/site-start.d/50yasnippet.el
+ (Closes: #884621).
+ Thanks H.-Dirk Schmitt for noting the problem.
+ * Priority extra->optional.
+ * Declare compliance with Debian Policy 4.1.2.
+
+ -- Sean Whitton <spwhitton@spwhitton.name> Fri, 22 Dec 2017 10:07:25 +0000
+
+yasnippet (0.12.2-1) unstable; urgency=medium
+
+ * New upstream release.
+ * Tidy Copyright: fields & bump copyright years.
+ * Add a build-conflict with elpa-org.
+ See https://github.com/joaotavora/yasnippet/issues/852
+ * Bump std-ver to 4.1.0 (no changes required).
+
+ -- Sean Whitton <spwhitton@spwhitton.name> Sat, 02 Sep 2017 13:21:36 -0700
+
+yasnippet (0.12.1-1) unstable; urgency=medium
+
+ * New upstream release (Closes: #771159).
+ * Drop 0001-typos-and-grammar.patch
+ Merged upstream.
+ * Drop 0002-Avoiding-having-git-as-a-building-dependency.patch
+ Obsoleted by upstream's switch to use SOURCE_DATE_EPOCH.
+ * Refresh 0003-Debian-yas-installed-snippets-dir.patch
+ * Drop 0004-no-timestamp-in-html-head.patch
+ Merged upstream.
+ * Bump standards version to 4.0.0 (no changes required).
+
+ -- Sean Whitton <spwhitton@spwhitton.name> Mon, 24 Jul 2017 15:18:42 -0700
+
yasnippet (0.11.0-2) unstable; urgency=medium
* Add missing Vcs-* fields.
diff --git a/debian/compat b/debian/compat
index f599e28..b4de394 100644
--- a/debian/compat
+++ b/debian/compat
@@ -1 +1 @@
-10
+11
diff --git a/debian/control b/debian/control
index 7686b99..c7dca53 100644
--- a/debian/control
+++ b/debian/control
@@ -1,39 +1,41 @@
Source: yasnippet
Section: lisp
-Priority: extra
-Maintainer: Debian Emacs addons team <pkg-emacsen-addons@lists.alioth.debian.org>
+Priority: optional
+Maintainer: Debian Emacsen team <debian-emacsen@lists.debian.org>
Uploaders:
Barak A. Pearlmutter <bap@debian.org>,
Alberto Luaces Fernández <aluaces@udc.es>,
Sean Whitton <spwhitton@spwhitton.name>,
Build-Depends:
- debhelper (>= 10),
+ debhelper (>= 11~),
dh-elpa,
- emacs-goodies-el,
- yasnippet-snippets,
-Standards-Version: 3.9.8
+ elpa-htmlize,
+ elpa-yasnippet-snippets
+# See changelog entry 0.13.0-2 for why we build-depend on snippets.
+# For an explanation of the build conflict on elpa-org,
+# see https://github.com/joaotavora/yasnippet/issues/852
+Build-Conflicts: elpa-org
+Standards-Version: 4.1.5
Homepage: https://github.com/joaotavora/yasnippet
Testsuite: autopkgtest-pkg-elpa
-Vcs-Git: https://anonscm.debian.org/git/pkg-emacsen/pkg/yasnippet.git
-Vcs-Browser: https://anonscm.debian.org/git/pkg-emacsen/pkg/yasnippet.git
+Vcs-Browser: https://salsa.debian.org/emacsen-team/yasnippet
+Vcs-Git: https://salsa.debian.org/emacsen-team/yasnippet.git
Package: elpa-yasnippet
Architecture: all
Depends:
${elpa:Depends},
${misc:Depends},
-Built-Using: ${misc:Built-Using}
Recommends:
- yasnippet-snippets,
+ elpa-yasnippet-snippets,
Enhances:
emacs,
- emacs24,
emacs25,
Breaks:
yasnippet (<< 0.11.0),
Provides:
yasnippet,
-Description: Template system for Emacs
+Description: template system for Emacs
YASnippet (yet another snippet extension for Emacs) is a template
system for Emacs. It allows you to type an abbreviation and
automatically expand the abbreviation into function templates.
@@ -46,7 +48,7 @@ Architecture: all
Depends:
elpa-yasnippet,
${misc:Depends},
-Description: Transition Package, yasnippet to elpa-yasnippet
+Description: transition Package, yasnippet to elpa-yasnippet
The yasnippet emacs addon has been elpafied. This dummy package
helps ease transition from yasnippet to elpa-yasnippet, and may
safely be removed.
diff --git a/debian/copyright b/debian/copyright
index b9d0ce9..a91fb79 100644
--- a/debian/copyright
+++ b/debian/copyright
@@ -4,15 +4,14 @@ Source: https://github.com/capitaomorte/yasnippet
Files: *
License: GPL-3+
-Copyright: 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>
+Copyright: (C) 2008-2017 Free Software Foundation, Inc.
Files: debian/*
License: GPL-3+
Copyright: 2009 Julián Hernández Gómez <julianhernandez@gmail.com>
2015 Alberto Luaces <aluaces@udc.es>, Barak A. Pearlmutter <bap@debian.org>
2016 Sean Whitton <spwhitton@spwhitton.name>, Barak A. Pearlmutter <bap@debian.org>
+ 2017 Sean Whitton <spwhitton@spwhitton.name>
License: GPL-3+
.
diff --git a/debian/patches/0001-typos-and-grammar.patch b/debian/patches/0001-typos-and-grammar.patch
deleted file mode 100644
index 70bd0af..0000000
--- a/debian/patches/0001-typos-and-grammar.patch
+++ /dev/null
@@ -1,45 +0,0 @@
-From: "Barak A. Pearlmutter" <barak+git@pearlmutter.net>
-Date: Mon, 12 Oct 2015 19:29:44 +0100
-Subject: typos and grammar
-
----
- doc/faq.org | 18 +++++++++---------
- 1 file changed, 9 insertions(+), 9 deletions(-)
-
-diff --git a/doc/faq.org b/doc/faq.org
-index 79249e8..f40cae8 100644
---- a/doc/faq.org
-+++ b/doc/faq.org
-@@ -4,14 +4,14 @@
-
- * 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.
-+If there is a newline at the end of a snippet definition file,
-+YASnippet will add a newline when expanding that snippet. When editing
-+or saving a snippet file, please be careful not to accidentally add a
-+terminal newline.
-
--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.
-+Note that 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 automatically.
-
- * Why doesn't TAB expand a snippet?
-
-@@ -27,8 +27,8 @@ code to your =.emacs= /before/ loading YASnippet:
- 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.
-+The command =M-x yas-global-mode= turns YASnippet on 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
diff --git a/debian/patches/0002-Avoiding-having-git-as-a-building-dependency.patch b/debian/patches/0002-Avoiding-having-git-as-a-building-dependency.patch
deleted file mode 100644
index fd0a40e..0000000
--- a/debian/patches/0002-Avoiding-having-git-as-a-building-dependency.patch
+++ /dev/null
@@ -1,25 +0,0 @@
-From: Alberto Luaces <aluaces@udc.es>
-Date: Fri, 15 May 2015 20:28:25 +0200
-Subject: Avoiding having git as a building dependency.
-
----
- doc/yas-doc-helper.el | 6 +-----
- 1 file changed, 1 insertion(+), 5 deletions(-)
-
-diff --git a/doc/yas-doc-helper.el b/doc/yas-doc-helper.el
-index 8459eb7..61059ee 100755
---- a/doc/yas-doc-helper.el
-+++ b/doc/yas-doc-helper.el
-@@ -128,11 +128,7 @@
-
- (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)))))
-+ (rev "0.9.0-beta")
- (proj-plist
- `(,@(when (fboundp 'org-html-publish-to-html)
- '(:publishing-function org-html-publish-to-html))
diff --git a/debian/patches/0003-Debian-yas-installed-snippets-dir.patch b/debian/patches/0003-Debian-yas-installed-snippets-dir.patch
deleted file mode 100644
index 7bb5888..0000000
--- a/debian/patches/0003-Debian-yas-installed-snippets-dir.patch
+++ /dev/null
@@ -1,41 +0,0 @@
-From: Sean Whitton <spwhitton@spwhitton.name>
-Date: Sun, 27 Nov 2016 22:05:41 -0700
-Subject: Debian-yas-installed-snippets-dir
-
-This replaces the old emacsen-startup script.
-
-Forwarded: not-needed
----
- yasnippet.el | 12 ++++++++----
- 1 file changed, 8 insertions(+), 4 deletions(-)
-
-diff --git a/yasnippet.el b/yasnippet.el
-index a6977d1..22477cc 100644
---- a/yasnippet.el
-+++ b/yasnippet.el
-@@ -153,9 +153,9 @@
- :group 'editing)
-
- (defvar yas-installed-snippets-dir nil)
--(setq yas-installed-snippets-dir
-- (when load-file-name
-- (expand-file-name "snippets" (file-name-directory load-file-name))))
-+(let ((snippets-directory "/usr/share/yasnippet-snippets"))
-+ (when (file-exists-p snippets-directory)
-+ (setq yas-installed-snippets-dir snippets-directory)))
-
- (defconst yas--default-user-snippets-dir
- (expand-file-name "snippets" user-emacs-directory))
-@@ -197,7 +197,11 @@ created with `yas-new-snippet'. "
- (stringp (symbol-value e)))
- (symbol-value e))
- (t
-- (error "[yas] invalid element %s in `yas-snippet-dirs'" e)))))
-+ (error "[yas] invalid element %s in `yas-snippet-dirs'
-+
-+Perhaps you need to install the yasnippet-snippets Debian package,
-+or remove %s from `yas-snippet-dirs'
-+before loading yasnippet." e e)))))
-
- (defcustom yas-new-snippet-default "\
- # -*- mode: snippet -*-
diff --git a/debian/patches/0004-no-timestamp-in-html-head.patch b/debian/patches/0004-no-timestamp-in-html-head.patch
deleted file mode 100644
index 769487c..0000000
--- a/debian/patches/0004-no-timestamp-in-html-head.patch
+++ /dev/null
@@ -1,24 +0,0 @@
-From: Sean Whitton <spwhitton@spwhitton.name>
-Date: Fri, 9 Dec 2016 21:28:56 -0700
-Subject: no timestamp in html head
-
-Makes the documentation build reproducible.
-
-Forwarded: https://github.com/joaotavora/yasnippet/pull/757
----
- doc/org-setup.inc | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/doc/org-setup.inc b/doc/org-setup.inc
-index 6ad09c9..60b9382 100644
---- a/doc/org-setup.inc
-+++ b/doc/org-setup.inc
-@@ -4,7 +4,7 @@
-
- #+LINK: sym file:snippet-reference.org::#%s
-
--#+OPTIONS: author:nil num:nil
-+#+OPTIONS: author:nil num:nil timestamp:nil
- #+AUTHOR:
- # org < 8.0 use +STYLE, after use +HTML_HEAD
- #+STYLE: <link rel="stylesheet" type="text/css" href="stylesheets/manual.css" />
diff --git a/debian/patches/series b/debian/patches/series
deleted file mode 100644
index 9d6a280..0000000
--- a/debian/patches/series
+++ /dev/null
@@ -1,4 +0,0 @@
-0001-typos-and-grammar.patch
-0002-Avoiding-having-git-as-a-building-dependency.patch
-0003-Debian-yas-installed-snippets-dir.patch
-0004-no-timestamp-in-html-head.patch
diff --git a/debian/rules b/debian/rules
index 4b7ff67..01eeea8 100755
--- a/debian/rules
+++ b/debian/rules
@@ -4,4 +4,4 @@
dh $@ --with elpa
override_dh_auto_build:
- HOME=$$(pwd) emacs -Q -L . --batch -l doc/yas-doc-helper.el -f yas--generate-html-batch
+ HOME=$$(pwd) emacs -Q -L /usr/share/emacs/site-lisp/elpa-src/htmlize-* -L . --batch -l htmlize -l doc/yas-doc-helper.el -f yas--generate-html-batch
diff --git a/debian/yasnippet.maintscript b/debian/yasnippet.maintscript
new file mode 100644
index 0000000..55a6bde
--- /dev/null
+++ b/debian/yasnippet.maintscript
@@ -0,0 +1 @@
+rm_conffile /etc/emacs/site-start.d/50yasnippet.el
diff --git a/doc/faq.org b/doc/faq.org
index f40cae8..9994e7b 100644
--- a/doc/faq.org
+++ b/doc/faq.org
@@ -2,6 +2,11 @@
#+TITLE: Frequently Asked Questions
+- *Note*: In addition to the questions and answers presented here,
+ you might also with to visit the list of [[https://github.com/joaotavora/yasnippet/issues?q=label%3Asupport][solved support issues]] in
+ the Github issue tracker. It might be more up-to-date than this
+ list.
+
* Why is there an extra newline?
If there is a newline at the end of a snippet definition file,
@@ -13,73 +18,6 @@ Note that 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 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.
-
-The command =M-x yas-global-mode= turns YASnippet on 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
@@ -99,7 +37,7 @@ same property in YASnippet's overlays, even if one sets the latter's
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?
+* How do 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:
@@ -107,7 +45,7 @@ Edit the keymaps [[sym:yas-minor-mode-map][=yas-minor-mode-map=]] and
#+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)
+ (define-key yas-minor-mode-map (kbd "<the new key>") yas-maybe-expand)
;;keys for navigation
(define-key yas-keymap [(tab)] nil)
diff --git a/doc/nav-menu.html.inc b/doc/nav-menu.html.inc
index 2a3f0a1..3e74cf8 100644
--- a/doc/nav-menu.html.inc
+++ b/doc/nav-menu.html.inc
@@ -3,8 +3,8 @@
<li> <a href="index.html">Overview</a>
<li> <a href="https://github.com/joaotavora/yasnippet/blob/master/README.mdown">
Intro and Tutorial</a>
- <li class="center">Snippet
- <ul>
+ <li class="center border">Snippet
+ <ul class="nopad">
<li> <a href="snippet-organization.html">Organization</a>
<li> <a href="snippet-expansion.html">Expansion</a>
<li> <a href="snippet-development.html">Development</a>
diff --git a/doc/snippet-development.org b/doc/snippet-development.org
index ffe648c..9112cd0 100644
--- a/doc/snippet-development.org
+++ b/doc/snippet-development.org
@@ -80,7 +80,7 @@ Here's a list of currently supported directives:
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
+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
@@ -89,8 +89,8 @@ 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.
+If you omit this name, it will default to the file name the snippet
+was loaded from.
** =# condition:= snippet condition
@@ -277,16 +277,16 @@ like this:
${N:default value}
#+END_SRC
-They acts as the default value for a tab stop. But when you firstly
+They act as the default value for a tab stop. But when you first
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-fields][mirrors]] or
[[mirror-transformations][transformations]] for this field.
** Mirrors <<mirrors-fields>>
-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:
+We refer the tab stops with placeholders as a /field/. A field can
+have mirrors. *All* mirrors get updated whenever you update any field
+text. Here's an example:
#+BEGIN_SRC snippet
\begin{${1:enumerate}}
@@ -362,12 +362,26 @@ is not. Here's an snippet for rst title:
$0
#+END_SRC
+Note that a mirror with a transform is not restricted to the text of
+the field it is mirroring. By making use of [[sym:yas-field-value][=yas-field-value=]], a
+mirror can look at any of the snippet's field (as mentioned above, all
+mirrors are updated when any field is updated). Here is an example
+which shows a "live" result of calling format:
+
+#+BEGIN_SRC snippet
+(format "${1:formatted %s}" "${2:value}")
+=> "${1:$(ignore-errors (format (yas-field-value 1) (yas-field-value 2)))}"
+#+END_SRC
+
+To keep the example simple, it uses =ignore-errors= to suppress errors
+due to incomplete format codes.
+
** 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.
+fields. These work mostly like mirror transformations. However, they
+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
@@ -431,7 +445,7 @@ From version 0.6 on, you can also have nested placeholders of the type:
#+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
+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.
diff --git a/doc/snippet-expansion.org b/doc/snippet-expansion.org
index 4bd133b..a699d57 100644
--- a/doc/snippet-expansion.org
+++ b/doc/snippet-expansion.org
@@ -21,7 +21,7 @@
- Using hippie-expand
- - Call [[sym:yas-insert-snippet][=yas-insert-snippet=]] (use =M-x yas-insert-snippet== or its
+ - 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
@@ -32,17 +32,33 @@
** Trigger key
[[sym:yas-expand][=yas-expand=]] tries to expand a /snippet abbrev/ (also known as
-/snippet key/) before point.
+/snippet key/) before point. YASnippet also provides a /conditional
+binding/ for this command: the variable [[sym:yas-expand][=yas-maybe-expand=]] contains a
+special value which, when bound in a keymap, tells Emacs to call
+[[sym:yas-expand][=yas-expand=]] if and only if there is a snippet abbrev before point.
+If there is no snippet to expand, Emacs will behave as if [[sym:yas-expand][=yas-expand=]]
+is unbound and so will run whatever command is bound to that key
+normally.
-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:
+When [[sym:yas-minor-mode][=yas-minor-mode=]] is enabled, it binds [[sym:yas-maybe-expand][=yas-maybe-expand=]] to =TAB=
+and =<tab>= by default, however, you can freely remove those bindings:
#+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
+And set your own:
+
+#+begin_src emacs-lisp :exports code
+ ;; Bind `SPC' to `yas-expand' when snippet expansion available (it
+ ;; will still call `self-insert-command' otherwise).
+ (define-key yas-minor-mode-map (kbd "SPC") yas-maybe-expand)
+ ;; Bind `C-c y' to `yas-expand' ONLY.
+ (define-key yas-minor-mode-map (kbd "C-c y") #'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=:
@@ -50,28 +66,18 @@ command [[sym:yas-global-mode][=yas-global-mode=]]. This will enable a modeline
[[./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.
+YASnippet in some buffers by calling [[sym:yas-minor-mode][=yas-minor-mode=]] with a negative
+argument 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.
+YASnippet used to support a more complicated way of sharing
+keybindings before [[sym:yas-expand][=yas-maybe-expand=]] was added. This is now
+obsolete.
** Insert at point
-The command [[#yas-insert-snippet][=yas-insert-snippet=]] lets you insert snippets at point
+The command [[sym: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.
@@ -88,7 +94,7 @@ The prompting methods used are again controlled by
It's often useful to inject already written text in the middle of a
snippet. The variable [[sym:yas-wrap-around-region][=yas-wrap-around-region=]] when to t substitute
the region contents into the =$0= placeholder of a snippet expanded by
-[[#yas-insert-snippet][=yas-insert-snippet=]]. Setting it to a character value (e.g. =?0=)
+[[sym:yas-insert-snippet][=yas-insert-snippet=]]. Setting it to a character value (e.g. =?0=)
will insert the contents of corresponding register.
Older (versions 0.9.1 and below) of Yasnippet, supported a setting of
@@ -163,7 +169,7 @@ In particular, the following things matter:
- Buffer-local list of extra modes
- Use [[#yas-activate-extra-mode][=yas-activate-extra-mode=]] to
+ Use [[sym: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:
@@ -175,32 +181,30 @@ In particular, the following things matter:
(yas-activate-extra-mode 'rails-mode)))
#+END_SRC
-- Buffer-local
- [[sym:yas-buffer-local-condition][=yas-buffer-local-condition=]]
- variable
+- 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.
+ be expanded in the current buffer. For example, the constant
+ [[sym:yas-not-string-or-comment-condition][=yas-not-string-or-comment-condition=]] has a value that disables
+ snippet expansion inside comments or string literals. See [[condition-system][the
+ condition system]] for more info.
-** The condition system
+** The condition system <<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-syntax-comment-or-string-p))=. 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-syntax-comment-or-string-p))= in =python-mode-hook=.
+If you use the =# condition := directive (see [[./snippet-development.org][Writing Snippets]]) you
+could just specify the condition for =if= to be =(not
+(python-syntax-comment-or-string-p))=. But how about =while=, =for=,
+etc? Writing the same condition for all the snippets is just boring.
+So you can instead set [[sym:yas-buffer-local-condition][=yas-buffer-local-condition=]] to =(not
+(python-syntax-comment-or-string-p))= 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
+inside a comment? Set [[sym:yas-buffer-local-condition][=yas-buffer-local-condition=]] like this
#+BEGIN_SRC emacs-lisp
(add-hook 'python-mode-hook
@@ -211,10 +215,10 @@ inside a comment? Set [[sym:yas-buffer-local-condition][=yas-buffer-local-condit
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.
+... and for a snippet that you want to expand in comments, specify a
+condition which evaluates to the symbol =force-in-comment=. Then it
+can be expanded as you expected, while other snippets like =if= still
+can't expanded in comments.
For the full set of possible conditions, see the documentation for
[[sym:yas-buffer-local-condition][=yas-buffer-local-condition=]].
diff --git a/doc/snippet-menu.org b/doc/snippet-menu.org
index 272ea16..fee3a19 100644
--- a/doc/snippet-menu.org
+++ b/doc/snippet-menu.org
@@ -24,7 +24,7 @@ In this menu, you can find
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=]]
+Also useful is the "Reload everything" item to invoke [[sym: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.
diff --git a/doc/snippet-organization.org b/doc/snippet-organization.org
index 746cff0..22d3e46 100644
--- a/doc/snippet-organization.org
+++ b/doc/snippet-organization.org
@@ -4,8 +4,9 @@
* 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.
+ Snippet collections can be stored in plain text files. They are
+ arranged by sub-directories naming *snippet tables*. These mostly
+ name Emacs major mode names.
#+begin_example
.
@@ -81,7 +82,7 @@
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
+ considered groups of snippets and [[file:snippet-menu.org][the menu]] is organized much more
cleanly:
[[./images/menu-groups.png]]
diff --git a/doc/stylesheets/manual.css b/doc/stylesheets/manual.css
index 9580347..74bfe16 100644
--- a/doc/stylesheets/manual.css
+++ b/doc/stylesheets/manual.css
@@ -1,5 +1,14 @@
-nav > ul > li.center > ul {
- padding: 0;
+.center { margin-left: auto; margin-right: auto; text-align: center; }
+.current {
+ font-weight: bold;
+ background-color: #E0E8F0;
+}
+
+body { background-color: #E4F0F4 }
+div#content {
+ max-width: 20cm;
+ margin-left: auto;
+ margin-right: auto;
}
nav li {
@@ -9,20 +18,22 @@ nav li {
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;
+.nopad {
+ padding: 0;
+}
+li.border {
+ border: solid;
+ border-width: 1px;
}
-#content {
- margin-left: 5%;
- margin-right: 10%;
+pre, code{ background-color: #F3F5F7; }
+code {
+ /* http://neugierig.org/software/chromium/notes/2009/09/monospace-fonts-workaround.html */
+ font-family: WorkAroundWebKitAndMozilla, monospace;
+ white-space: nowrap;
}
/* Styles for htmlize.el fontification. */
diff --git a/doc/stylesheets/styles.css b/doc/stylesheets/styles.css
deleted file mode 100644
index a158012..0000000
--- a/doc/stylesheets/styles.css
+++ /dev/null
@@ -1,93 +0,0 @@
-@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/doc/yas-doc-helper.el b/doc/yas-doc-helper.el
index 61059ee..f48628f 100644
--- a/doc/yas-doc-helper.el
+++ b/doc/yas-doc-helper.el
@@ -31,75 +31,103 @@
(require 'ox-publish))
(require 'yasnippet) ; docstrings must be loaded
-(defun yas--org-raw-html (tag content)
+(defun yas--org-raw-html (tag content &optional attrs)
;; in version 8.0 org-mode changed the export syntax, see
;; http://orgmode.org/worg/org-8.0.html#sec-8-1
(format (if (version< org-version "8.0.0")
"@<%s>%s@</%s>" ; old: @<tag>
"@@html:<%s>@@%s@@html:</%s>@@") ; new: @@html:<tag>@@
- tag content tag))
+ (concat tag (if attrs " ") attrs)
+ content tag))
(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)
- (yas--org-raw-html "code" "%s"))
- ((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))))
+ (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)\n" stars (yas--org-raw-html "code" symbol "class='function'")
+ (mapconcat (lambda (a)
+ (format (if (string-prefix-p "&" a)
+ "/%s/" "=%s=")
+ a))
+ args " ")))
+ (t
+ (format "%s %s\n" stars
+ (yas--org-raw-html "code" symbol "class='variable'")))))
+ (after-heading (format ":PROPERTIES:\n:CUSTOM_ID: %s\n:END:" symbol))
+ (text-quoting-style 'grave)
+ (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=]]
+ ;; (...) becomes #+BEGIN_SRC elisp (...) #+END_SRC
+ ;; Info node `(some-manual) Node Name' becomes
+ ;; [[https://www.gnu.org/software/emacs/manual/html_node/some-manual/Node-Name.html]
+ ;; [(some-manual) Node Name]]
+ ;;
+ ;; This is fairly fragile, though it seems to be working for
+ ;; now...
+ (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)
+ (yas--org-raw-html "code" "%s"))
+ ((null suffix) "/%s/"))))
+ (if fmt (format fmt prefix)
+ match1)))
+ body t t 1)
+ body (replace-regexp-in-string
+ "\\\\{[^}]+}"
+ (lambda (match)
+ (concat "#+BEGIN_EXAMPLE\n"
+ (substitute-command-keys match)
+ "#+END_EXAMPLE\n"))
+ body t t)
+ body (substitute-command-keys body)
+ body (replace-regexp-in-string
+ "Info node `(\\([-a-z]+\\)) \\([A-Za-z0-9 ]+\\)'"
+ (lambda (match)
+ (let* ((manual (match-string 1 match))
+ (node (match-string 2 match))
+ (html-node (replace-regexp-in-string " " "-" node t t)))
+ (format "Info node\
+ [[https://www.gnu.org/software/emacs/manual/html_node/%s/%s.html][(%s) %s]]"
+ manual html-node manual node)))
+ body t t)
+ body (replace-regexp-in-string
+ "`\\([-a-z]+\\)'"
+ #'(lambda (match)
+ (let* ((name (downcase (match-string 1 match)))
+ (sym (intern-soft name)))
+ (if (memq sym yas--exported-syms)
+ (format "[[#%s][=%s=]]" name name)
+ (format "=%s=" name))))
+ body t t)
+ body (replace-regexp-in-string
+ "\n\n +(.+\\(?:\n +.+\\)*"
+ (lambda (match)
+ (concat "\n#+BEGIN_SRC elisp\n"
+ match
+ "\n#+END_SRC\n"))
+ body t t))
+ ;; output the paragraph
+ (concat heading after-heading "\n" body)))
(defun yas--document-symbols (level &rest names-and-predicates)
(let ((sym-lists (make-vector (length names-and-predicates) nil))
@@ -126,22 +154,52 @@
;; This lets all the org files be exported to HTML with
;; `org-publish-current-project' (C-c C-e P).
+(defun yas--make-preamble (props)
+ "Return contents of nav-menu-html.inc.
+But replace link to \"current\" page with a span element."
+ (with-temp-buffer
+ (let ((dir (file-name-directory (plist-get props :input-file))))
+ (insert-file-contents (expand-file-name "nav-menu.html.inc" dir))
+ (goto-char (point-min))
+ (search-forward (concat "<a href=\""
+ (file-name-nondirectory
+ (plist-get props :output-file))
+ "\">"))
+ (replace-match "<span class='current'>")
+ (search-forward "</a>")
+ (replace-match "</span>")
+ (buffer-string))))
+
(let* ((dir (if load-file-name (file-name-directory load-file-name)
default-directory))
- (rev "0.9.0-beta")
+ (src-epoch (getenv "SOURCE_DATE_EPOCH"))
+ ;; Presence of SOURCE_DATE_EPOCH indicates a reproducible
+ ;; build, don't depend on git.
+ (rev (unless src-epoch
+ (ignore-errors
+ (car (process-lines "git" "describe" "--dirty")))))
+ (date (format-time-string
+ "(%Y-%m-%d %H:%M:%S)"
+ (seconds-to-time
+ (string-to-number
+ (or (if rev (car (process-lines "git" "show" "--format=%ct"))
+ src-epoch)
+ "0")))
+ t))
(proj-plist
`(,@(when (fboundp 'org-html-publish-to-html)
'(:publishing-function org-html-publish-to-html))
: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-preamble yas--make-preamble
+ ;;:with-broken-links mark
:html-postamble
- ,(concat "<hr><p class='creator'>Generated by %c on %d from "
- rev "</p>\n"
+ ,(concat "<hr><p class='creator'>Generated by %c from "
+ (or rev yas--version) " " date "</p>\n"
"<p class='xhtml-validation'>%v</p>\n")))
(project (assoc "yasnippet" org-publish-project-alist)))
+ (when rev ;; Rakefile :doc:upload uses "html-revision".
+ (with-temp-file (expand-file-name "html-revision" dir)
+ (princ rev (current-buffer))))
(if project
(setcdr project proj-plist)
(push `("yasnippet" . ,proj-plist)
diff --git a/yasnippet-debug.el b/yasnippet-debug.el
index b12bcd4..abce89c 100644
--- a/yasnippet-debug.el
+++ b/yasnippet-debug.el
@@ -1,8 +1,8 @@
-;;; yasnippet-debug.el --- debug functions for yasnippet
+;;; yasnippet-debug.el --- debug functions for yasnippet -*- lexical-binding: t -*-
-;; Copyright (C) 2010, 2013, 2014 Free Software Foundation, Inc.
+;; Copyright (C) 2010, 2013-2014, 2017-2018 Free Software Foundation, Inc.
-;; Author: João Távora
+;; Author: João Távora
;; Keywords: emulations, convenience
;; This program is free software; you can redistribute it and/or modify
@@ -20,114 +20,336 @@
;;; Commentary:
-;; Just some debug functions
-
+;; Some debug functions. When loaded from the command line, provides
+;; quick way to test out snippets in a fresh Emacs instance.
+;;
+;; emacs -Q -l yasnippet-debug [-v[v]]
+;; [-M:<modename>] [-M.<filext>] [-S:[<snippet-file|name>]]
+;; [-- <more-arguments-passed-to-Emacs>...]
+;;
+;; See the source in `yas-debug-process-command-line' for meaning of
+;; args.
+;;
;;; Code:
-(require 'yasnippet)
-(require 'cl)
-
-(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: %s 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: %s 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 (min 19
- (length buffer-undo-list)))))
- (dolist (undo-elem first-ten)
- (princ (format "%2s: %s\n" (position undo-elem first-ten) (truncate-string-to-width (format "%s" undo-elem) 70))))))))
+(defconst yas--loaddir
+ (file-name-directory (or load-file-name buffer-file-name))
+ "Directory that yasnippet was loaded from.")
+
+(require 'yasnippet (if (boundp 'yas--loaddir)
+ ;; Don't require '-L <path>' when debugging.
+ (expand-file-name "yasnippet" yas--loaddir)))
+(require 'cl-lib)
+(eval-when-compile
+ (unless (fboundp 'cl-flet)
+ (defalias 'cl-flet 'flet)))
+(require 'color nil t)
+(require 'edebug)
+(eval-when-compile
+ (require 'subr-x nil t)
+ (cond ((fboundp 'when-let*) nil) ; Introduced in 26.
+ ((fboundp 'when-let) ; Introduced in 25.1,
+ (defalias 'when-let* 'when-let)) ; deprecated in 26.
+ (t (defmacro when-let* (key-vals &rest body)
+ (declare (indent 1) (debug ((symbolp form) body)))
+ (let ((key-val (pop key-vals)))
+ (if key-val
+ `(let ((,(car key-val) ,(cadr key-val)))
+ (if ,(car key-val)
+ (when-let* ,key-vals
+ ,@body)))
+ `(progn ,@body)))))))
+
+(defvar yas-debug-live-indicators
+ (make-hash-table :test #'eq))
+
+(defun yas-debug-live-colors ()
+ (let ((colors ()))
+ (maphash (lambda (_k v) (push (nth 1 (car v)) colors)) yas-debug-live-indicators)
+ colors))
+
+(defvar yas-debug-recently-live-indicators)
+
+(defun yas-debug-get-live-indicator (location)
+ (require 'color)
+ (when (boundp 'yas-debug-recently-live-indicators)
+ (push location yas-debug-recently-live-indicators))
+ (let (beg end)
+ (if (markerp location)
+ (setq beg (setq end (marker-position location)))
+ (setq beg (yas-debug-ov-fom-start location)
+ end (yas-debug-ov-fom-end location)))
+ (or (when-let* ((color-ov (gethash location yas-debug-live-indicators)))
+ (if (and beg end) (move-overlay (cdr color-ov) beg end)
+ (delete-overlay (cdr color-ov)))
+ color-ov)
+ (let* ((live-colors (yas-debug-live-colors))
+ (color
+ (cl-loop with best-color = nil with max-dist = -1
+ for color = (format "#%06X" (random #x1000000))
+ for comp = (if (fboundp 'color-complement)
+ (apply #'color-rgb-to-hex (color-complement color))
+ color)
+ if (< (color-distance color (face-foreground 'default))
+ (color-distance comp (face-foreground 'default)))
+ do (setq color comp)
+ for dist = (cl-loop for c in live-colors
+ minimize (color-distance c color))
+ if (or (not live-colors) (> dist max-dist))
+ do (setq best-color color) (setq max-dist dist)
+ repeat (if live-colors 100 1)
+ finally return `(:background ,best-color)))
+ (ov (make-overlay beg end)))
+ (if (markerp location)
+ (overlay-put ov 'before-string (propertize "↓" 'face color))
+ (overlay-put ov 'before-string (propertize "↘" 'face color))
+ (overlay-put ov 'after-string (propertize "↙" 'face color)))
+ (puthash location (cons color ov) yas-debug-live-indicators)))))
+
+(defun yas-debug-live-marker (marker)
+ (let* ((color-ov (yas-debug-get-live-indicator marker))
+ (color (car color-ov))
+ (ov (cdr color-ov))
+ (decorator (overlay-get ov 'before-string))
+ (str (format "at %d" (+ marker))))
+ (if (markerp marker)
+ (propertize str
+ 'cursor-sensor-functions
+ `(,(lambda (_window _oldpos dir)
+ (overlay-put
+ ov 'before-string
+ (propertize decorator
+ 'face (if (eq dir 'entered)
+ 'mode-line-highlight color)))))
+ 'face color)
+ str)))
+
+(defun yas-debug-ov-fom-start (ovfom)
+ (cond ((overlayp ovfom) (overlay-start ovfom))
+ ((integerp ovfom) ovfom)
+ (t (yas--fom-start ovfom))))
+(defun yas-debug-ov-fom-end (ovfom)
+ (cond ((overlayp ovfom) (overlay-end ovfom))
+ ((integerp ovfom) ovfom)
+ (t (yas--fom-end ovfom))))
+
+(defun yas-debug-live-range (range)
+ (let* ((color-ov (yas-debug-get-live-indicator range))
+ (color (car color-ov))
+ (ov (cdr color-ov))
+ (decorator-beg (overlay-get ov 'before-string))
+ (decorator-end (overlay-get ov 'after-string))
+ (beg (yas-debug-ov-fom-start range))
+ (end (yas-debug-ov-fom-end range)))
+ (if (and beg end (not (integerp beg)) (not (integerp end)))
+ (propertize (format "from %d to %d" (+ beg) (+ end))
+ 'cursor-sensor-functions
+ `(,(lambda (_window _oldpos dir)
+ (let ((face (if (eq dir 'entered)
+ 'mode-line-highlight color)))
+ (overlay-put ov 'before-string
+ (propertize decorator-beg 'face face))
+ (overlay-put ov 'after-string
+ (propertize decorator-end 'face face)))))
+ 'face color)
+ "<dead>")))
+
+(defmacro yas-debug-with-tracebuf (outbuf &rest body)
+ (declare (indent 1))
+ (let ((tracebuf-var (make-symbol "tracebuf")))
+ `(let ((,tracebuf-var (or ,outbuf (get-buffer-create "*YASnippet trace*"))))
+ (unless (eq ,tracebuf-var (current-buffer))
+ (cl-flet ((printf (fmt &rest args)
+ (with-current-buffer ,tracebuf-var
+ (insert (apply #'format fmt args)))))
+ (unless ,outbuf
+ (with-current-buffer ,tracebuf-var
+ (erase-buffer)
+ (when (fboundp 'cursor-sensor-mode)
+ (cursor-sensor-mode +1))
+ (setq truncate-lines t)))
+ (setq ,outbuf ,tracebuf-var)
+ (save-restriction
+ (widen)
+ ,@body))))))
+
+
+(defun yas-debug-snippet (snippet &optional outbuf)
+ (yas-debug-with-tracebuf outbuf
+ (when-let* ((overlay (yas--snippet-control-overlay snippet)))
+ (printf "\tsid: %d control overlay %s\n"
+ (yas--snippet-id snippet)
+ (yas-debug-live-range overlay)))
+ (when-let* ((active-field (yas--snippet-active-field snippet)))
+ (unless (consp (yas--field-start active-field))
+ (printf "\tactive field: #%d %s %s covering \"%s\"\n"
+ (or (yas--field-number active-field) -1)
+ (if (yas--field-modified-p active-field) "**" "--")
+ (yas-debug-live-range active-field)
+ (buffer-substring-no-properties (yas--field-start active-field) (yas--field-end active-field)))))
+ (when-let* ((exit (yas--snippet-exit snippet)))
+ (printf "\tsnippet-exit: %s next: %s\n"
+ (yas-debug-live-marker (yas--exit-marker exit))
+ (yas--exit-next exit)))
+ (dolist (field (yas--snippet-fields snippet))
+ (unless (consp (yas--field-start field))
+ (printf "\tfield: %d %s %s covering \"%s\" next: %s%s\n"
+ (or (yas--field-number field) -1)
+ (if (yas--field-modified-p field) "**" "--")
+ (yas-debug-live-range 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)
+ (format " parent: %s"
+ (yas--debug-format-fom-concise
+ (yas--field-parent-field field)))
+ "")))
+ (dolist (mirror (yas--field-mirrors field))
+ (unless (consp (yas--mirror-start mirror))
+ (printf "\t\tmirror: %s covering \"%s\" next: %s\n"
+ (yas-debug-live-range mirror)
+ (buffer-substring-no-properties (yas--mirror-start mirror) (yas--mirror-end mirror))
+ (yas--debug-format-fom-concise (yas--mirror-next mirror))))))))
+
+(defvar yas-debug-target-buffer nil)
+(defvar yas-debug-target-snippets nil nil)
+(make-variable-buffer-local 'yas-debug-target-snippets)
+
+(defvar yas-debug-undo nil)
+
+(defun yas-toggle-debug-undo (value)
+ (interactive (list (not yas-debug-undo)))
+ (setq yas-debug-undo value)
+ (yas--message 3 "debug undo %sabled" (if yas-debug-undo "en" "dis")))
+
+(defadvice yas--snippet-parse-create (before yas-debug-target-snippet (snippet))
+ (add-to-list 'yas-debug-target-snippets snippet))
+
+(defadvice yas--commit-snippet (after yas-debug-untarget-snippet (snippet))
+ (setq yas-debug-target-snippets
+ (remq snippet yas-debug-target-snippets))
+ (maphash (lambda (k color-ov)
+ (delete-overlay (cdr color-ov)))
+ yas-debug-live-indicators)
+ (clrhash yas-debug-live-indicators))
+
+(defun yas-debug-snippets (&optional outbuf hook)
+ "Print debug information on active snippets to buffer OUTBUF.
+If OUTBUF is nil, use a buffer named \"*YASsnippet trace*\".
+If HOOK is non-nil, install `yas-debug-snippets' in
+`post-command-hook' to update the information on every command
+after this one. If it is `snippet-navigation' then install hook
+buffer-locally, otherwise install it globally. If HOOK is
+`edebug-create', also instrument the function
+`yas--snippet-parse-create' with `edebug' and show its source."
+ (interactive (list nil t))
+ (condition-case err
+ (yas-debug-with-tracebuf outbuf
+ (unless (buffer-live-p yas-debug-target-buffer)
+ (setq yas-debug-target-buffer nil))
+ (with-current-buffer (or yas-debug-target-buffer (current-buffer))
+ (when yas-debug-target-snippets
+ (setq yas-debug-target-snippets
+ (cl-delete-if-not #'yas--snippet-p yas-debug-target-snippets)))
+ (let ((yas-debug-recently-live-indicators nil))
+ (dolist (snippet (or yas-debug-target-snippets
+ (yas-active-snippets)))
+ (printf "snippet %d\n" (yas--snippet-id snippet))
+ (yas-debug-snippet snippet outbuf))
+ (maphash (lambda (loc color-ov)
+ (unless (memq loc yas-debug-recently-live-indicators)
+ (delete-overlay (cdr color-ov))
+ (remhash loc yas-debug-live-indicators)))
+ yas-debug-live-indicators))
+ (when (and yas-debug-undo (listp buffer-undo-list))
+ (printf "Undo list has %s elements:\n" (length buffer-undo-list))
+ (cl-loop for undo-elem in buffer-undo-list
+ do (printf "%S\n" undo-elem))))
+ (when hook
+ (setq yas-debug-target-buffer (current-buffer))
+ (ad-enable-advice 'yas--snippet-parse-create 'before 'yas-debug-target-snippet)
+ (ad-activate 'yas--snippet-parse-create)
+ (ad-enable-advice 'yas--commit-snippet 'after 'yas-debug-untarget-snippet)
+ (ad-activate 'yas--commit-snippet)
+ (add-hook 'post-command-hook #'yas-debug-snippets
+ nil (eq hook 'snippet-navigation))
+ ;; Window management is slapped together, it does what I
+ ;; want when the caller has a single window open. Good
+ ;; enough for now.
+ (when (eq hook 'edebug-create)
+ (edebug-instrument-function 'yas--snippet-parse-create)
+ (let ((buf-point (find-function-noselect 'yas--snippet-parse-create)))
+ (with-current-buffer (car buf-point)
+ (goto-char (cdr buf-point)))))
+ outbuf))
+ ((debug error) (signal (car err) (cdr err)))))
+
+(defun yas-debug-snippet-create ()
+ (yas-debug-snippets nil 'create))
(defun yas--debug-format-fom-concise (fom)
(when fom
(cond ((yas--field-p fom)
(format "field %s from %d to %d"
(yas--field-number fom)
- (marker-position (yas--field-start fom))
- (marker-position (yas--field-end fom))))
+ (+ (yas--field-start fom))
+ (+ (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))))
+ (+ (yas--mirror-start fom))
+ (+ (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)))
+ (+ (yas--fom-start fom)))))))
+
+(defun yas-debug-process-command-line (&optional options)
+ "Implement command line processing."
+ (setq yas-verbosity 99)
+ (setq yas-triggers-in-field t)
+ (setq debug-on-error t)
+ (let* ((snippet-mode 'fundamental-mode)
+ (snippet-key nil))
+ (unless options
+ (setq options (cl-loop for opt = (pop command-line-args-left)
+ while (and opt (not (equal opt "--"))
+ (string-prefix-p "-" opt))
+ collect opt)))
+ (when-let* ((mode (cl-member "-M:" options :test #'string-prefix-p)))
+ (setq snippet-mode (intern (concat (substring (car mode) 3) "-mode"))))
+ (when-let* ((mode (cl-member "-M." options :test #'string-prefix-p)))
+ (setq snippet-mode
+ (cdr (cl-assoc (substring (car mode) 2) auto-mode-alist
+ :test (lambda (ext regexp) (string-match-p regexp ext))))))
+ (switch-to-buffer (get-buffer-create "*yas test*"))
+ (funcall snippet-mode)
+ (when-let* ((snippet-file (cl-member "-S:" options :test #'string-prefix-p)))
+ (setq snippet-file (substring (car snippet-file) 3))
+ (if (file-exists-p snippet-file)
+ (with-temp-buffer
+ (insert-file-contents snippet-file)
+ (let ((snippet-deflist (yas--parse-template snippet-file)))
+ (yas-define-snippets snippet-mode (list snippet-deflist))
+ (setq snippet-key (car snippet-deflist))))
+ (yas-reload-all)
+ (let ((template (yas--lookup-snippet-1 snippet-file snippet-mode)))
+ (if template
+ (setq snippet-key (yas--template-key template))
+ (error "No such snippet `%s'" snippet-file)))))
+ (display-buffer (find-file-noselect
+ (expand-file-name "yasnippet.el" yas--loaddir)))
+ (when-let* ((verbosity (car (or (member "-v" options) (member "-vv" options)))))
+ (set-window-buffer
+ (split-window) (yas-debug-snippets
+ nil (if (equal verbosity "-vv") 'edebug-create t))))
+ (yas-minor-mode +1)
+ (when snippet-key (insert snippet-key))))
+
+(when command-line-args-left
+ (yas-debug-process-command-line))
(provide 'yasnippet-debug)
;; Local Variables:
;; indent-tabs-mode: nil
-;; byte-compile-warnings: (not cl-functions)
+;; autoload-compute-prefixes: nil
;; End:
;;; yasnippet-debug.el ends here
diff --git a/yasnippet-tests.el b/yasnippet-tests.el
index 6b7a91a..6048467 100644
--- a/yasnippet-tests.el
+++ b/yasnippet-tests.el
@@ -1,6 +1,6 @@
;;; yasnippet-tests.el --- some yasnippet tests -*- lexical-binding: t -*-
-;; Copyright (C) 2012, 2013, 2014, 2015 Free Software Foundation, Inc.
+;; Copyright (C) 2012-2015, 2017-2018 Free Software Foundation, Inc.
;; Author: João Távora <joaot@siscog.pt>
;; Keywords: emulations, convenience
@@ -22,12 +22,134 @@
;; Test basic snippet mechanics and the loading system
+;; 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
+
;;; Code:
(require 'yasnippet)
(require 'ert)
(require 'ert-x)
(require 'cl-lib)
+(require 'org)
+
+
+;;; Helper macros and function
+
+(defmacro yas-with-snippet-dirs (dirs &rest body)
+ (declare (indent defun) (debug t))
+ `(yas-call-with-snippet-dirs
+ ,dirs #'(lambda () ,@body)))
+
+(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))
+ (ert-simulate-command '(yas-expand))
+ (unless (string= (yas--buffer-contents) (cdr key-and-expansion))
+ (ert-fail (format "\"%s\" should have expanded to \"%s\" but got \"%s\""
+ (car key-and-expansion)
+ (cdr key-and-expansion)
+ (yas--buffer-contents)))))
+ (yas-exit-all-snippets))
+
+(defun yas--collect-menu-items (menu-keymap)
+ (let ((yas--menu-items ()))
+ (map-keymap (lambda (_binding definition)
+ (when (eq (car-safe definition) 'menu-item)
+ (push definition yas--menu-items)))
+ menu-keymap)
+ yas--menu-items))
+
+(defun yas-should-not-expand (keys)
+ (dolist (key keys)
+ (yas-exit-all-snippets)
+ (erase-buffer)
+ (insert key)
+ (ert-simulate-command '(yas-expand))
+ (unless (string= (yas--buffer-contents) key)
+ (ert-fail (format "\"%s\" should have stayed put, but instead expanded to \"%s\""
+ key
+ (yas--buffer-contents))))))
+
+(defun yas-mock-insert (string)
+ (dotimes (i (length string))
+ (let ((last-command-event (aref string i)))
+ (ert-simulate-command '(self-insert-command 1)))))
+
+(defun yas-mock-yank (string)
+ (let ((interprogram-paste-function (lambda () string)))
+ (ert-simulate-command '(yank nil))))
+
+(defun yas--key-binding (key)
+ "Like `key-binding', but override `this-command-keys-vector'.
+This lets `yas--maybe-expand-from-keymap-filter' work as expected."
+ (cl-letf (((symbol-function 'this-command-keys-vector)
+ (lambda () (cl-coerce key 'vector))))
+ (key-binding key)))
+
+(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)
+ (cl-loop for var in vars
+ for saved in saved-values
+ do (set var saved)))))
+
+(defun yas-call-with-snippet-dirs (dirs fn)
+ (let* ((default-directory (make-temp-file "yasnippet-fixture" t))
+ (yas-snippet-dirs (mapcar (lambda (d) (expand-file-name (car d))) 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))))))
+
+;;; Older emacsen
+;;;
+(unless (fboundp 'special-mode)
+ ;; FIXME: Why provide this default definition here?!?
+ (defalias 'special-mode 'fundamental))
+
+(unless (fboundp 'string-suffix-p)
+ ;; introduced in Emacs 24.4
+ (defun string-suffix-p (suffix string &optional ignore-case)
+ "Return non-nil if SUFFIX is a suffix of STRING.
+If IGNORE-CASE is non-nil, the comparison is done without paying
+attention to case differences."
+ (let ((start-pos (- (length string) (length suffix))))
+ (and (>= start-pos 0)
+ (eq t (compare-strings suffix nil nil
+ string start-pos nil ignore-case))))))
;;; Snippet mechanics
@@ -90,6 +212,15 @@
(should (string= (yas--buffer-contents)
(concat filled-words "\n"))))))
+(ert-deftest auto-fill-with-multiparagraph ()
+ "Test auto-fill protection on snippet spanning multiple paragraphs"
+ (with-temp-buffer
+ (yas-minor-mode +1)
+ (auto-fill-mode +1)
+ (yas-expand-snippet "foo$1\n\n$2bar")
+ (yas-mock-insert " ")
+ (ert-simulate-command '(yas-next-field-or-maybe-expand))
+ (should (looking-at "bar"))))
(ert-deftest primary-field-transformation ()
(with-temp-buffer
@@ -143,9 +274,20 @@
(ert-simulate-command '(yas-next-field-or-maybe-expand))
(should (looking-at "testblable"))
(ert-simulate-command '(yas-next-field-or-maybe-expand))
- (ert-simulate-command '(yas-skip-and-clear-or-delete-char))
+ (ert-simulate-command '(yas-skip-and-clear-field))
(should (looking-at "ble"))
- (should (null (yas--snippets-at-point)))))
+ (should (null (yas-active-snippets)))))
+
+(ert-deftest delete-nested-simple-field-issue-824 ()
+ "Test deleting a field with a nested simple field in it."
+ (with-temp-buffer
+ (yas-minor-mode 1)
+ (yas-expand-snippet "${3:so-$4and}$0${2:-so}")
+ (ert-simulate-command '(yas-next-field-or-maybe-expand))
+ (should (looking-at "so-and-so"))
+ (ert-simulate-command '(yas-skip-and-clear-or-delete-char))
+ (should (looking-at "-so"))
+ (should (null (yas-active-snippets)))))
(ert-deftest ignore-trailing-whitespace ()
(should (equal
@@ -166,6 +308,76 @@
;; (should (string= (yas--buffer-contents)
;; "brother from another mother!"))))
+(ert-deftest undo-redo ()
+ "Check redoing of snippet undo."
+ (yas-with-snippet-dirs '((".emacs.d/snippets"
+ ("emacs-lisp-mode" ("x" . "${1:one},and done"))))
+ (with-temp-buffer
+ (emacs-lisp-mode)
+ (yas-reload-all)
+ (yas-minor-mode 1)
+ (yas-expand-snippet "x$0")
+ (let ((pre-expand-string (buffer-string)))
+ (setq buffer-undo-list nil)
+ (ert-simulate-command '(yas-expand))
+ (push nil buffer-undo-list)
+ (ert-simulate-command '(yas-next-field)) ; $1 -> exit snippet.
+ (should (string-match-p "\\`one,and done" (buffer-string)))
+ (push nil buffer-undo-list)
+ (ert-simulate-command '(undo)) ; Revive snippet.
+ (ert-simulate-command '(undo)) ; Undo expansion.
+ (should (string= (buffer-string) pre-expand-string))
+ (ert-simulate-command '(move-end-of-line 1))
+ (push nil buffer-undo-list)
+ (ert-simulate-command '(undo)) ; Redo (re-expand snippet).
+ (should (string-match-p "\\`one,and done" (buffer-string)))))))
+
+(defun yas-test-expand-and-undo (mode snippet-entry initial-contents)
+ (yas-with-snippet-dirs
+ `((".emacs.d/snippets" (,(symbol-name mode) ,snippet-entry)))
+ (with-temp-buffer
+ (funcall mode)
+ (yas-reload-all)
+ (yas-minor-mode 1)
+ (yas-expand-snippet initial-contents)
+ (let ((pre-expand-string (buffer-string)))
+ (setq buffer-undo-list ())
+ (ert-simulate-command '(yas-expand))
+ ;; Need undo barrier, I think command loop puts it normally.
+ (push nil buffer-undo-list)
+ (ert-simulate-command '(undo))
+ (should (string= (buffer-string) pre-expand-string))))))
+
+(ert-deftest undo-indentation-1 ()
+ "Check undoing works when only line of snippet is indented."
+ (let ((yas-also-auto-indent-first-line t))
+ (yas-test-expand-and-undo
+ 'emacs-lisp-mode '("s" . "(setq $0)") "(let\n(while s$0")))
+
+(ert-deftest undo-indentation-2 ()
+ "Check undoing works when only line of snippet is indented."
+ (let ((yas-also-auto-indent-first-line t)
+ (indent-tabs-mode nil))
+ (yas-test-expand-and-undo
+ 'emacs-lisp-mode '("t" . "; TODO") "t$0")))
+
+(ert-deftest undo-indentation-multiline-1 ()
+ "Check undoing works when 1st line of multi-line snippet is indented."
+ (let ((yas-also-auto-indent-first-line t)
+ (indent-tabs-mode nil))
+ (yas-test-expand-and-undo
+ 'js-mode '("if" . "if ($1) {\n\n}\n")
+ "if$0\nabc = 123456789 + abcdef;")))
+
+
+(ert-deftest undo-indentation-multiline-2 ()
+ "Check undoing works when 2nd line of multi-line snippet is indented."
+ (let ((yas-also-auto-indent-first-line t)
+ (indent-tabs-mode nil))
+ (yas-test-expand-and-undo
+ 'js-mode '("if" . "if (true) {\n${1:foo};\n}\n")
+ "if$0\nabc = 123456789 + abcdef;")))
+
(ert-deftest dont-clear-on-partial-deletion-issue-515 ()
"Ensure fields are not cleared when user doesn't really mean to."
(with-temp-buffer
@@ -218,6 +430,47 @@ end" (buffer-string)))
end" (buffer-string)))
(should (= 4 (current-column)))))
+(ert-deftest yas-also-indent-empty-lines ()
+ "Respect `yas-also-indent-empty-lines' setting."
+ (with-temp-buffer
+ (ruby-mode)
+ (yas-minor-mode 1)
+ (set (make-local-variable 'yas-indent-line) 'auto)
+ (set (make-local-variable 'yas-also-auto-indent-first-line) t)
+ (set (make-local-variable 'yas-also-indent-empty-lines) t)
+ (yas-expand-snippet "def foo\n\nend")
+ (should (string= "def foo\n \nend" (buffer-string)))
+ ;; Test that it keeps working without setting
+ ;; `yas-also-auto-indent-first-line'.
+ (setq yas-also-auto-indent-first-line nil)
+ (erase-buffer)
+ (yas-expand-snippet "def foo\n\nend")
+ (should (string= "def foo\n \nend" (buffer-string)))))
+
+(ert-deftest yas-indent-first-line ()
+ (with-temp-buffer
+ (ruby-mode)
+ (yas-minor-mode 1)
+ (set (make-local-variable 'yas-indent-line) 'auto)
+ (set (make-local-variable 'yas-also-auto-indent-first-line) nil)
+ (set (make-local-variable 'yas-also-indent-empty-lines) nil)
+ (yas-expand-snippet "def foo\n$0\nend\n")
+ ;; First (and only) line should not indent.
+ (yas-expand-snippet "#not indented")
+ (should (equal "def foo\n#not indented\nend\n" (buffer-string)))))
+
+(ert-deftest yas-indent-first-line-fixed ()
+ (with-temp-buffer
+ (ruby-mode)
+ (yas-minor-mode 1)
+ (set (make-local-variable 'yas-indent-line) 'fixed)
+ (set (make-local-variable 'yas-also-auto-indent-first-line) nil)
+ (set (make-local-variable 'yas-also-indent-empty-lines) nil)
+ (yas-expand-snippet " def foo\n $0\n end\n")
+ ;; First (and only) line should not indent.
+ (yas-expand-snippet "#not more indented")
+ (should (equal " def foo\n #not more indented\n end\n" (buffer-string)))))
+
(ert-deftest indentation-markers ()
"Test a snippet with indentation markers (`$<')."
(with-temp-buffer
@@ -243,6 +496,78 @@ $1 ------------------------")
XXXXX ---------------- XXXXX ----
XXXXX ------------------------"))))
+(ert-deftest single-line-multi-mirror-indentation-2 ()
+ "Like `single-line-multi-mirror-indentation' but 2 mirrors interleaved."
+ ;; See also Github issue #768.
+ (with-temp-buffer
+ (c-mode)
+ (yas-minor-mode 1)
+ (yas-expand-snippet "${1:one} ${2:two};\n$1 $2_;\n$2 $1_;\n")
+ (should (string= (yas--buffer-contents)
+ "one two;\none two_;\ntwo one_;\n"))))
+
+(ert-deftest indent-org-property ()
+ "Handling of `org-mode' property indentation, see `org-property-format'."
+ ;; This is an interesting case because `org-indent-line' calls
+ ;; `replace-match' for properties.
+ (with-temp-buffer
+ (org-mode)
+ (yas-minor-mode +1)
+ (yas-expand-snippet "* Test ${1:test}\n:PROPERTIES:\n:ID: $1-after\n:END:")
+ (yas-mock-insert "foo bar")
+ (ert-simulate-command '(yas-next-field))
+ (goto-char (point-min))
+ (let ((expected (with-temp-buffer
+ (insert (format (concat "* Test foo bar\n"
+ " " org-property-format "\n"
+ " " org-property-format "\n"
+ " " org-property-format)
+ ":PROPERTIES:" ""
+ ":ID:" "foo bar-after"
+ ":END:" ""))
+ (delete-trailing-whitespace)
+ (buffer-string))))
+ ;; Some org-mode versions leave trailing whitespace, some don't.
+ (delete-trailing-whitespace)
+ (should (equal expected (buffer-string))))))
+
+(ert-deftest indent-cc-mode ()
+ "Handling of cc-mode's indentation."
+ ;; This is an interesting case because cc-mode deletes all the
+ ;; indentation before recreating it.
+ (with-temp-buffer
+ (c++-mode)
+ (yas-minor-mode +1)
+ (yas-expand-snippet "\
+int foo()
+{
+ if ($1) {
+ delete $1;
+ $1 = 0;
+ }
+}")
+ (yas-mock-insert "var")
+ (should (string= "\
+int foo()
+{
+ if (var) {
+ delete var;
+ var = 0;
+ }
+}" (buffer-string)))))
+
+(ert-deftest indent-snippet-mode ()
+ "Handling of snippet-mode indentation."
+ ;; This is an interesting case because newlines match [[:space:]] in
+ ;; snippet-mode.
+ (with-temp-buffer
+ (snippet-mode)
+ (yas-minor-mode +1)
+ (yas-expand-snippet "# -*- mode: snippet -*-\n# name: $1\n# key: $1\n# --\n")
+ (yas-mock-insert "foo")
+ (should (string= "# -*- mode: snippet -*-\n# name: foo\n# key: foo\n# --\n"
+ (buffer-string)))))
+
(ert-deftest indent-mirrors-on-update ()
"Check that mirrors are always kept indented."
(with-temp-buffer
@@ -351,6 +676,43 @@ mapconcat #'(lambda (arg)
(yas-expand-snippet "Look ma! ${1:`(yas-selected-text)`} OK?")
(should (string= (yas--buffer-contents) "Look ma! He)}o world! OK?")))))
+(ert-deftest insert-snippet-with-backslashes-in-active-field ()
+ ;; This test case fails if `yas--inhibit-overlay-hooks' is not bound
+ ;; in `yas-expand-snippet' (see Github #844).
+ (with-temp-buffer
+ (yas-minor-mode 1)
+ (yas-expand-snippet "${1:$$(if (not yas-modified-p) \"a\")}")
+ (yas-expand-snippet "\\\\alpha")))
+
+(ert-deftest expand-with-unused-yas-selected-text ()
+ (with-temp-buffer
+ (yas-with-snippet-dirs
+ '((".emacs.d/snippets"
+ ("emacs-lisp-mode"
+ ("foo" . "expanded `yas-selected-text`foo"))))
+ (yas-reload-all)
+ (emacs-lisp-mode)
+ (yas-minor-mode +1)
+ (insert "foo")
+ (ert-simulate-command '(yas-expand))
+ (should (equal (buffer-string) "expanded foo")))))
+
+(ert-deftest yas-expand-command-snippet ()
+ (with-temp-buffer
+ (yas-with-snippet-dirs
+ '((".emacs.d/snippets"
+ ("emacs-lisp-mode"
+ ("foo" . "\
+# type: command
+# --
+\(insert \"expanded foo\")"))))
+ (yas-reload-all)
+ (emacs-lisp-mode)
+ (yas-minor-mode +1)
+ (insert "foo")
+ (ert-simulate-command '(yas-expand))
+ (should (equal (buffer-string) "expanded foo")))))
+
(ert-deftest example-for-issue-271 ()
(with-temp-buffer
(yas-minor-mode 1)
@@ -361,6 +723,17 @@ mapconcat #'(lambda (arg)
(yas-mock-insert "bbb")
(should (string= (yas--buffer-contents) "if condition\naaa\nelse\nbbb\nend")))))
+(ert-deftest yas-no-memory-of-bad-snippet ()
+ "Expanding an incorrect snippet should not influence future expansions."
+ ;; See https://github.com/joaotavora/yasnippet/issues/800.
+ (with-temp-buffer
+ (yas-minor-mode 1)
+ (should-error (yas-expand-snippet "```foo\n\n```"))
+ (erase-buffer) ; Bad snippet may leave wrong text.
+ ;; But expanding the corrected snippet should work fine.
+ (yas-expand-snippet "\\`\\`\\`foo\n\n\\`\\`\\`")
+ (should (equal (buffer-string) "```foo\n\n```"))))
+
(defmacro yas--with-font-locked-temp-buffer (&rest body)
"Like `with-temp-buffer', but ensure `font-lock-mode'."
(declare (indent 0) (debug t))
@@ -382,14 +755,9 @@ mapconcat #'(lambda (arg)
(kill-buffer ,temp-buffer))))))))
(defmacro yas-saving-variables (&rest body)
+ (declare (debug t))
`(yas-call-with-saving-variables #'(lambda () ,@body)))
-(defmacro yas-with-snippet-dirs (dirs &rest body)
- (declare (indent defun))
- `(yas-call-with-snippet-dirs ,dirs
- #'(lambda ()
- ,@body)))
-
(ert-deftest example-for-issue-474 ()
(yas--with-font-locked-temp-buffer
(c-mode)
@@ -480,6 +848,88 @@ TODO: correct this bug!"
"brother from another mother") ;; no newline should be here!
)))
+(defvar yas-tests--ran-exit-hook nil)
+
+(ert-deftest snippet-exit-hooks ()
+ (with-temp-buffer
+ (yas-saving-variables
+ (let ((yas-tests--ran-exit-hook nil)
+ (yas-triggers-in-field t))
+ (yas-with-snippet-dirs
+ '((".emacs.d/snippets"
+ ("emacs-lisp-mode"
+ ("foo" . "\
+# expand-env: ((yas-after-exit-snippet-hook (lambda () (setq yas-tests--ran-exit-hook t))))
+# --
+FOO ${1:f1} ${2:f2}")
+ ("sub" . "\
+# expand-env: ((yas-after-exit-snippet-hook (lambda () (setq yas-tests--ran-exit-hook 'sub))))
+# --
+SUB"))))
+ (yas-reload-all)
+ (emacs-lisp-mode)
+ (yas-minor-mode +1)
+ (insert "foo")
+ (ert-simulate-command '(yas-expand))
+ (should-not yas-tests--ran-exit-hook)
+ (yas-mock-insert "sub")
+ (ert-simulate-command '(yas-expand))
+ (ert-simulate-command '(yas-next-field))
+ (should-not yas-tests--ran-exit-hook)
+ (ert-simulate-command '(yas-next-field))
+ (should (eq yas-tests--ran-exit-hook t)))))))
+
+(ert-deftest snippet-exit-hooks-bindings ()
+ "Check that `yas-after-exit-snippet-hook' is handled correctly
+in the case of a buffer-local variable and being overwritten by
+the expand-env field."
+ (with-temp-buffer
+ (yas-saving-variables
+ (let ((yas-tests--ran-exit-hook nil)
+ (yas-triggers-in-field t)
+ (yas-after-exit-snippet-hook nil))
+ (yas-with-snippet-dirs
+ '((".emacs.d/snippets"
+ ("emacs-lisp-mode"
+ ("foo" . "foobar\n")
+ ("baz" . "\
+# expand-env: ((yas-after-exit-snippet-hook (lambda () (setq yas-tests--ran-exit-hook 'letenv))))
+# --
+foobaz\n"))))
+ (yas-reload-all)
+ (emacs-lisp-mode)
+ (yas-minor-mode +1)
+ (add-hook 'yas-after-exit-snippet-hook (lambda () (push 'global yas-tests--ran-exit-hook)))
+ (add-hook 'yas-after-exit-snippet-hook (lambda () (push 'local yas-tests--ran-exit-hook)) nil t)
+ (insert "baz")
+ (ert-simulate-command '(yas-expand))
+ (should (eq 'letenv yas-tests--ran-exit-hook))
+ (insert "foo")
+ (ert-simulate-command '(yas-expand))
+ (should (eq 'global (nth 0 yas-tests--ran-exit-hook)))
+ (should (eq 'local (nth 1 yas-tests--ran-exit-hook))))))))
+
+(ert-deftest snippet-mirror-bindings ()
+ "Check that variables defined with the expand-env field are
+accessible from mirror transformations."
+ (with-temp-buffer
+ (yas-saving-variables
+ (let ((yas-triggers-in-field t)
+ (yas-good-grace nil))
+ (yas-with-snippet-dirs
+ '((".emacs.d/snippets"
+ ("emacs-lisp-mode"
+ ("baz" . "\
+# expand-env: ((func #'upcase))
+# --
+hello ${1:$(when (stringp yas-text) (funcall func yas-text))} foo${1:$$(concat \"baz\")}$0"))))
+ (yas-reload-all)
+ (emacs-lisp-mode)
+ (yas-minor-mode +1)
+ (insert "baz")
+ (ert-simulate-command '(yas-expand))
+ (should (string= (yas--buffer-contents) "hello BAZ foobaz\n")))))))
+
(defvar yas--barbaz)
(defvar yas--foobarbaz)
@@ -523,11 +973,69 @@ TODO: correct this bug!"
(yas-should-expand '(("foo-barbaz" . "OKfoo-barbazOK")
("foo " . "foo "))))))))
+(ert-deftest nested-snippet-expansion-1 ()
+ (with-temp-buffer
+ (yas-minor-mode +1)
+ (let ((yas-triggers-in-field t))
+ (yas-expand-snippet "Parent $1 Snippet")
+ (yas-expand-snippet "(Child $1 $2 Snippet)")
+ (let ((snippets (yas-active-snippets)))
+ (should (= (length snippets) 2))
+ (should (= (length (yas--snippet-fields (nth 0 snippets))) 2))
+ (should (= (length (yas--snippet-fields (nth 1 snippets))) 1))))))
+
+(ert-deftest nested-snippet-expansion-2 ()
+ (let ((yas-triggers-in-field t))
+ (yas-with-snippet-dirs
+ '((".emacs.d/snippets"
+ ("text-mode"
+ ("nest" . "one($1:$1) two($2).$0"))))
+ (yas-reload-all)
+ (text-mode)
+ (yas-minor-mode +1)
+ (insert "nest")
+ (ert-simulate-command '(yas-expand))
+ (yas-mock-insert "nest")
+ (ert-simulate-command '(yas-expand))
+ (yas-mock-insert "x")
+ (ert-simulate-command '(yas-next-field-or-maybe-expand))
+ (yas-mock-insert "y")
+ (ert-simulate-command '(yas-next-field-or-maybe-expand))
+ (ert-simulate-command '(yas-next-field-or-maybe-expand))
+ (yas-mock-insert "z")
+ (ert-simulate-command '(yas-next-field-or-maybe-expand))
+ (should (string= (buffer-string)
+ "one(one(x:x) two(y).:one(x:x) two(y).) two(z).")))))
+
+(ert-deftest nested-snippet-expansion-3 ()
+ (let ((yas-triggers-in-field t))
+ (yas-with-snippet-dirs
+ '((".emacs.d/snippets"
+ ("text-mode"
+ ("rt" . "\
+\\sqrt${1:$(if (string-equal \"\" yas/text) \"\" \"[\")}${1:}${1:$(if (string-equal \"\" yas/text) \"\" \"]\")}{$2}$0"))))
+ (yas-reload-all)
+ (text-mode)
+ (yas-minor-mode +1)
+ (insert "rt")
+ (ert-simulate-command '(yas-expand))
+ (yas-mock-insert "3")
+ (ert-simulate-command '(yas-next-field-or-maybe-expand))
+ (yas-mock-insert "rt")
+ (ert-simulate-command '(yas-next-field-or-maybe-expand))
+ (yas-mock-insert "5")
+ (ert-simulate-command '(yas-next-field-or-maybe-expand))
+ (yas-mock-insert "2")
+ (ert-simulate-command '(yas-next-field-or-maybe-expand))
+ (ert-simulate-command '(yas-next-field-or-maybe-expand))
+ (should (string= (buffer-string) "\\sqrt[3]{\\sqrt[5]{2}}")))))
+
;;; Loading
;;;
(defmacro yas-with-overriden-buffer-list (&rest body)
+ (declare (debug t))
(let ((saved-sym (make-symbol "yas--buffer-list")))
`(let ((,saved-sym (symbol-function 'buffer-list)))
(cl-letf (((symbol-function 'buffer-list)
@@ -540,6 +1048,7 @@ TODO: correct this bug!"
(defmacro yas-with-some-interesting-snippet-dirs (&rest body)
+ (declare (debug t))
`(yas-saving-variables
(yas-with-overriden-buffer-list
(yas-with-snippet-dirs
@@ -562,11 +1071,28 @@ TODO: correct this bug!"
"Test `yas-lookup-snippet'."
(yas-with-some-interesting-snippet-dirs
(yas-reload-all 'no-jit)
- (should (equal (yas-lookup-snippet "printf" 'c-mode) "printf($1);"))
- (should (equal (yas-lookup-snippet "def" 'c-mode) "# define"))
+ (should (equal (yas--template-content (yas-lookup-snippet "printf" 'c-mode))
+ "printf($1);"))
+ (should (equal (yas--template-content (yas-lookup-snippet "def" 'c-mode))
+ "# define"))
(should-not (yas-lookup-snippet "no such snippet" nil 'noerror))
(should-not (yas-lookup-snippet "printf" 'emacs-lisp-mode 'noerror))))
+(ert-deftest yas-lookup-snippet-with-env ()
+ (with-temp-buffer
+ (yas-with-snippet-dirs
+ '((".emacs.d/snippets"
+ ("emacs-lisp-mode"
+ ("foo" . "\
+# expand-env: ((foo \"bar\"))
+# --
+`foo`"))))
+ (yas-reload-all)
+ (emacs-lisp-mode)
+ (yas-minor-mode +1)
+ (yas-expand-snippet (yas-lookup-snippet "foo"))
+ (should (equal (buffer-string) "bar")))))
+
(ert-deftest basic-jit-loading ()
"Test basic loading and expansion of snippets"
(yas-with-some-interesting-snippet-dirs
@@ -594,13 +1120,15 @@ TODO: correct this bug!"
(with-temp-buffer
(text-mode)
(yas-minor-mode +1)
- (should (equal (yas-lookup-snippet "one") "one"))
- (should (eq (key-binding "\C-c1") 'yas-expand-from-keymap))
+ (should (equal (yas--template-content (yas-lookup-snippet "one"))
+ "one"))
+ (should (eq (yas--key-binding "\C-c1") 'yas-expand-from-keymap))
(yas-define-snippets
'text-mode '(("_1" "one!" "won" nil nil nil nil nil "uuid-1")))
(should (null (yas-lookup-snippet "one" nil 'noerror)))
- (should (null (key-binding "\C-c1")))
- (should (equal (yas-lookup-snippet "won") "one!")))))
+ (should (null (yas--key-binding "\C-c1")))
+ (should (equal (yas--template-content(yas-lookup-snippet "won"))
+ "one!")))))
(ert-deftest snippet-save ()
"Make sure snippets can be saved correctly."
@@ -620,13 +1148,13 @@ TODO: correct this bug!"
(yas-minor-mode +1)
(save-current-buffer
(yas-new-snippet t)
- (with-current-buffer "*new snippet*"
+ (with-current-buffer yas-new-snippet-buffer-name
(snippet-mode)
(insert "# name: foo\n# key: bar\n# --\nsnippet foo")
(call-interactively 'yas-load-snippet-buffer-and-close)))
(save-current-buffer
(yas-new-snippet t)
- (with-current-buffer "*new snippet*"
+ (with-current-buffer yas-new-snippet-buffer-name
(snippet-mode)
(insert "# name: bar\n# key: bar\n# --\nsnippet bar")
(call-interactively 'yas-load-snippet-buffer-and-close)))
@@ -667,7 +1195,7 @@ TODO: correct this bug!"
yet-another-c-mode
and-also-this-one
and-that-one
- ;; prog-mode doesn't exist in emacs 24.3
+ ;; prog-mode doesn't exist in emacs 23.4
,@(if (fboundp 'prog-mode)
'(prog-mode))
emacs-lisp-mode
@@ -694,7 +1222,7 @@ TODO: correct this bug!"
c-mode
,major-mode))
(expected-rest `(cc-mode
- ;; prog-mode doesn't exist in emacs 24.3
+ ;; prog-mode doesn't exist in emacs 23.4
,@(if (fboundp 'prog-mode)
'(prog-mode))
emacs-lisp-mode
@@ -767,9 +1295,38 @@ TODO: correct this bug!"
(yas-should-not-expand '("sc" "dolist" "ert-deftest"))))
+;;; Unloading
+(ert-deftest yas-unload ()
+ "Test unloading and reloading."
+ (with-temp-buffer
+ (let ((status (call-process
+ (concat invocation-directory invocation-name)
+ nil '(t t) nil
+ "-Q" "--batch" "-L" yas--loaddir "-l" "yasnippet"
+ "--eval"
+ (prin1-to-string
+ '(condition-case err
+ (progn
+ (yas-minor-mode +1)
+ (unload-feature 'yasnippet)
+ ;; Unloading leaves `yas-minor-mode' bound,
+ ;; harmless, though perhaps surprising.
+ (when (bound-and-true-p yas-minor-mode)
+ (error "`yas-minor-mode' still enabled"))
+ (when (fboundp 'yas-minor-mode)
+ (error "`yas-minor-mode' still fboundp"))
+ (require 'yasnippet)
+ (unless (fboundp 'yas-minor-mode)
+ (error "Failed to reload")))
+ (error (message "%S" (error-message-string err))
+ (kill-emacs 1)))))))
+ (ert-info ((buffer-string)) (should (eq status 0))))))
+
+
;;; Menu
;;;
(defmacro yas-with-even-more-interesting-snippet-dirs (&rest body)
+ (declare (debug t))
`(yas-saving-variables
(yas-with-snippet-dirs
`((".emacs.d/snippets"
@@ -805,16 +1362,16 @@ TODO: correct this bug!"
(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)))
+ (let ((menu-items (yas--collect-menu-items
+ (gethash 'fancy-mode yas--menu-table))))
+ (should (eql 4 (length menu-items)))
(dolist (item '("a-guy" "a-beggar"))
- (should (cl-find item menu :key #'cl-third :test #'string=)))
- (should-not (cl-find "an-outcast" menu :key #'cl-third :test #'string=))
+ (should (cl-find item menu-items :key #'cl-second :test #'string=)))
+ (should-not (cl-find "an-outcast" menu-items :key #'cl-second :test #'string=))
(dolist (submenu '("sirs" "ladies"))
(should (keymapp
- (cl-fourth
- (cl-find submenu menu :key #'cl-third :test #'string=)))))
- ))))
+ (cl-third
+ (cl-find submenu menu-items :key #'cl-second :test #'string=)))))))))
(ert-deftest test-group-menus ()
"Test group-based menus using .yas-make-groups and the group directive"
@@ -889,16 +1446,23 @@ TODO: be meaner"
;;; 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))))
+ (yas-saving-variables
+ (yas-with-snippet-dirs
+ '((".emacs.d/snippets"
+ ("fundamental-mode"
+ ("foo" . "foobar"))))
+ (yas-reload-all)
+ (with-temp-buffer
+ (yas-minor-mode -1)
+ (insert "foo")
+ (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 ()
(let* ((yas-minor-mode-map (copy-keymap yas-minor-mode-map))
@@ -918,11 +1482,58 @@ TODO: be meaner"
(should (eq (key-binding (kbd "SPC")) 'yas-expand)))))
(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))))
+ (yas-saving-variables
+ (yas-with-snippet-dirs
+ '((".emacs.d/snippets"
+ ("org-mode"
+ ("foo" . "foobar"))))
+ (yas-reload-all)
+ (with-temp-buffer
+ (org-mode)
+ (yas-minor-mode 1)
+ (insert "foo")
+ (should (eq (key-binding [(tab)]) 'yas-expand))
+ (should (eq (key-binding (kbd "TAB")) 'yas-expand))))))
+
+(ert-deftest yas-org-native-tab-in-source-block ()
+ "Test expansion of snippets in org source blocks."
+ :expected-result (if (and (fboundp 'org-in-src-block-p) (version< (org-version) "9"))
+ :passed :failed)
+ (yas-saving-variables
+ (yas-with-snippet-dirs
+ '((".emacs.d/snippets"
+ ("text-mode"
+ ("T" . "${1:one} $1\n${2:two} $2\n<<$0>> done!"))))
+ (let ((text-mode-hook '(yas-minor-mode))
+ (org-src-tab-acts-natively t)
+ ;; Org 8.x requires this in order for
+ ;; `org-src-tab-acts-natively' to have effect.
+ (org-src-fontify-natively t))
+ (yas-reload-all)
+ ;; Org relies on font-lock to identify source blocks.
+ (yas--with-font-locked-temp-buffer
+ (org-mode)
+ (yas-minor-mode 1)
+ (insert "#+BEGIN_SRC text\nT\n#+END_SRC")
+ (if (fboundp 'font-lock-ensure)
+ (font-lock-ensure)
+ (jit-lock-fontify-now))
+ (re-search-backward "^T$") (goto-char (match-end 0))
+ (should (org-in-src-block-p))
+ (ert-simulate-command `(,(key-binding (kbd "TAB"))))
+ (ert-simulate-command `(,(key-binding (kbd "TAB"))))
+ (ert-simulate-command `(,(key-binding (kbd "TAB"))))
+ ;; Check snippet exit location.
+ (should (looking-at ">> done!"))
+ (goto-char (point-min))
+ (forward-line)
+ ;; Check snippet expansion, ignore leading whitespace due to
+ ;; `org-edit-src-content-indentation'.
+ (should (looking-at "\
+[[:space:]]*one one
+[[:space:]]*two two
+[[:space:]]*<<>> done!")))))))
+
(ert-deftest test-yas-activate-extra-modes ()
"Given a symbol, `yas-activate-extra-mode' should be able to
@@ -948,116 +1559,10 @@ add the snippets associated with the given mode."
(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)))
- (unless (string= (yas--buffer-contents) (cdr key-and-expansion))
- (ert-fail (format "\"%s\" should have expanded to \"%s\" but got \"%s\""
- (car key-and-expansion)
- (cdr key-and-expansion)
- (yas--buffer-contents)))))
- (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)))
- (unless (string= (yas--buffer-contents) key)
- (ert-fail (format "\"%s\" should have stayed put, but instead expanded to \"%s\""
- key
- (yas--buffer-contents))))))
-
-(defun yas-mock-insert (string)
- (dotimes (i (length string))
- (let ((last-command-event (aref string i)))
- (ert-simulate-command '(self-insert-command 1)))))
-
-(defun yas-mock-yank (string)
- (let ((interprogram-paste-function (lambda () string)))
- (ert-simulate-command '(yank nil))))
-
-(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)
- (cl-loop for var in vars
- for saved in saved-values
- do (set var saved)))))
-
-(defun yas-call-with-snippet-dirs (dirs fn)
- (let* ((default-directory (make-temp-file "yasnippet-fixture" t))
- (yas-snippet-dirs (mapcar (lambda (d) (expand-file-name (car d))) 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))))))
-
-;;; Older emacsen
-;;;
-(unless (fboundp 'special-mode)
- ;; FIXME: Why provide this default definition here?!?
- (defalias 'special-mode 'fundamental))
-
-(unless (fboundp 'string-suffix-p)
- ;; introduced in Emacs 24.4
- (defun string-suffix-p (suffix string &optional ignore-case)
- "Return non-nil if SUFFIX is a suffix of STRING.
-If IGNORE-CASE is non-nil, the comparison is done without paying
-attention to case differences."
- (let ((start-pos (- (length string) (length suffix))))
- (and (>= start-pos 0)
- (eq t (compare-strings suffix nil nil
- string start-pos nil ignore-case))))))
-
-;;; 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)
(provide 'yasnippet-tests)
;; Local Variables:
;; indent-tabs-mode: nil
+;; autoload-compute-prefixes: nil
;; End:
;;; yasnippet-tests.el ends here
diff --git a/yasnippet.el b/yasnippet.el
index 22477cc..d478073 100644
--- a/yasnippet.el
+++ b/yasnippet.el
@@ -1,11 +1,11 @@
;;; yasnippet.el --- Yet another snippet extension for Emacs.
-;; Copyright (C) 2008-2016 Free Software Foundation, Inc.
+;; Copyright (C) 2008-2018 Free Software Foundation, Inc.
;; Authors: pluskid <pluskid@gmail.com>,
;; João Távora <joaotavora@gmail.com>,
;; Noam Postavsky <npostavs@gmail.com>
;; Maintainer: Noam Postavsky <npostavs@gmail.com>
-;; Version: 0.11.0
+;; Version: 0.13.0
;; X-URL: http://github.com/joaotavora/yasnippet
;; Keywords: convenience, emulation
;; URL: http://github.com/joaotavora/yasnippet
@@ -132,6 +132,7 @@
;;; Code:
(require 'cl-lib)
+(declare-function cl-progv-after "cl-extra") ; Needed for 23.4.
(require 'easymenu)
(require 'help-mode)
@@ -152,17 +153,18 @@
:prefix "yas-"
:group 'editing)
-(defvar yas-installed-snippets-dir nil)
-(let ((snippets-directory "/usr/share/yasnippet-snippets"))
- (when (file-exists-p snippets-directory)
- (setq yas-installed-snippets-dir snippets-directory)))
+(defconst yas--loaddir
+ (file-name-directory (or load-file-name buffer-file-name))
+ "Directory that yasnippet was loaded from.")
+
+(defconst yas-installed-snippets-dir (expand-file-name "snippets" yas--loaddir))
+(make-obsolete-variable 'yas-installed-snippets-dir "\
+Yasnippet no longer comes with installed snippets" "0.13")
(defconst yas--default-user-snippets-dir
(expand-file-name "snippets" user-emacs-directory))
-(defcustom yas-snippet-dirs (remove nil
- (list yas--default-user-snippets-dir
- 'yas-installed-snippets-dir))
+(defcustom yas-snippet-dirs (list yas--default-user-snippets-dir)
"List of top-level snippet directories.
Each element, a string or a symbol whose value is a string,
@@ -197,11 +199,7 @@ created with `yas-new-snippet'. "
(stringp (symbol-value e)))
(symbol-value e))
(t
- (error "[yas] invalid element %s in `yas-snippet-dirs'
-
-Perhaps you need to install the yasnippet-snippets Debian package,
-or remove %s from `yas-snippet-dirs'
-before loading yasnippet." e e)))))
+ (error "[yas] invalid element %s in `yas-snippet-dirs'" e)))))
(defcustom yas-new-snippet-default "\
# -*- mode: snippet -*-
@@ -239,7 +237,7 @@ nil.
- To signal that the user quit the prompting process, you can
signal `quit' with
- (signal \\='quit \"user quit!\")."
+ (signal \\='quit \"user quit!\")"
:type '(repeat function))
(defcustom yas-indent-line 'auto
@@ -261,7 +259,11 @@ applies)."
(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'"
+Naturally this is only valid when `yas-indent-line' is `auto'."
+ :type 'boolean)
+
+(defcustom yas-also-indent-empty-lines nil
+ "Non-nil means also indent empty lines according to mode."
:type 'boolean)
(defcustom yas-snippet-revival t
@@ -275,22 +277,20 @@ Otherwise `yas-next-field-or-maybe-expand' just moves on to the
next field"
:type 'boolean)
-(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."
+(defcustom yas-fallback-behavior 'return-nil
+ "This option is obsolete.
+Now that the conditional keybinding `yas-maybe-expand' is
+available, there's no more need for it."
:type '(choice (const :tag "Call previous command" call-other-command)
(const :tag "Do nothing" return-nil)))
+(make-obsolete-variable
+ 'yas-fallback-behavior
+ "For `call-other-command' behavior bind to the conditional
+command value `yas-maybe-expand', for `return-nil' behavior bind
+directly to `yas-expand'."
+ "0.12")
+
(defcustom yas-choose-keys-first nil
"If non-nil, prompt for snippet key first, then for template.
@@ -346,9 +346,16 @@ per-snippet basis. A value of `cua' is considered equivalent to
(const cua))) ; backwards compat
(defcustom yas-good-grace t
- "If non-nil, don't raise errors in inline elisp evaluation.
+ "If non-nil, don't raise errors in elisp evaluation.
-An error string \"[yas] error\" is returned instead."
+This affects both the inline elisp in snippets and the hook
+variables such as `yas-after-exit-snippet-hook'.
+
+If this variable's value is `inline', an error string \"[yas]
+error\" is returned instead of raising the error. If this
+variable's value is `hooks', a message is output to according to
+`yas-verbosity-level'. If this variable's value is t, both are
+active."
:type 'boolean)
(defcustom yas-visit-from-menu nil
@@ -389,18 +396,26 @@ It must be set to nil before loading yasnippet to take effect."
;;; User-visible variables
+(defconst yas-maybe-skip-and-clear-field
+ '(menu-item "" yas-skip-and-clear-field
+ :filter yas--maybe-clear-field-filter)
+ "A conditional key definition.
+This can be used as a key definition in keymaps to bind a key to
+`yas-skip-and-clear-field' only when at the beginning of an
+unmodified snippey field.")
+
(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)
+ (define-key map (kbd "C-d") yas-maybe-skip-and-clear-field)
map)
"The active keymap while a snippet expansion is in progress.")
-(defvar yas-key-syntaxes (list "w" "w_" "w_." "w_.()"
- #'yas-try-key-from-whitespace)
+(defvar yas-key-syntaxes (list #'yas-try-key-from-whitespace
+ "w_.()" "w_." "w_" "w")
"Syntaxes and functions to help look for trigger keys before point.
Each element in this list specifies how to skip buffer positions
@@ -455,13 +470,17 @@ Attention: These hooks are not run when exiting nested/stacked snippet expansion
'()
"Hooks to run just before expanding a snippet.")
-(defvar yas-buffer-local-condition
+(defconst yas-not-string-or-comment-condition
'(if (and (let ((ppss (syntax-ppss)))
(or (nth 3 ppss) (nth 4 ppss)))
(memq this-command '(yas-expand yas-expand-from-trigger-key
yas-expand-from-keymap)))
'(require-snippet-condition . force-in-comment)
t)
+ "Disables snippet expansion in strings and comments.
+To use, set `yas-buffer-local-condition' to this value.")
+
+(defcustom yas-buffer-local-condition t
"Snippet expanding condition.
This variable is a Lisp form which is evaluated every time a
@@ -508,17 +527,26 @@ conditions.
(setq yas-buffer-local-condition
\\='(if (python-syntax-comment-or-string-p)
\\='(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'.")
+ t))))"
+ :type
+ `(choice
+ (const :tag "Disable snippet expansion inside strings and comments"
+ ,yas-not-string-or-comment-condition)
+ (const :tag "Expand all snippets regardless of conditions" always)
+ (const :tag "Expand snippets unless their condition is nil" t)
+ (const :tag "Disable all snippet expansion" nil)
+ sexp))
+
+(defcustom yas-overlay-priority 100
+ "Priority to use for yasnippets overlays.
+This is useful to control whether snippet navigation bindings
+override bindings from other packages (e.g., `company-mode')."
+ :type 'integer)
;;; Internal variables
-(defconst yas--version "0.11.0")
+(defconst yas--version "0.13.0")
(defvar yas--menu-table (make-hash-table)
"A hash table of MAJOR-MODE symbols to menu keymaps.")
@@ -550,6 +578,38 @@ snippet itself contains a condition that returns the symbol
(defvar yas--snippet-id-seed 0
"Contains the next id for a snippet.")
+(defvar yas--original-auto-fill-function nil
+ "The original value of `auto-fill-function'.")
+(make-variable-buffer-local 'yas--original-auto-fill-function)
+
+(defvar yas--watch-auto-fill-backtrace nil)
+
+(defun yas--watch-auto-fill (sym newval op _where)
+ (when (and (or (and (eq sym 'yas--original-auto-fill-function)
+ (null newval)
+ (eq auto-fill-function 'yas--auto-fill))
+ (and (eq sym 'auto-fill-function)
+ (eq newval 'yas--auto-fill)
+ (null yas--original-auto-fill-function)))
+ (null yas--watch-auto-fill-backtrace)
+ (fboundp 'backtrace-frames) ; Suppress compiler warning.
+ ;; If we're about to change `auto-fill-function' too,
+ ;; it's okay (probably).
+ (not (and (eq op 'makunbound)
+ (not (eq (default-value 'auto-fill-function) 'yas--auto-fill))
+ (cl-member 'kill-all-local-variables
+ (backtrace-frames 'yas--watch-auto-fill)
+ :key (lambda (frame) (nth 1 frame))))))
+ (setq yas--watch-auto-fill-backtrace
+ (backtrace-frames 'yas--watch-auto-fill))))
+
+;; Try to get more info on #873/919 (this only works for Emacs 26+).
+(when (fboundp 'add-variable-watcher)
+ (add-variable-watcher 'yas--original-auto-fill-function
+ #'yas--watch-auto-fill)
+ (add-variable-watcher 'auto-fill-function
+ #'yas--watch-auto-fill))
+
(defun yas--snippet-next-id ()
(let ((id yas--snippet-id-seed))
(cl-incf yas--snippet-id-seed)
@@ -564,10 +624,24 @@ snippet itself contains a condition that returns the symbol
(defvar yas--minor-mode-menu nil
"Holds the YASnippet menu.")
+(defvar yas--condition-cache-timestamp nil)
+
+(defun yas--maybe-expand-key-filter (cmd)
+ (when (let ((yas--condition-cache-timestamp (current-time)))
+ (yas--templates-for-key-at-point))
+ cmd))
+
+(defconst yas-maybe-expand
+ '(menu-item "" yas-expand :filter yas--maybe-expand-key-filter)
+ "A conditional key definition.
+This can be used as a key definition in keymaps to bind a key to
+`yas-expand' only when there is a snippet available to be
+expanded.")
+
(defvar yas-minor-mode-map
(let ((map (make-sparse-keymap)))
- (define-key map [(tab)] 'yas-expand)
- (define-key map (kbd "TAB") 'yas-expand)
+ (define-key map [(tab)] yas-maybe-expand)
+ (define-key map (kbd "TAB") yas-maybe-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)
@@ -707,10 +781,10 @@ 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.")
+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 `yas-maybe-expand-from-keymap'
+which decides on the snippet to expand.")
(defun yas-direct-keymaps-reload ()
"Force reload the direct keybinding for active snippet tables."
@@ -746,6 +820,12 @@ defined direct keybindings to the command
(defvar yas-minor-mode-hook nil
"Hook run when `yas-minor-mode' is turned on.")
+(defun yas--auto-fill-wrapper ()
+ (when (and auto-fill-function
+ (not (eq auto-fill-function #'yas--auto-fill)))
+ (setq yas--original-auto-fill-function auto-fill-function)
+ (setq auto-fill-function #'yas--auto-fill)))
+
;;;###autoload
(define-minor-mode yas-minor-mode
"Toggle YASnippet mode.
@@ -778,12 +858,17 @@ Key bindings:
(set-default name nil)
(set (make-local-variable name) t)))
;; Perform JIT loads
- ;;
- (yas--load-pending-jits))
+ (yas--load-pending-jits)
+ ;; Install auto-fill handler.
+ (yas--auto-fill-wrapper) ; Now...
+ (add-hook 'auto-fill-mode-hook #'yas--auto-fill-wrapper)) ; or later.
(t
- ;; Uninstall the direct keymaps and the post-command hook
- ;;
+ ;; Uninstall the direct keymaps, post-command hook, and
+ ;; auto-fill handler.
(remove-hook 'post-command-hook #'yas--post-command-handler t)
+ (remove-hook 'auto-fill-mode-hook #'yas--auto-fill-wrapper)
+ (when (local-variable-p 'yas--original-auto-fill-function)
+ (setq auto-fill-function yas--original-auto-fill-function))
(setq emulation-mode-map-alists
(remove 'yas--direct-keymaps emulation-mode-map-alists)))))
@@ -902,15 +987,38 @@ Honour `yas-dont-activate-functions', which see."
"The keymap used when `snippet-mode' is active.")
-;;;###autoload
-(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 ]*")
- (add-hook 'after-save-hook #'yas-maybe-load-snippet-buffer nil t))
+;;;###autoload(autoload 'snippet-mode "yasnippet" "A mode for editing yasnippets" t nil)
+(eval-and-compile
+ (if (fboundp 'prog-mode)
+ ;; `prog-mode' is new in 24.1.
+ (define-derived-mode snippet-mode prog-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 ]*")
+ (add-hook 'after-save-hook #'yas-maybe-load-snippet-buffer nil t))
+ (define-derived-mode snippet-mode fundamental-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 ]*")
+ (add-hook 'after-save-hook #'yas-maybe-load-snippet-buffer nil t))))
+
+(defun yas-snippet-mode-buffer-p ()
+ "Return non-nil if current buffer should be in `snippet-mode'.
+Meaning it's visiting a file under one of the mode directories in
+`yas-snippet-dirs'."
+ (when buffer-file-name
+ (cl-member buffer-file-name (yas-snippet-dirs)
+ :test #'file-in-directory-p)))
+
+;; We're abusing `magic-fallback-mode-alist' here because
+;; `auto-mode-alist' doesn't support function matchers.
+(add-to-list 'magic-fallback-mode-alist
+ `(yas-snippet-mode-buffer-p . snippet-mode))
;;; Internal structs for template management
@@ -980,7 +1088,7 @@ Has the following fields:
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'.
+ bindings to `yas-maybe-expand-from-keymap'.
`yas--table-uuidhash'
@@ -1079,6 +1187,10 @@ Has the following fields:
;;
(remhash uuid (yas--table-uuidhash table))))))
+(defconst yas-maybe-expand-from-keymap
+ '(menu-item "" yas-expand-from-keymap
+ :filter yas--maybe-expand-from-keymap-filter))
+
(defun yas--add-template (table template)
"Store in TABLE the snippet template TEMPLATE.
@@ -1097,7 +1209,7 @@ keybinding)."
(make-hash-table :test 'equal)
(yas--table-hash table))))
(when (vectorp k)
- (define-key (yas--table-direct-keymap table) k 'yas-expand-from-keymap)))
+ (define-key (yas--table-direct-keymap table) k yas-maybe-expand-from-keymap)))
;; Update TABLE's `yas--table-uuidhash'
(puthash (yas--template-uuid template)
@@ -1300,33 +1412,58 @@ Returns (TEMPLATES START END). This function respects
;;; Internal functions and macros:
-(defun yas--handle-error (err)
- "Handle error depending on value of `yas-good-grace'."
- (let ((msg (yas--format "elisp error: %s" (error-message-string err))))
- (if yas-good-grace msg
- (error "%s" msg))))
+(defun yas--remove-misc-free-from-undo (old-undo-list)
+ "Tries to work around Emacs Bug#30931.
+Helper function for `yas--save-restriction-and-widen'."
+ ;; If Bug#30931 is unfixed, we get (#<Lisp_Misc_Free> . INTEGER)
+ ;; entries in the undo list. If we call `type-of' on the
+ ;; Lisp_Misc_Free object then Emacs aborts, so try to find it by
+ ;; checking that its type is none of the expected ones.
+ (when (consp buffer-undo-list)
+ (let* ((prev buffer-undo-list)
+ (undo-list prev))
+ (while (and (consp undo-list)
+ ;; Only check new entries.
+ (not (eq undo-list old-undo-list)))
+ (let ((entry (pop undo-list)))
+ (when (consp entry)
+ (let ((head (car entry)))
+ (unless (or (stringp head)
+ (markerp head)
+ (integerp head)
+ (symbolp head)
+ (not (integerp (cdr entry))))
+ ;; (message "removing misc free %S" entry)
+ (setcdr prev undo-list)))))
+ (setq prev undo-list)))))
+
+(defmacro yas--save-restriction-and-widen (&rest body)
+ "Equivalent to (save-restriction (widen) BODY).
+Also tries to work around Emacs Bug#30931."
+ (declare (debug (body)) (indent 0))
+ ;; Disable garbage collection, since it could cause an abort.
+ `(let ((gc-cons-threshold most-positive-fixnum)
+ (old-undo-list buffer-undo-list))
+ (prog1 (save-restriction
+ (widen)
+ ,@body)
+ (yas--remove-misc-free-from-undo old-undo-list))))
-(defun yas--eval-lisp (form)
+(defun yas--eval-for-string (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 (yas--handle-error err))))))
- (when (and (consp retval)
- (eq 'yas--exception (car retval)))
- (error (cdr retval)))
- retval))
+ (let ((debug-on-error (and (not (memq yas-good-grace '(t inline)))
+ debug-on-error)))
+ (condition-case oops
+ (save-excursion
+ (yas--save-restriction-and-widen
+ (save-match-data
+ (let ((result (eval form)))
+ (when result
+ (format "%s" result))))))
+ ((debug error) (cdr oops)))))
-(defun yas--eval-lisp-no-saves (form)
- (condition-case err
- (eval form)
- (error (message "%s" (yas--handle-error err)))))
+(defun yas--eval-for-effect (form)
+ (yas--safely-call-fun (apply-partially #'eval form)))
(defun yas--read-lisp (string &optional nil-on-error)
"Read STRING as a elisp expression and return it.
@@ -1434,7 +1571,7 @@ Here's a list of currently recognized directives:
expand-env
binding
uuid)
- (if (re-search-forward "^# --\n" nil t)
+ (if (re-search-forward "^# --\\s-*\n" nil t)
(progn (setq template
(buffer-substring-no-properties (point)
(point-max)))
@@ -1642,7 +1779,7 @@ 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.
+`yas--eval-for-string'-ed.
The remaining elements are strings.
@@ -1735,8 +1872,7 @@ With prefix argument USE-JIT do jit-loading of snippets."
;;
(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))))
+ (let ((fun (apply-partially #'yas--load-directory-1 dir mode-sym)))
(if use-jit
(yas--schedule-jit mode-sym fun)
(funcall fun)))
@@ -1880,9 +2016,19 @@ prefix argument."
(yas-direct-keymaps-reload)
(run-hooks 'yas-after-reload-hook)
- (yas--message (if errors 2 3) "Reloaded everything%s...%s."
- (if no-jit "" " (snippets will load just-in-time)")
- (if errors " (some errors, check *Messages*)" "")))))
+ (let ((no-snippets
+ (cl-every (lambda (table) (= (hash-table-count table) 0))
+ (list yas--scheduled-jit-loads
+ yas--parents yas--tables))))
+ (yas--message (if (or no-snippets errors) 2 3)
+ (if no-jit "Snippets loaded %s."
+ "Prepared just-in-time loading of snippets %s.")
+ (cond (errors
+ "with some errors. Check *Messages*")
+ (no-snippets
+ "(but no snippets found)")
+ (t
+ "successfully")))))))
(defvar yas-after-reload-hook nil
"Hooks run after `yas-reload-all'.")
@@ -1934,9 +2080,24 @@ This works by stubbing a few functions, then calling
(defun yas-about ()
(interactive)
- (message (concat "yasnippet (version "
- yas--version
- ") -- pluskid/joaotavora/npostavs")))
+ (message "yasnippet (version %s) -- pluskid/joaotavora/npostavs"
+ (or (ignore-errors (car (let ((default-directory yas--loaddir))
+ (process-lines "git" "describe"
+ "--tags" "--dirty"))))
+ (when (and (featurep 'package)
+ (fboundp 'package-desc-version)
+ (fboundp 'package-version-join))
+ (defvar package-alist)
+ (ignore-errors
+ (let* ((yas-pkg (cdr (assq 'yasnippet package-alist)))
+ (version (package-version-join
+ (package-desc-version (car yas-pkg)))))
+ ;; Special case for MELPA's bogus version numbers.
+ (if (string-match "\\`20..[01][0-9][0-3][0-9][.][0-9]\\{3,4\\}\\'"
+ version)
+ (concat yas--version "-snapshot" version)
+ version))))
+ yas--version)))
;;; Apropos snippet menu:
@@ -2007,7 +2168,7 @@ static in the menu."
;; higher passes.
;;
(mapc #'(lambda (item)
- (when (and (listp (cdr item))
+ (when (and (consp (cdr-safe item))
(keymapp (nth 2 (cdr item))))
(yas--delete-from-keymap (nth 2 (cdr item)) uuid)))
(cdr keymap))
@@ -2017,9 +2178,10 @@ static in the menu."
;; Destructively modify keymap
;;
(setcdr keymap (cl-delete-if (lambda (item)
- (or (null (cdr item))
- (and (keymapp (nth 2 (cdr item)))
- (null (cdr (nth 2 (cdr item)))))))
+ (cond ((not (listp item)) nil)
+ ((null (cdr item)))
+ ((and (keymapp (nth 2 (cdr item)))
+ (null (cdr (nth 2 (cdr item))))))))
(cdr keymap))))
(defun yas-define-menu (mode menu &optional omit-items)
@@ -2057,35 +2219,34 @@ omitted from MODE's menu, even if they're manually loaded."
"Helper for `yas-define-menu'."
(cl-loop
for (type name submenu) in (reverse menu)
- if (or (eq type 'yas-item)
- (and yas-alias-to-yas/prefix-p
- (eq type 'yas/item)))
- do (let ((template (or (gethash name uuidhash)
- (puthash name
- (yas--make-template
- :table table
- :perm-group group-list
- :uuid name)
- uuidhash))))
- (define-key menu-keymap (vector (cl-gensym))
- (car (yas--template-menu-binding-pair-get-create template :stay))))
- else if (or (eq type 'yas-submenu)
- (and yas-alias-to-yas/prefix-p
- (eq type 'yas/submenu)))
- do (let ((subkeymap (make-sparse-keymap)))
- (define-key menu-keymap (vector (cl-gensym))
- `(menu-item ,name ,subkeymap))
- (yas--define-menu-1 table
- subkeymap
- submenu
- uuidhash
- (append group-list (list name))))
- else if (or (eq type 'yas-separator)
- (and yas-alias-to-yas/prefix-p
- (eq type 'yas/separator)))
- do (define-key menu-keymap (vector (cl-gensym))
- '(menu-item "----"))
- else do (yas--message 1 "Don't know anything about menu entry %s" type)))
+ collect (cond
+ ((or (eq type 'yas-item)
+ (and yas-alias-to-yas/prefix-p
+ (eq type 'yas/item)))
+ (let ((template (or (gethash name uuidhash)
+ (puthash name
+ (yas--make-template
+ :table table
+ :perm-group group-list
+ :uuid name)
+ uuidhash))))
+ (car (yas--template-menu-binding-pair-get-create
+ template :stay))))
+ ((or (eq type 'yas-submenu)
+ (and yas-alias-to-yas/prefix-p
+ (eq type 'yas/submenu)))
+ (let ((subkeymap (make-sparse-keymap)))
+ (yas--define-menu-1 table subkeymap submenu uuidhash
+ (append group-list (list name)))
+ `(menu-item ,name ,subkeymap)))
+ ((or (eq type 'yas-separator)
+ (and yas-alias-to-yas/prefix-p
+ (eq type 'yas/separator)))
+ '(menu-item "----"))
+ (t (yas--message 1 "Don't know anything about menu entry %s" type)
+ nil))
+ into menu-entries
+ finally do (push (apply #'vector menu-entries) (cdr menu-keymap))))
(defun yas--define (mode key template &optional name condition group)
"Define a snippet. Expanding KEY into TEMPLATE.
@@ -2114,7 +2275,6 @@ Just put this function in `hippie-expand-try-functions-list'."
;;;
;;;
;;;
-(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
@@ -2168,23 +2328,24 @@ object satisfying `yas--field-p' to restrict the expansion to."
(nth 2 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 (cl-subseq (this-command-keys-vector)
+(defun yas--maybe-expand-from-keymap-filter (cmd)
+ (let* ((yas--condition-cache-timestamp (current-time))
+ (vec (cl-subseq (this-command-keys-vector)
(if current-prefix-arg
(length (this-command-keys))
0)))
(templates (cl-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)))))
+ (if templates (or cmd templates))))
+
+(defun yas-expand-from-keymap ()
+ "Directly expand some snippets, searching `yas--direct-keymaps'."
+ (interactive)
+ (setq yas--condition-cache-timestamp (current-time))
+ (let* ((templates (yas--maybe-expand-from-keymap-filter nil)))
+ (when templates
+ (yas--expand-or-prompt-for-template templates))))
(defun yas--expand-or-prompt-for-template (templates &optional start end)
"Expand one of TEMPLATES from START to END.
@@ -2312,18 +2473,17 @@ Honours `yas-choose-tables-first', `yas-choose-keys-first' and
:key #'yas--template-name :test #'string=)))
(defun yas-lookup-snippet (name &optional mode noerror)
- "Get the snippet content for the snippet NAME in MODE's tables.
+ "Get the snippet named NAME in MODE's tables.
MODE defaults to the current buffer's `major-mode'. If NOERROR
is non-nil, then don't signal an error if there isn't any snippet
called NAME.
Honours `yas-buffer-local-condition'."
- (let ((snippet (yas--lookup-snippet-1 name mode)))
- (cond
- (snippet (yas--template-content snippet))
- (noerror nil)
- (t (error "No snippet named: %s" name)))))
+ (cond
+ ((yas--lookup-snippet-1 name mode))
+ (noerror nil)
+ (t (error "No snippet named: %s" name))))
(defun yas-insert-snippet (&optional no-condition)
"Choose a snippet to expand, pop-up a list of choices according
@@ -2419,10 +2579,11 @@ where snippets of table might exist."
(tables (if 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))
+ (unless table
+ ;; The major mode is probably the best guess, put it first.
+ (let ((major-mode-table (yas--table-get-create major-mode)))
+ (cl-callf2 delq major-mode-table tables)
+ (push major-mode-table tables)))
(mapcar #'(lambda (table)
(cons table
@@ -2452,6 +2613,10 @@ where snippets of table might exist."
;; create the .yas-parents file here...
candidate)))))
+;; NOTE: Using the traditional "*new snippet*" stops whitespace mode
+;; from activating (it doesn't like the leading "*").
+(defconst yas-new-snippet-buffer-name "+new-snippet+")
+
(defun yas-new-snippet (&optional no-template)
"Pops a new buffer for writing a snippet.
@@ -2464,7 +2629,7 @@ NO-TEMPLATE is non-nil."
(buffer-substring-no-properties
(region-beginning) (region-end))))))
- (switch-to-buffer "*new snippet*")
+ (switch-to-buffer yas-new-snippet-buffer-name)
(erase-buffer)
(kill-all-local-variables)
(snippet-mode)
@@ -2609,8 +2774,11 @@ and `kill-buffer' instead."
(save-buffer)))
(quit-window kill)))
+(declare-function yas-debug-snippets "yasnippet-debug")
+
(defun yas-tryout-snippet (&optional debug)
- "Test current buffer's snippet template in other buffer."
+ "Test current buffer's snippet template in other buffer.
+DEBUG is for debugging the YASnippet engine itself."
(interactive "P")
(let* ((major-mode-and-parent (yas--compute-major-mode-and-parents buffer-file-name))
(parsed (yas--parse-template))
@@ -2640,9 +2808,9 @@ and `kill-buffer' instead."
(point-max)
(yas--template-expand-env yas--current-template))
(when (and debug
- (require 'yasnippet-debug nil t)
- (fboundp 'yas-debug-snippet-vars))
- (add-hook 'post-command-hook #'yas-debug-snippet-vars nil t))))
+ (require 'yasnippet-debug nil t))
+ (yas-debug-snippets "*YASnippet trace*" 'snippet-navigation)
+ (display-buffer "*YASnippet trace*"))))
(t
(yas--message 1 "Cannot test snippet for unknown major mode")))))
@@ -2828,22 +2996,23 @@ The last element of POSSIBILITIES may be a list of strings."
key)))))
(defun yas-throw (text)
- "Throw a yas--exception with TEXT as the reason."
- (throw 'yas--exception (cons 'yas--exception text)))
+ "Signal `yas-exception' with TEXT as the reason."
+ (signal 'yas-exception (list text)))
+(put 'yas-exception 'error-conditions '(error yas-exception))
+(put 'yas-exception 'error-message "[yas] Exception")
(defun yas-verify-value (possibilities)
"Verify that the current field value is in POSSIBILITIES.
-
-Otherwise throw exception."
- (when (and yas-moving-away-p
- (cl-notany (lambda (pos) (string= pos yas-text)) possibilities))
- (yas-throw (yas--format "Field only allows %s" possibilities))))
+Otherwise signal `yas-exception'."
+ (when (and yas-moving-away-p (cl-notany (lambda (pos) (string= pos yas-text)) possibilities))
+ (yas-throw (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)))
+Use this in primary and mirror transformations to get the text of
+other fields."
+ (let* ((snippet (car (yas-active-snippets)))
(field (and snippet
(yas--snippet-find-field snippet number))))
(when field
@@ -2903,10 +3072,11 @@ Use this in primary and mirror transformations to tget."
(put 'yas--active-field-overlay 'permanent-local t)
(put 'yas--field-protection-overlays 'permanent-local t)
-(cl-defstruct (yas--snippet (:constructor yas--make-snippet ()))
+(cl-defstruct (yas--snippet (:constructor yas--make-snippet (expand-env)))
"A snippet.
..."
+ expand-env
(fields '())
(exit nil)
(id (yas--snippet-next-id) :read-only t)
@@ -2955,6 +3125,42 @@ DEPTH is a count of how many nested mirrors can affect this mirror"
marker
next)
+(defmacro yas--letenv (env &rest body)
+ "Evaluate BODY with bindings from ENV.
+ENV is a lisp expression that evaluates to list of elements with
+the form (VAR FORM), where VAR is a symbol and FORM is a lisp
+expression that evaluates to its value."
+ (declare (debug (form body)) (indent 1))
+ (let ((envvar (make-symbol "envvar")))
+ `(let ((,envvar ,env))
+ (cl-progv
+ (mapcar #'car ,envvar)
+ (mapcar (lambda (v-f) (eval (cadr v-f))) ,envvar)
+ ,@body))))
+
+(defun yas--snippet-map-markers (fun snippet)
+ "Apply FUN to all marker (sub)fields in SNIPPET.
+Update each field with the result of calling FUN."
+ (dolist (field (yas--snippet-fields snippet))
+ (setf (yas--field-start field) (funcall fun (yas--field-start field)))
+ (setf (yas--field-end field) (funcall fun (yas--field-end field)))
+ (dolist (mirror (yas--field-mirrors field))
+ (setf (yas--mirror-start mirror) (funcall fun (yas--mirror-start mirror)))
+ (setf (yas--mirror-end mirror) (funcall fun (yas--mirror-end mirror)))))
+ (let ((snippet-exit (yas--snippet-exit snippet)))
+ (when snippet-exit
+ (setf (yas--exit-marker snippet-exit)
+ (funcall fun (yas--exit-marker snippet-exit))))))
+
+(defun yas--snippet-live-p (snippet)
+ "Return non-nil if SNIPPET hasn't been committed."
+ (catch 'live
+ (yas--snippet-map-markers (lambda (m)
+ (if (markerp m) m
+ (throw 'live nil)))
+ snippet)
+ t))
+
(defun yas--apply-transform (field-or-mirror field &optional empty-on-nil-p)
"Calculate transformed string for FIELD-OR-MIRROR from FIELD.
@@ -2973,7 +3179,7 @@ string iff EMPTY-ON-NIL-P is true."
(transformed (and transform
(save-excursion
(goto-char start-point)
- (let ((ret (yas--eval-lisp transform)))
+ (let ((ret (yas--eval-for-string transform)))
(or ret (and empty-on-nil-p "")))))))
transformed))
@@ -3040,18 +3246,30 @@ through the field's start point"
(not (and (yas--field-number field)
(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
- (delq nil (delete-dups
- (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-active-snippets (&optional beg end)
+ "Return a sorted list of active snippets.
+The most recently-inserted snippets are returned first.
+
+Only snippets overlapping the region BEG ... END are returned.
+Overlapping has the same meaning as described in `overlays-in'.
+If END is omitted, it defaults to (1+ BEG). If BEG is omitted,
+it defaults to point. A non-nil, non-buffer position BEG is
+equivalent to a range covering the whole buffer."
+ (unless beg
+ (setq beg (point)))
+ (cond ((not (or (integerp beg) (markerp beg)))
+ (setq beg (point-min) end (point-max)))
+ ((not end)
+ (setq end (1+ beg))))
+ (cl-sort
+ (delete-dups ;; Snippets have multiple overlays.
+ (delq nil
+ (mapcar (lambda (ov) (overlay-get ov 'yas--snippet))
+ (overlays-in beg end))))
+ #'>= :key #'yas--snippet-id))
+
+(define-obsolete-function-alias 'yas--snippets-at-point
+ 'yas-active-snippets "0.12")
(defun yas-next-field-or-maybe-expand ()
"Try to expand a snippet at a key before point.
@@ -3068,7 +3286,7 @@ Otherwise delegate to `yas-next-field'."
(defun yas-next-field-will-exit-p (&optional arg)
"Return non-nil if (yas-next-field ARG) would exit the current snippet."
- (let ((snippet (car (yas--snippets-at-point)))
+ (let ((snippet (car (yas-active-snippets)))
(active (overlay-get yas--active-field-overlay 'yas--field)))
(when snippet
(not (yas--find-next-field arg snippet active)))))
@@ -3088,23 +3306,28 @@ Otherwise delegate to `yas-next-field'."
If there's none, exit the snippet."
(interactive)
(unless arg (setq arg 1))
- (let* ((snippet (car (yas--snippets-at-point)))
+ (let* ((snippet (car (yas-active-snippets)))
(active-field (overlay-get yas--active-field-overlay 'yas--field))
(target-field (yas--find-next-field arg snippet active-field)))
- ;; Apply transform to active field.
- (when active-field
- (let ((yas-moving-away-p t))
- (when (yas--field-update-display active-field)
- (yas--update-mirrors snippet))))
- ;; Now actually move...
- (if target-field
- (yas--move-to-field snippet target-field)
- (yas-exit-snippet snippet))))
+ (yas--letenv (yas--snippet-expand-env snippet)
+ ;; Apply transform to active field.
+ (when active-field
+ (let ((yas-moving-away-p t))
+ (when (yas--field-update-display active-field)
+ (yas--update-mirrors snippet))))
+ ;; Now actually move...
+ (if target-field
+ (yas--move-to-field snippet target-field)
+ (yas-exit-snippet snippet)))))
(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))
+ ;; Only move active field overlays if this is field is from the
+ ;; innermost snippet.
+ (when (eq snippet (car (yas-active-snippets (1- (yas--field-start field))
+ (1+ (yas--field-end field)))))
+ (yas--make-move-active-field-overlay snippet field)))
(defun yas--move-to-field (snippet field)
"Update SNIPPET to move to field FIELD.
@@ -3112,6 +3335,7 @@ If there's none, exit the snippet."
Also create some protection overlays"
(goto-char (yas--field-start field))
(yas--place-overlays snippet field)
+ (overlay-put yas--active-field-overlay 'yas--snippet snippet)
(overlay-put yas--active-field-overlay 'yas--field field)
(let ((number (yas--field-number field)))
;; check for the special ${0: ...} field
@@ -3137,13 +3361,13 @@ Also create some protection overlays"
(defun yas-abort-snippet (&optional snippet)
(interactive)
(let ((snippet (or snippet
- (car (yas--snippets-at-point)))))
+ (car (yas-active-snippets)))))
(when snippet
(setf (yas--snippet-force-exit snippet) t))))
(defun yas-exit-snippet (snippet)
"Goto exit-marker of SNIPPET."
- (interactive (list (cl-first (yas--snippets-at-point))))
+ (interactive (list (cl-first (yas-active-snippets))))
(when snippet
(setf (yas--snippet-force-exit snippet) t)
(goto-char (if (yas--snippet-exit snippet)
@@ -3156,7 +3380,7 @@ Also create some protection overlays"
(mapc #'(lambda (snippet)
(yas-exit-snippet snippet)
(yas--check-commit-snippet))
- (yas--snippets-at-point 'all-snippets)))
+ (yas-active-snippets 'all)))
;;; Some low level snippet-routines:
@@ -3181,7 +3405,8 @@ This renders the snippet as ordinary text."
(overlay-buffer control-overlay))
(setq yas-snippet-beg (overlay-start control-overlay))
(setq yas-snippet-end (overlay-end control-overlay))
- (delete-overlay control-overlay))
+ (delete-overlay control-overlay)
+ (setf (yas--snippet-control-overlay snippet) nil))
(let ((yas--inhibit-overlay-hooks t))
(when yas--active-field-overlay
@@ -3201,9 +3426,8 @@ This renders the snippet as ordinary text."
;;
(yas--markers-to-points snippet)
- ;; Take care of snippet revival
- ;;
- (if yas-snippet-revival
+ ;; Take care of snippet revival on undo.
+ (if (and yas-snippet-revival (listp buffer-undo-list))
(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
@@ -3212,100 +3436,155 @@ This renders the snippet as ordinary text."
(yas--message 4 "Snippet %s exited." (yas--snippet-id snippet)))
-(defun yas--safely-run-hooks (hook-var)
- (condition-case error
- (run-hooks hook-var)
- (error
- (yas--message 2 "%s error: %s" hook-var (error-message-string error)))))
+(defvar yas--snippets-to-move nil)
+(make-variable-buffer-local 'yas--snippets-to-move)
+(defun yas--prepare-snippets-for-move (beg end buf pos)
+ "Gather snippets in BEG..END for moving to POS in BUF."
+ (let ((to-move nil)
+ (snippets (yas-active-snippets beg end))
+ (dst-base-line (with-current-buffer buf
+ (count-lines (point-min) pos))))
+ (when snippets
+ (dolist (snippet snippets)
+ (yas--snippet-map-markers
+ (lambda (m)
+ (goto-char m)
+ (beginning-of-line)
+ (prog1 (cons (count-lines (point-min) (point))
+ (yas--snapshot-marker-location m))
+ (set-marker m nil)))
+ snippet)
+ (let ((ctrl-ov (yas--snapshot-overlay-line-location
+ (yas--snippet-control-overlay snippet))))
+ (push (list ctrl-ov dst-base-line snippet) to-move)
+ (delete-overlay (car ctrl-ov))))
+ (with-current-buffer buf
+ (setq yas--snippets-to-move (nconc to-move yas--snippets-to-move))))))
+
+(defun yas--on-buffer-kill ()
+ ;; Org mode uses temp buffers for fontification and "native tab",
+ ;; move all the snippets to the original org-mode buffer when it's
+ ;; killed.
+ (let ((org-marker nil))
+ (when (and yas-minor-mode
+ (or (bound-and-true-p org-edit-src-from-org-mode)
+ (bound-and-true-p org-src--from-org-mode))
+ (markerp
+ (setq org-marker
+ (or (bound-and-true-p org-edit-src-beg-marker)
+ (bound-and-true-p org-src--beg-marker)))))
+ (yas--prepare-snippets-for-move
+ (point-min) (point-max)
+ (marker-buffer org-marker) org-marker))))
+
+(add-hook 'kill-buffer-hook #'yas--on-buffer-kill)
+
+(defun yas--finish-moving-snippets ()
+ "Finish job started in `yas--prepare-snippets-for-move'."
+ (cl-loop for (ctrl-ov base-line snippet) in yas--snippets-to-move
+ for base-pos = (progn (goto-char (point-min))
+ (forward-line base-line) (point))
+ do (yas--snippet-map-markers
+ (lambda (l-m-r-w)
+ (goto-char base-pos)
+ (forward-line (nth 0 l-m-r-w))
+ (save-restriction
+ (narrow-to-region (line-beginning-position)
+ (line-end-position))
+ (yas--restore-marker-location (cdr l-m-r-w)))
+ (nth 1 l-m-r-w))
+ snippet)
+ (goto-char base-pos)
+ (yas--restore-overlay-location ctrl-ov)
+ (yas--maybe-move-to-active-field snippet))
+ (setq yas--snippets-to-move nil))
+
+(defun yas--safely-call-fun (fun)
+ "Call FUN and catch any errors."
+ (condition-case error
+ (funcall fun)
+ ((debug error)
+ (yas--message 2 "Error running %s: %s" fun
+ (error-message-string error)))))
+
+(defun yas--safely-run-hook (hook)
+ "Call HOOK's functions.
+HOOK should be a symbol, a hook variable, as in `run-hooks'."
+ (let ((debug-on-error (and (not (memq yas-good-grace '(t hooks)))
+ debug-on-error)))
+ (yas--safely-call-fun (apply-partially #'run-hooks hook))))
(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))
+ (let* ((snippets (yas-active-snippets 'all))
(snippets-left snippets)
- (snippet-exit-transform))
+ (snippet-exit-transform)
+ ;; Record the custom snippet `yas-after-exit-snippet-hook'
+ ;; set in the expand-env field.
+ (snippet-exit-hook yas-after-exit-snippet-hook))
(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))))
+ (yas--letenv (yas--snippet-expand-env snippet)
+ ;; Note: the `force-exit' field could be a transform in case of
+ ;; ${0: ...}, see `yas--move-to-field'.
+ (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)
+ (setq snippet-exit-hook yas-after-exit-snippet-hook)
+ (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))))
+ (when snippet-exit-transform
+ (yas--eval-for-effect snippet-exit-transform))
+ (let ((yas-after-exit-snippet-hook snippet-exit-hook))
+ (yas--safely-run-hook '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 was found useful for performance reasons, so that an excessive
+;; number of live markers aren't kept around in the
+;; `buffer-undo-list'. We don't reuse the original marker object
+;; because that leaves an unreadable object in the history list and
+;; undo-tree persistence has trouble with that.
;;
-;; This shouldn't bring horrible problems with undo/redo, but it
-;; you never know
+;; This shouldn't bring horrible problems with undo/redo, but 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)))))))
+ "Save all markers of SNIPPET as positions."
+ (yas--snippet-map-markers (lambda (m)
+ (prog1 (marker-position m)
+ (set-marker m nil)))
+ snippet))
(defun yas--points-to-markers (snippet)
- "Convert all cons (POINT . MARKER) in SNIPPET to markers.
+ "Restore SNIPPET's marker positions, saved by `yas--markers-to-points'."
+ (yas--snippet-map-markers #'copy-marker snippet))
-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--maybe-move-to-active-field (snippet)
+ "Try to move to SNIPPET's active (or first) field and return it if found."
+ (let ((target-field (or (yas--snippet-active-field snippet)
+ (car (yas--snippet-fields snippet)))))
+ (when target-field
+ (yas--move-to-field snippet target-field)
+ target-field)))
(defun yas--field-contains-point-p (field &optional point)
(let ((point (or point
@@ -3332,26 +3611,41 @@ holds the keymap."
nil
t)))
(overlay-put overlay 'keymap yas-keymap)
- (overlay-put overlay 'priority 100)
+ (overlay-put overlay 'priority yas-overlay-priority)
(overlay-put overlay 'yas--snippet snippet)
overlay))
+(defun yas-current-field ()
+ "Return the currently active field."
+ (and yas--active-field-overlay
+ (overlay-buffer yas--active-field-overlay)
+ (overlay-get yas--active-field-overlay 'yas--field)))
+
+(defun yas--maybe-clear-field-filter (cmd)
+ "Return CMD if at start of unmodified snippet field.
+Use as a `:filter' argument for a conditional keybinding."
+ (let ((field (yas-current-field)))
+ (when (and field
+ (not (yas--field-modified-p field))
+ (eq (point) (marker-position (yas--field-start field))))
+ cmd)))
+
+(defun yas-skip-and-clear-field (&optional field)
+ "Clears unmodified FIELD if at field start, skips to next tab."
+ (interactive)
+ (yas--skip-and-clear (or field (yas-current-field)))
+ (yas-next-field 1))
+
(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)))))
+ (declare (obsolete "Bind to `yas-maybe-skip-and-clear-field' instead." "0.13"))
+ (cond ((yas--maybe-clear-field-filter t)
+ (yas--skip-and-clear (or field (yas-current-field)))
+ (yas-next-field 1))
+ (t (call-interactively 'delete-char))))
(defun yas--skip-and-clear (field &optional from)
"Deletes the region of FIELD and sets it's modified state to t.
@@ -3388,7 +3682,7 @@ Move the overlay, or create it if it does not exit."
(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 'priority yas-overlay-priority)
(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))
@@ -3412,19 +3706,97 @@ field start. This hook does nothing if an undo is in progress."
(unless (or (not after?)
yas--inhibit-overlay-hooks
(not (overlayp yas--active-field-overlay)) ; Avoid Emacs bug #21824.
+ ;; If a single change hits multiple overlays of the same
+ ;; snippet, then we delete the snippet the first time,
+ ;; and then subsequent calls get a deleted overlay.
+ ;; Don't delete the snippet again!
+ (not (overlay-buffer overlay))
(yas--undo-in-progress))
- (let* ((inhibit-modification-hooks t)
+ (let* ((inhibit-modification-hooks nil)
+ (yas--inhibit-overlay-hooks t)
(field (overlay-get overlay 'yas--field))
(snippet (overlay-get yas--active-field-overlay 'yas--snippet)))
- (save-match-data
- (when (yas--skip-and-clear-field-p field beg end length)
- ;; We delete text starting from the END of insertion.
- (yas--skip-and-clear field end))
- (setf (yas--field-modified-p field) t)
- (yas--advance-end-maybe field (overlay-end overlay))
- (save-excursion
- (yas--field-update-display field))
- (yas--update-mirrors snippet)))))
+ (if (yas--snippet-live-p snippet)
+ (save-match-data
+ (yas--letenv (yas--snippet-expand-env snippet)
+ (when (yas--skip-and-clear-field-p field beg end length)
+ ;; We delete text starting from the END of insertion.
+ (yas--skip-and-clear field end))
+ (setf (yas--field-modified-p field) t)
+ (yas--advance-end-maybe field (overlay-end overlay))
+ (save-excursion
+ (yas--field-update-display field))
+ (yas--update-mirrors snippet)))
+ (lwarn '(yasnippet zombie) :warning "Killing zombie snippet!")
+ (delete-overlay overlay)))))
+
+(defun yas--auto-fill ()
+ (let* ((orig-point (point))
+ (end (progn (forward-paragraph) (point)))
+ (beg (progn (backward-paragraph) (point)))
+ (snippets (yas-active-snippets beg end))
+ (remarkers nil)
+ (reoverlays nil))
+ (dolist (snippet snippets)
+ (dolist (m (yas--collect-snippet-markers snippet))
+ (when (and (<= beg m) (<= m end))
+ (push (yas--snapshot-marker-location m beg end) remarkers)))
+ (push (yas--snapshot-overlay-location
+ (yas--snippet-control-overlay snippet) beg end)
+ reoverlays))
+ (goto-char orig-point)
+ (let ((yas--inhibit-overlay-hooks t))
+ (if (null yas--original-auto-fill-function)
+ ;; Try to get more info on #873/919.
+ (let ((yas--fill-fun-values `((t ,(default-value 'yas--original-auto-fill-function))))
+ (fill-fun-values `((t ,(default-value 'auto-fill-function))))
+ ;; Listing 2 buffers with the same value is enough
+ (print-length 3))
+ (save-current-buffer
+ (dolist (buf (let ((bufs (buffer-list)))
+ ;; List the current buffer first.
+ (setq bufs (cons (current-buffer)
+ (remq (current-buffer) bufs)))))
+ (set-buffer buf)
+ (let* ((yf-cell (assq yas--original-auto-fill-function
+ yas--fill-fun-values))
+ (af-cell (assq auto-fill-function fill-fun-values)))
+ (when (local-variable-p 'yas--original-auto-fill-function)
+ (if yf-cell (setcdr yf-cell (cons buf (cdr yf-cell)))
+ (push (list yas--original-auto-fill-function buf) yas--fill-fun-values)))
+ (when (local-variable-p 'auto-fill-function)
+ (if af-cell (setcdr af-cell (cons buf (cdr af-cell)))
+ (push (list auto-fill-function buf) fill-fun-values))))))
+ (lwarn '(yasnippet auto-fill bug) :error
+ "`yas--original-auto-fill-function' unexpectedly nil in %S! Disabling auto-fill.
+ %S
+ `auto-fill-function': %S\n%s"
+ (current-buffer) yas--fill-fun-values fill-fun-values
+ (if (fboundp 'backtrace--print-frame)
+ (with-output-to-string
+ (mapc (lambda (frame)
+ (apply #'backtrace--print-frame frame))
+ yas--watch-auto-fill-backtrace))
+ ""))
+ ;; Try to avoid repeated triggering of this bug.
+ (auto-fill-mode -1)
+ ;; Don't pop up more than once in a session (still log though).
+ (defvar warning-suppress-types) ; `warnings' is autoloaded by `lwarn'.
+ (add-to-list 'warning-suppress-types '(yasnippet auto-fill bug)))
+ (funcall yas--original-auto-fill-function)))
+ (save-excursion
+ (setq end (progn (forward-paragraph) (point)))
+ (setq beg (progn (backward-paragraph) (point))))
+ (save-excursion
+ (save-restriction
+ (narrow-to-region beg end)
+ (mapc #'yas--restore-marker-location remarkers)
+ (mapc #'yas--restore-overlay-location reoverlays))
+ (mapc (lambda (snippet)
+ (yas--letenv (yas--snippet-expand-env snippet)
+ (yas--update-mirrors snippet)))
+ snippets))))
+
;;; Apropos protection overlays:
;;
@@ -3475,7 +3847,7 @@ Move the overlays, or create them if they do not exit."
(not after?)
(= length (- end beg)) ; deletion or insertion
(yas--undo-in-progress))
- (let ((snippets (yas--snippets-at-point)))
+ (let ((snippets (yas-active-snippets)))
(yas--message 2 "Committing snippets. Action would destroy a protection overlay.")
(cl-loop for snippet in snippets
do (yas--commit-snippet snippet)))))
@@ -3507,122 +3879,101 @@ Move the overlays, or create them if they do not exit."
;; 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.
+
+(defun yas-expand-snippet (snippet &optional start end expand-env)
+ "Expand SNIPPET at current point.
Text between START and END will be deleted before inserting
-template. EXPAND-ENV is a list of (SYM VALUE) let-style dynamic bindings
-considered when expanding the snippet."
+template. EXPAND-ENV is a list of (SYM VALUE) let-style dynamic
+bindings considered when expanding the snippet. If omitted, use
+SNIPPET's expand-env field.
+
+SNIPPET may be a snippet structure (e.g., as returned by
+`yas-lookup-snippet'), or just a snippet body (which is a string
+for normal snippets, and a list for command snippets)."
(cl-assert (and yas-minor-mode
(memq 'yas--post-command-handler post-command-hook))
nil
"[yas] `yas-expand-snippet' needs properly setup `yas-minor-mode'")
(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
+ (let* ((clear-field
+ (let ((field (and yas--active-field-overlay
+ (overlay-buffer yas--active-field-overlay)
+ (overlay-get yas--active-field-overlay 'yas--field))))
+ (and field (yas--skip-and-clear-field-p
+ field (point) (point) 0)
+ field)))
+ (start (cond (start)
+ ((region-active-p)
+ (region-beginning))
+ (clear-field
+ (yas--field-start clear-field))
+ (t (point))))
+ (end (cond (end)
+ ((region-active-p)
+ (region-end))
+ (clear-field
+ (yas--field-end clear-field))
+ (t (point))))
+ (to-delete (and (> end start)
(buffer-substring-no-properties start end)))
- snippet)
+ (yas-selected-text
+ (cond (yas-selected-text)
+ ((and (region-active-p)
+ (not clear-field))
+ to-delete))))
(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))
+ (when to-delete
(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
- ;;
+ (let ((content (if (yas--template-p snippet)
+ (yas--template-content snippet)
+ snippet)))
+ (when (and (not expand-env) (yas--template-p snippet))
+ (setq expand-env (yas--template-expand-env snippet)))
+ (cond ((listp content)
+ ;; x) This is a snippet-command.
+ (yas--eval-for-effect content))
+ (t
+ ;; x) This is a snippet-snippet :-)
(setq yas--start-column (current-column))
- (let ((yas--inhibit-overlay-hooks t)
- ;; Avoid major-mode's syntax propertizing function,
- ;; since we mess with the syntax-table and also
- ;; insert things that are not valid in the
- ;; major-mode language syntax anyway.
- (syntax-propertize-function nil))
+ ;; Stacked expansion: also shoosh the overlay modification hooks.
+ (let ((yas--inhibit-overlay-hooks t))
(setq snippet
- (if expand-env
- (eval `(let* ,expand-env
- (insert content)
- (yas--snippet-create start (point))))
- (insert content)
- (yas--snippet-create start (point)))))
- ;; Invalidate any syntax-propertizing done while `syntax-propertize-function' was nil
- (syntax-ppss-flush-cache start))
-
- ;; 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 4 "snippet expanded.")
- t))))
-
-(defun yas--take-care-of-redo (_beg _end snippet)
+ (yas--snippet-create content expand-env 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))
+
+ ;; 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--letenv (yas--snippet-expand-env snippet)
+ (yas--move-to-field snippet first-field))
+ (when (and (eq (yas--field-number first-field) 0)
+ (> (length (yas--field-text-for-display
+ first-field))
+ 0))
+ ;; Keep region for ${0:exit text}.
+ (setq deactivate-mark nil))))
+ (yas--message 4 "snippet %d expanded." (yas--snippet-id snippet))
+ t)))))
+
+(defun yas--take-care-of-redo (snippet)
"Commits SNIPPET, which in turn pushes an undo action for reviving it.
Meant to exit in the `buffer-undo-list'."
@@ -3641,43 +3992,58 @@ 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)
+ (when (yas--maybe-move-to-active-field snippet)
+ (setf (yas--snippet-control-overlay snippet) (yas--make-control-overlay snippet beg end))
+ (overlay-put (yas--snippet-control-overlay snippet) 'yas--snippet snippet)
+ (when (listp buffer-undo-list)
+ (push `(apply yas--take-care-of-redo ,snippet)
buffer-undo-list))))
-(defun yas--snippet-create (begin end)
+(defun yas--snippet-create (content expand-env 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)))
+ (let ((snippet (yas--make-snippet expand-env)))
+ (yas--letenv expand-env
+ ;; Put a single undo action for the expanded snippet's
+ ;; content.
+ (let ((buffer-undo-list t))
+ ;; Some versions of cc-mode fail when inserting snippet
+ ;; content in a narrowed buffer.
+ (goto-char begin)
+ (insert content)
+ (setq end (+ end (length content)))
+ (narrow-to-region begin end)
+ (goto-char (point-min))
+ (yas--snippet-parse-create snippet))
+ (when (listp buffer-undo-list)
+ (push (cons (point-min) (point-max))
+ buffer-undo-list))
+
+ ;; Indent, collecting undo information normally.
+ (yas--indent snippet)
+
+ ;; Follow up with `yas--take-care-of-redo' on the newly
+ ;; inserted snippet boundaries.
+ (when (listp buffer-undo-list)
+ (push `(apply yas--take-care-of-redo ,snippet)
+ buffer-undo-list))
+
+ ;; 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":
@@ -3762,22 +4128,27 @@ has to be called before the $-constructs are deleted."
(when soup
(cl-reduce link-foms soup)))))
-(defun yas--calculate-mirrors-in-fields (snippet mirror)
- "Attempt to assign a parent field of SNIPPET to the mirror MIRROR.
+(defun yas--calculate-simple-fom-parentage (snippet fom)
+ "Discover if FOM is parented by some field in SNIPPET.
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))
+ (dolist (field (remq fom (yas--snippet-fields snippet)))
+ (when (and (<= (yas--field-start field) (yas--fom-start fom))
+ (<= (yas--fom-end fom) (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)))))
+ (cond ((yas--field-p fom)
+ (setf (yas--field-parent-field fom) field))
+ ((yas--mirror-p fom)
+ (setf (yas--mirror-parent-field fom) field))
+ (t ; it's an exit, so noop
+ nil ))))))
(defun yas--advance-end-maybe (fom newend)
"Maybe advance FOM's end to NEWEND if it needs it.
@@ -3821,11 +4192,6 @@ next FOM. Works its way up recursively for parents of parents."
"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.")
-
(defvar yas--indent-markers nil
"List of markers for manual indentation.")
@@ -3834,42 +4200,30 @@ expansion.")
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 indent markers: `$>'.
- (goto-char parse-start)
- (yas--indent-parse-create)
- ;; 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
- ;;
+ (let ((saved-quotes nil)
+ (parse-start (point)))
+ ;; Avoid major-mode's syntax propertizing function, since we
+ ;; change the syntax-table while calling `scan-sexps'.
+ (let ((syntax-propertize-function nil))
+ (setq yas--dollar-regions nil) ; Reset the yas--dollar-regions.
+ (yas--protect-escapes nil '(?`)) ; Protect just the backquotes.
+ (goto-char parse-start)
+ (setq saved-quotes (yas--save-backquotes)) ; `expressions`.
+ (yas--protect-escapes) ; Protect escaped characters.
+ (goto-char parse-start)
+ (yas--indent-parse-create) ; Parse indent markers: `$>'.
+ (goto-char parse-start)
+ (yas--field-parse-create snippet) ; Parse fields with {}.
+ (goto-char parse-start)
+ (yas--simple-fom-create snippet) ; Parse simple mirrors & fields.
+ (goto-char parse-start)
+ (yas--transform-mirror-parse-create snippet) ; Parse mirror transforms.
+ ;; Invalidate any syntax-propertizing done while
+ ;; `syntax-propertize-function' was nil.
+ (syntax-ppss-flush-cache parse-start))
+ ;; Set "next" links of fields & mirrors.
(yas--calculate-adjacencies snippet)
- ;; Delete $-constructs
- ;;
- (save-restriction
- (widen)
+ (yas--save-restriction-and-widen ; Delete $-constructs.
(yas--delete-regions yas--dollar-regions))
;; Make sure to do this insertion *after* deleting the dollar
;; regions, otherwise we invalidate the calculated positions of
@@ -3884,55 +4238,153 @@ Meant to be called in a narrowed buffer, does various passes"
(get-register yas-wrap-around-region))
(insert (prog1 (get-register yas-wrap-around-region)
(set-register yas-wrap-around-region nil)))))
- ;; restore backquoted expression values
- ;;
- (yas--restore-backquotes)
- ;; restore escapes
- ;;
+ (yas--restore-backquotes saved-quotes) ; Restore `expression` values.
(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)))
+ (yas--restore-escapes) ; Restore escapes.
+ (yas--update-mirrors snippet) ; Update mirrors for the first time.
+ (goto-char parse-start)))
+
+;; HACK: Some implementations of `indent-line-function' (called via
+;; `indent-according-to-mode') delete text before they insert (like
+;; cc-mode), some make complicated regexp replacements (looking at
+;; you, org-mode). To find place where the marker "should" go after
+;; indentation, we create a regexp based on what the line looks like
+;; before, putting a capture group where the marker is. The regexp
+;; matches any whitespace with [[:space:]]* to allow for the
+;; indentation changing whitespace. Additionally, we try to preserve
+;; the amount of whitespace *following* the marker, because
+;; indentation generally affects whitespace at the beginning, not the
+;; end.
+;;
+;; Two other cases where we apply a similar strategy:
+;;
+;; 1. Handling `auto-fill-mode', in this case we need to use the
+;; current paragraph instead of line.
+;;
+;; 2. Moving snippets from an `org-src' temp buffer into the main org
+;; buffer, in this case we need to count the line offsets (because org
+;; may add indentation on each line making character positions
+;; unreliable).
+;;
+;; This is all best-effort heuristic stuff, but it should cover 99% of
+;; use-cases.
+
+(defun yas--snapshot-marker-location (marker &optional beg end)
+ "Returns info for restoring MARKER's location after indent.
+The returned value is a list of the form (MARKER REGEXP WS-COUNT)."
+ (unless beg (setq beg (line-beginning-position)))
+ (unless end (setq end (line-end-position)))
+ (let ((before (split-string (buffer-substring-no-properties beg marker)
+ "[[:space:]\n]+" t))
+ (after (split-string (buffer-substring-no-properties marker end)
+ "[[:space:]\n]+" t)))
+ (list marker
+ (concat "[[:space:]\n]*"
+ (mapconcat (lambda (s)
+ (if (eq s marker) "\\(\\)"
+ (regexp-quote s)))
+ (nconc before (list marker) after)
+ "[[:space:]\n]*"))
+ (progn (goto-char marker)
+ (skip-chars-forward "[:space:]\n" end)
+ (- (point) marker)))))
+
+(defun yas--snapshot-overlay-location (overlay beg end)
+ "Like `yas--snapshot-marker-location' for overlays.
+The returned format is (OVERLAY (RE WS) (RE WS)). Either of
+the (RE WS) lists may be nil if the start or end, respectively,
+of the overlay is outside the range BEG .. END."
+ (let ((obeg (overlay-start overlay))
+ (oend (overlay-end overlay)))
+ (list overlay
+ (when (and (<= beg obeg) (< obeg end))
+ (cdr (yas--snapshot-marker-location obeg beg end)))
+ (when (and (<= beg oend) (< oend end))
+ (cdr (yas--snapshot-marker-location oend beg end))))))
+
+(defun yas--snapshot-overlay-line-location (overlay)
+ "Return info for restoring OVERLAY's line based location.
+The returned format is (OVERLAY (LINE RE WS) (LINE RE WS))."
+ (let ((loc-beg (progn (goto-char (overlay-start overlay))
+ (yas--snapshot-marker-location (point))))
+ (loc-end (progn (goto-char (overlay-end overlay))
+ (yas--snapshot-marker-location (point)))))
+ (setcar loc-beg (count-lines (point-min) (progn (goto-char (car loc-beg))
+ (line-beginning-position))))
+ (setcar loc-end (count-lines (point-min) (progn (goto-char (car loc-end))
+ (line-beginning-position))))
+ (list overlay loc-beg loc-end)))
+
+(defun yas--goto-saved-location (regexp ws-count)
+ "Move point to location saved by `yas--snapshot-marker-location'.
+Buffer must be narrowed to BEG..END used to create the snapshot info."
+ (goto-char (point-min))
+ (if (not (looking-at regexp))
+ (lwarn '(yasnippet re-marker) :warning
+ "Couldn't find: %S" regexp)
+ (goto-char (match-beginning 1))
+ (skip-chars-forward "[:space:]\n")
+ (skip-chars-backward "[:space:]\n" (- (point) ws-count))))
+
+(defun yas--restore-marker-location (re-marker)
+ "Restores marker based on info from `yas--snapshot-marker-location'.
+Buffer must be narrowed to BEG..END used to create the snapshot info."
+ (apply #'yas--goto-saved-location (cdr re-marker))
+ (set-marker (car re-marker) (point)))
+
+(defun yas--restore-overlay-location (ov-locations)
+ "Restores marker based on info from `yas--snapshot-marker-location'.
+Buffer must be narrowed to BEG..END used to create the snapshot info."
+ (cl-destructuring-bind (overlay loc-beg loc-end) ov-locations
+ (move-overlay overlay
+ (if (not loc-beg) (overlay-start overlay)
+ (apply #'yas--goto-saved-location loc-beg)
+ (point))
+ (if (not loc-end) (overlay-end overlay)
+ (apply #'yas--goto-saved-location loc-end)
+ (point)))))
+
+
+(defun yas--restore-overlay-line-location (ov-locations)
+ "Restores overlay based on info from `yas--snapshot-overlay-line-location'."
+ (save-restriction
+ (move-overlay (car ov-locations)
+ (save-excursion
+ (forward-line (car (nth 1 ov-locations)))
+ (narrow-to-region (line-beginning-position) (line-end-position))
+ (apply #'yas--goto-saved-location (cdr (nth 1 ov-locations)))
+ (point))
+ (save-excursion
+ (forward-line (car (nth 2 ov-locations)))
+ (narrow-to-region (line-beginning-position) (line-end-position))
+ (apply #'yas--goto-saved-location (cdr (nth 2 ov-locations)))
+ (point)))))
(defun yas--indent-region (from to snippet)
"Indent the lines between FROM and TO with `indent-according-to-mode'.
The SNIPPET's markers are preserved."
- ;;; 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
- ;; the first non-whitespace char at the line. After indentation
- ;; takes place we should be at the correct to restore them. All
- ;; other non-trouble-markers should have been *pushed* and don't
- ;; need special attention.
- (let* ((snippet-markers (yas--collect-snippet-markers snippet))
- (to (set-marker (make-marker) to)))
- (save-excursion
- (goto-char from)
- (save-restriction
- (widen)
- ;; Indent each non-empty line.
- (cl-loop if (/= (line-beginning-position) (line-end-position)) do
- (back-to-indentation)
- (let ((trouble-markers ; The markers at (point).
- (cl-remove (point) snippet-markers :test #'/=)))
+ (save-excursion
+ (yas--save-restriction-and-widen
+ (let* ((snippet-markers (yas--collect-snippet-markers snippet))
+ (to (set-marker (make-marker) to)))
+ (goto-char from)
+ (cl-loop for bol = (line-beginning-position)
+ for eol = (line-end-position)
+ if (or yas-also-indent-empty-lines
+ (/= bol eol))
+ do
+ ;; Indent each non-empty line.
+ (let ((remarkers nil))
+ (dolist (m snippet-markers)
+ (when (and (<= bol m) (<= m eol))
+ (push (yas--snapshot-marker-location m bol eol)
+ remarkers)))
(unwind-protect
- (indent-according-to-mode)
- (dolist (marker trouble-markers)
- (set-marker marker (point)))))
+ (progn (back-to-indentation)
+ (indent-according-to-mode))
+ (save-restriction
+ (narrow-to-region bol (line-end-position))
+ (mapc #'yas--restore-marker-location remarkers))))
while (and (zerop (forward-line 1))
(< (point) to)))))))
@@ -3951,30 +4403,31 @@ The SNIPPET's markers are preserved."
(setq yas--indent-markers nil))
;; Now do stuff for `fixed' and `auto'.
(save-excursion
+ ;; We need to be at end of line, so that `forward-line' will only
+ ;; report 0 if it actually moves over a newline.
+ (end-of-line)
(cond ((eq yas-indent-line 'fixed)
- (while (and (zerop (forward-line))
- (zerop (current-column)))
- (indent-to-column yas--indent-original-column)))
+ (when (= (forward-line 1) 0)
+ (let ((indent-line-function
+ (lambda ()
+ ;; We need to be at beginning of line in order to
+ ;; indent existing whitespace correctly.
+ (beginning-of-line)
+ (indent-to-column yas--indent-original-column))))
+ (yas--indent-region (line-beginning-position)
+ (point-max)
+ snippet))))
((eq yas-indent-line 'auto)
- (unless yas-also-auto-indent-first-line
- (forward-line 1))
- (yas--indent-region (line-beginning-position)
- (point-max)
- snippet)))))
+ (when (or yas-also-auto-indent-first-line
+ (= (forward-line 1) 0))
+ (yas--indent-region (line-beginning-position)
+ (point-max)
+ snippet))))))
(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)))
+ (yas--snippet-map-markers (lambda (m) (push m markers) m) snippet)
markers))
(defun yas--escape-string (escaped)
@@ -4009,60 +4462,60 @@ With optional string TEXT do it in string instead of the buffer."
changed-text))
(defun yas--save-backquotes ()
- "Save all the \"\\=`(lisp-expression)\\=`\"-style expressions
-with their evaluated value into `yas--backquote-markers-and-strings'."
- (let* ((yas--snippet-buffer (current-buffer))
+ "Save all \"\\=`(lisp-expression)\\=`\"-style expressions.
+Return a list of (MARKER . STRING) entires for each backquoted
+Lisp expression."
+ (let* ((saved-quotes nil)
+ (yas--snippet-buffer (current-buffer))
(yas--change-detected nil)
(detect-change (lambda (_beg _end)
(when (eq (current-buffer) yas--snippet-buffer)
(setq yas--change-detected t)))))
(while (re-search-forward yas--backquote-lisp-expression-regexp nil t)
(let ((current-string (match-string-no-properties 1)) transformed)
- (save-restriction (widen)
- (delete-region (match-beginning 0) (match-end 0)))
+ (yas--save-restriction-and-widen
+ (delete-region (match-beginning 0) (match-end 0)))
(let ((before-change-functions
(cons detect-change before-change-functions)))
- (setq transformed (yas--eval-lisp (yas--read-lisp
- (yas--restore-escapes
- current-string '(?`))))))
+ (setq transformed (yas--eval-for-string (yas--read-lisp
+ (yas--restore-escapes
+ current-string '(?`))))))
(goto-char (match-beginning 0))
(when transformed
(let ((marker (make-marker))
(before-change-functions (cdr before-change-functions)))
- (save-restriction
- (widen)
+ (yas--save-restriction-and-widen
(insert "Y") ;; quite horrendous, I love it :)
(set-marker marker (point))
(insert "Y"))
- (push (cons marker transformed) yas--backquote-markers-and-strings)))))
+ (push (cons marker transformed) saved-quotes)))))
(when yas--change-detected
(lwarn '(yasnippet backquote-change) :warning
"`%s' modified buffer in a backquote expression.
To hide this warning, add (yasnippet backquote-change) to `warning-suppress-types'."
(if yas--current-template
(yas--template-name yas--current-template)
- "Snippet")))))
-
-(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)
- (save-restriction
- (widen)
- (delete-char -1)
- (insert string)
- (delete-char 1))
- (set-marker marker nil)))))
+ "Snippet")))
+ saved-quotes))
+
+(defun yas--restore-backquotes (saved-quotes)
+ "Replace markers in SAVED-QUOTES with their values.
+SAVED-QUOTES is the in format returned by `yas--save-backquotes'."
+ (cl-loop for (marker . string) in saved-quotes do
+ (save-excursion
+ (goto-char marker)
+ (yas--save-restriction-and-widen
+ (delete-char -1)
+ (insert string)
+ (delete-char 1))
+ (set-marker marker nil))))
(defun yas--scan-sexps (from count)
(ignore-errors
(save-match-data ; `scan-sexps' may modify match data.
(with-syntax-table (standard-syntax-table)
- (scan-sexps from count)))))
+ (let ((parse-sexp-lookup-properties nil))
+ (scan-sexps from count))))))
(defun yas--make-marker (pos)
"Create a marker at POS with nil `marker-insertion-type'."
@@ -4174,10 +4627,10 @@ When multiple expressions are found, only the last one counts."
(when brand-new-mirror
(push brand-new-mirror
(yas--field-mirrors field))
- (yas--calculate-mirrors-in-fields snippet brand-new-mirror)
+ (yas--calculate-simple-fom-parentage snippet brand-new-mirror)
(push (cons (match-beginning 0) real-match-end-0) yas--dollar-regions)))))
-(defun yas--simple-mirror-parse-create (snippet)
+(defun yas--simple-fom-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))))
@@ -4187,20 +4640,22 @@ When multiple expressions are found, only the last one counts."
(push (cons (match-beginning 0) (yas--exit-marker (yas--snippet-exit snippet)))
yas--dollar-regions))
(t
- (let ((field (yas--snippet-find-field snippet number)))
+ (let ((field (yas--snippet-find-field snippet number))
+ (fom))
(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
+ (setq fom (yas--make-mirror
+ (yas--make-marker (match-beginning 0))
+ (yas--make-marker (match-beginning 0))
+ nil))
+ (yas--field-mirrors field))
+ (push
+ (setq fom (yas--make-field number
+ (yas--make-marker (match-beginning 0))
+ (yas--make-marker (match-beginning 0))
+ nil))
+ (yas--snippet-fields snippet)))
+ (yas--calculate-simple-fom-parentage snippet fom))
(push (cons (match-beginning 0) (match-end 0))
yas--dollar-regions))))))
@@ -4230,49 +4685,46 @@ When multiple expressions are found, only the last one counts."
(defun yas--update-mirrors (snippet)
"Update all the mirrors of SNIPPET."
- (save-restriction
- (widen)
+ (yas--save-restriction-and-widen
(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
- ;;
- (cl-mapcan #'(lambda (field)
- (mapcar #'(lambda (mirror)
- (cons field mirror))
- (cl-sort
- (cl-copy-list
- (yas--field-mirrors field))
- #'<
- :key #'yas--mirror-start)))
- (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 snippet)
- ;; `yas--place-overlays' is needed since the active field and
- ;; protected overlays might have been changed because of insertions
- ;; in `yas--mirror-update-display'.
- (let ((active-field (yas--snippet-active-field snippet)))
- (when active-field (yas--place-overlays snippet active-field))))))))
-
-(defun yas--mirror-update-display (mirror field snippet)
+ (cl-loop
+ for (field . mirror)
+ in (cl-sort
+ ;; Make a list of (FIELD . MIRROR).
+ (cl-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 also handles the case where a mirror in
+ ;; a field causes another mirror to need reupdating.
+ #'> :key (lambda (fm) (yas--calculate-mirror-depth (cdr fm))))
+ ;; Before updating a mirror with a parent-field, maybe advance
+ ;; its start (#290).
+ do (let ((parent-field (yas--mirror-parent-field mirror)))
+ (when parent-field
+ (yas--advance-start-maybe mirror (yas--fom-start parent-field))))
+ ;; Update this mirror.
+ do (yas--mirror-update-display mirror field)
+ ;; Delay indenting until we're done all mirrors. We must do
+ ;; this to avoid losing whitespace between fields that are
+ ;; still empty (i.e., they will be non-empty after updating).
+ when (eq yas-indent-line 'auto)
+ collect (cons (yas--mirror-start mirror) (yas--mirror-end mirror))
+ into indent-regions
+ ;; `yas--place-overlays' is needed since the active field and
+ ;; protected overlays might have been changed because of insertions
+ ;; in `yas--mirror-update-display'.
+ do (let ((active-field (yas--snippet-active-field snippet)))
+ (when active-field (yas--place-overlays snippet active-field)))
+ finally do
+ (let ((yas--inhibit-overlay-hooks t))
+ (cl-loop for (beg . end) in (cl-sort indent-regions #'< :key #'car)
+ do (yas--indent-region beg end snippet)))))))
+
+(defun yas--mirror-update-display (mirror field)
"Update MIRROR according to FIELD (and mirror transform)."
(let* ((mirror-parent-field (yas--mirror-parent-field mirror))
@@ -4291,11 +4743,7 @@ When multiple expressions are found, only the last one counts."
(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)))
- (let ((yas--inhibit-overlay-hooks t))
- (yas--indent-region (yas--mirror-start mirror)
- (yas--mirror-end mirror)
- snippet)))))
+ (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."
@@ -4320,26 +4768,41 @@ When multiple expressions are found, only the last one counts."
;;
(defun yas--post-command-handler ()
"Handles various yasnippet conditions after each command."
- (cond ((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
- (cl-find-if-not
- (lambda (field)
- (yas--field-probably-deleted-p snippet field))
- (remq 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))))
+ (when (and yas--watch-auto-fill-backtrace
+ (fboundp 'backtrace--print-frame)
+ (null yas--original-auto-fill-function)
+ (eq auto-fill-function 'yas--auto-fill))
+ (lwarn '(yasnippet auto-fill bug) :error
+ "`yas--original-auto-fill-function' unexpectedly nil! Please report this backtrace\n%S"
+ (with-output-to-string
+ (mapc #'backtrace--print-frame
+ yas--watch-auto-fill-backtrace)))
+ ;; Don't pop up more than once in a session (still log though).
+ (defvar warning-suppress-types) ; `warnings' is autoloaded by `lwarn'.
+ (add-to-list 'warning-suppress-types '(yasnippet auto-fill bug)))
+ (condition-case err
+ (progn (yas--finish-moving-snippets)
+ (cond ((eq 'undo this-command)
+ ;;
+ ;; After undo revival the correct field is sometimes not
+ ;; restored correctly, this condition handles that
+ ;;
+ (let* ((snippet (car (yas-active-snippets)))
+ (target-field
+ (and snippet
+ (cl-find-if-not
+ (lambda (field)
+ (yas--field-probably-deleted-p snippet field))
+ (remq 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))))
+ ((debug error) (signal (car err) (cdr err)))))
;;; Fancy docs:
;;
@@ -4426,7 +4889,7 @@ object satisfying `yas--field-p' to restrict the expansion to.")))
"Log level for `yas--message' 4 means trace most anything, 0 means nothing.")
(defun yas--message (level message &rest args)
- "When LEVEL is at or below `yas-verbosity-level', log MESSAGE and ARGS."
+ "When LEVEL is at or below `yas-verbosity', log MESSAGE and ARGS."
(when (>= yas-verbosity level)
(message "%s" (apply #'yas--format message args))))
@@ -4494,6 +4957,31 @@ and return the directory. Return nil if not found."
(directory-file-name file))))
(setq file nil))))
root))))
+
+;;; Unloading
+
+(defvar unload-function-defs-list) ; loadhist.el
+
+(defun yasnippet-unload-function ()
+ "Disable minor modes when calling `unload-feature'."
+ ;; Disable `yas-minor-mode' everywhere it's enabled.
+ (yas-global-mode -1)
+ (save-current-buffer
+ (dolist (buffer (buffer-list))
+ (set-buffer buffer)
+ (when yas-minor-mode
+ (yas-minor-mode -1))))
+ ;; Remove symbol properties of all our functions, this avoids
+ ;; Bug#25088 in Emacs 25.1, where the compiler macro on
+ ;; `cl-defstruct' created functions hang around in the symbol plist
+ ;; and cause errors when loading again (we don't *need* to clean
+ ;; *all* symbol plists, but it's easier than being precise).
+ (dolist (def unload-function-defs-list)
+ (when (eq (car-safe def) 'defun)
+ (setplist (cdr def) nil)))
+ ;; Return nil so that `unload-feature' will take of undefining
+ ;; functions, and changing any buffers using `snippet-mode'.
+ nil)
;;; Backward compatibility to yasnippet <= 0.7