changeset 151:34d8d00044fc

web-mode: upgrade to version 14
author Jordi Gutiérrez Hermoso <jordigh@octave.org>
date Thu, 13 Oct 2016 16:15:00 -0400
parents 4fa988afce81
children 55ceabc58fcc
files elpa/web-mode-14/web-mode-autoloads.el elpa/web-mode-14/web-mode-pkg.el elpa/web-mode-14/web-mode.el elpa/web-mode-8.0.4/web-mode-autoloads.el elpa/web-mode-8.0.4/web-mode-pkg.el elpa/web-mode-8.0.4/web-mode.el
diffstat 3 files changed, 9386 insertions(+), 6720 deletions(-) [+]
line wrap: on
line diff
rename from elpa/web-mode-8.0.4/web-mode-autoloads.el
rename to elpa/web-mode-14/web-mode-autoloads.el
--- a/elpa/web-mode-8.0.4/web-mode-autoloads.el
+++ b/elpa/web-mode-14/web-mode-autoloads.el
@@ -3,12 +3,12 @@
 ;;; Code:
 (add-to-list 'load-path (or (file-name-directory #$) (car load-path)))
 
-;;;### (autoloads nil "web-mode" "web-mode.el" (21984 33172 489521
-;;;;;;  727000))
+;;;### (autoloads nil "web-mode" "web-mode.el" (22527 59143 509095
+;;;;;;  721000))
 ;;; Generated autoloads from web-mode.el
 
 (autoload 'web-mode "web-mode" "\
-Major mode for editing web templates (HTML documents with embedded parts and blocks).
+Major mode for editing web templates.
 
 \(fn)" t nil)
 
rename from elpa/web-mode-8.0.4/web-mode-pkg.el
rename to elpa/web-mode-14/web-mode-pkg.el
--- a/elpa/web-mode-8.0.4/web-mode-pkg.el
+++ b/elpa/web-mode-14/web-mode-pkg.el
@@ -1,1 +1,1 @@
-(define-package "web-mode" "8.0.4" "major mode for editing html templates" 'nil)
+(define-package "web-mode" "14" "major mode for editing web templates" 'nil :url "http://web-mode.org" :keywords '("languages"))
rename from elpa/web-mode-8.0.4/web-mode.el
rename to elpa/web-mode-14/web-mode.el
--- a/elpa/web-mode-8.0.4/web-mode.el
+++ b/elpa/web-mode-14/web-mode.el
@@ -1,74 +1,34 @@
-;;; web-mode.el --- major mode for editing html templates
+;;; web-mode.el --- major mode for editing web templates
 ;;; -*- coding: utf-8 -*-
 
-;; Copyright 2011-2014 François-Xavier Bois
-
-;; Version: 8.0.4
-;; Author: François-Xavier Bois <fxbois AT Google Mail Service>
-;; Maintainer: François-Xavier Bois
-;; Created: July 2011
-;; Keywords: html template php javascript js css web
-;;           django jsp asp erb twig jinja blade dust closure
-;;           freemarker mustache velocity cheetah smarty
+;; Copyright 2011-2016 François-Xavier Bois
+
+;; Version: 14.0.0
+;; Package-Version: 14
+;; Author: François-Xavier Bois <fxbois AT Google Mail Service>
+;; Maintainer: François-Xavier Bois
 ;; URL: http://web-mode.org
 ;; Repository: http://github.com/fxbois/web-mode
-
-;; =========================================================================
-;; This work is sponsored by Kernix : Digital Agency (Web & Mobile) in Paris
-;; =========================================================================
-
-;; This file is not part of Emacs
-
-;; This file is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 2, or (at your option)
-;; any later version.
-
-;; This file is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING.  If not, write to
-;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-;; Boston, MA 02111-1307, USA.
-
-;; Code goes here
-
-;;new lexer/highlighter : web-mode.el is now compatible with minor modes relying on font-locking
-;;compatibility with  *.js.erb (javascript content type), *.css.erb (css content type)
-;;engine compatibility : web2py (python), mako (python), mason (perl)
-;;jshint compatibility : web-mode-jshint
-;;alertnative delimiters can be defined for smarty (see web-mode-engines-alternate-delimiters)
-;;delimiters highlighting is more robust (same loop than string/comment highlighting)
-
-;;todo :
-;;       essayer de réduire la zone à scanner / repeindre
-;;       phphint
-;;       navigation blocs mako
-;;       tester django-extra comments
-;;       test whitespaces
-;;       colorer : <a href=" >
-;;       bug issue169.js.erb
-
-;;todo : Stickiness of Text Properties
-;;todo : web-mode-engine-real-name
-;;todo : finir filling
-;;todo : screenshot : http://www.cockos.com/licecap/
-;;todo : better default colors for tags & attrs
-;;todo : passer les content-types en symboles
-;;todo : tester shortcut A -> pour pomme
-;;todo : commentaire d'une ligne ruby ou d'une ligne asp
-;;todo : créer tag-token pour différentier de part-token : tag-token=attr,comment ???
-
-(defconst web-mode-version "8.0.4"
+;; Created: July 2011
+;; Keywords: languages
+;; License: GNU General Public License >= 2
+;; Distribution: This file is not part of Emacs
+
+;;==============================================================================
+;;WEB-MODE is sponsored by ** Kernix ** Best Digital Factory & Data Lab in Paris
+;;==============================================================================
+
+;;; Code:
+
+;;---- CONSTS ------------------------------------------------------------------
+
+(defconst web-mode-version "14.0.0"
   "Web Mode version.")
 
+;;---- GROUPS ------------------------------------------------------------------
+
 (defgroup web-mode nil
-  "Major mode for editing web templates:
-   HTML files embedding parts (CSS/JavaScript)
-   and blocks (php, erb, django/twig, smarty, jsp, asp, etc.)"
+  "Major mode for editing web templates"
   :group 'languages
   :prefix "web-"
   :link '(url-link :tag "Site" "http://web-mode.org")
@@ -79,6 +39,8 @@
   :group 'web-mode
   :group 'faces)
 
+;;---- CUSTOMS -----------------------------------------------------------------
+
 (defcustom web-mode-script-padding 1
   "Script element left padding."
   :type 'integer
@@ -90,42 +52,86 @@
   :group 'web-mode)
 
 (defcustom web-mode-block-padding 0
-  "Multi-line block (PHP, Ruby, Java, etc.) left padding."
-  :type 'integer
-  :group 'web-mode)
-
-(defcustom web-mode-markup-indent-offset 2
-  "HTML indentation level."
-  :type 'integer
-  :group 'web-mode)
-
-(defcustom web-mode-css-indent-offset 2
-  "CSS indentation level."
-  :type 'integer
-  :group 'web-mode)
-
-(defcustom web-mode-code-indent-offset 2
-  "Code (JavaScript, PHP, etc.) indentation level."
+  "Multi-line block (php, ruby, java, python, asp, etc.) left padding."
   :type 'integer
   :group 'web-mode)
 
-(defcustom web-mode-disable-css-colorization (not (display-graphic-p))
-  "In a CSS block, do not set background according to the color: #xxx, rgb(x,x,x)."
-  :type 'boolean
+(defcustom web-mode-attr-indent-offset nil
+  "Html attribute indentation level."
+  :type 'integer
+  :safe #'integerp
+  :group 'web-mode)
+
+(defcustom web-mode-attr-value-indent-offset nil
+  "Html attribute value indentation level."
+  :type 'integer
+  :safe #'integerp
+  :group 'web-mode)
+
+(defcustom web-mode-markup-indent-offset
+  (if (and (boundp 'standard-indent) standard-indent) standard-indent 2)
+  "Html indentation level."
+  :type 'integer
+  :safe #'integerp
   :group 'web-mode)
 
-(defcustom web-mode-disable-auto-indentation (not (display-graphic-p))
-  "Disable auto-indentation."
+(defcustom web-mode-css-indent-offset
+  (if (and (boundp 'standard-indent) standard-indent) standard-indent 2)
+  "CSS indentation level."
+  :type 'integer
+  :safe #'integerp
+  :group 'web-mode)
+
+(defcustom web-mode-code-indent-offset
+  (if (and (boundp 'standard-indent) standard-indent) standard-indent 2)
+  "Code (javascript, php, etc.) indentation level."
+  :type 'integer
+  :safe #'integerp
+  :group 'web-mode)
+
+(defcustom web-mode-sql-indent-offset 4
+  "Sql (inside strings) indentation level."
+  :type 'integer
+  :safe #'integerp
+  :group 'web-mode)
+
+(defcustom web-mode-enable-css-colorization (display-graphic-p)
+  "In a CSS part, set background according to the color: #xxx, rgb(x,x,x)."
   :type 'boolean
   :group 'web-mode)
 
-(defcustom web-mode-disable-auto-pairing (not (display-graphic-p))
-  "Disable auto-pairing."
+(defcustom web-mode-enable-auto-indentation (display-graphic-p)
+  "Auto-indentation."
+  :type 'boolean
+  :group 'web-mode)
+
+(defcustom web-mode-enable-auto-closing (display-graphic-p)
+  "Auto-closing."
+  :type 'boolean
+  :group 'web-mode)
+
+(defcustom web-mode-enable-auto-pairing (display-graphic-p)
+  "Auto-pairing."
   :type 'boolean
   :group 'web-mode)
 
-(defcustom web-mode-disable-auto-opening (not (display-graphic-p))
-  "Disable html element auto-opening."
+(defcustom web-mode-enable-auto-opening (display-graphic-p)
+  "Html element auto-opening."
+  :type 'boolean
+  :group 'web-mode)
+
+(defcustom web-mode-enable-auto-quoting (display-graphic-p)
+  "Add double quotes after the character = in a tag."
+  :type 'boolean
+  :group 'web-mode)
+
+(defcustom web-mode-enable-auto-expanding nil
+  "e.g. s/ expands to <span>|</span>."
+  :type 'boolean
+  :group 'web-mode)
+
+(defcustom web-mode-enable-control-block-indentation t
+  "Control blocks increase indentation."
   :type 'boolean
   :group 'web-mode)
 
@@ -134,11 +140,21 @@
   :type 'boolean
   :group 'web-mode)
 
-(defcustom web-mode-enable-whitespaces nil
+(defcustom web-mode-enable-current-column-highlight nil
+  "Show column for current element."
+  :type 'boolean
+  :group 'web-mode)
+
+(defcustom web-mode-enable-whitespace-fontification nil
   "Enable whitespaces."
   :type 'boolean
   :group 'web-mode)
 
+(defcustom web-mode-enable-html-entities-fontification nil
+  "Enable html entities fontification."
+  :type 'boolean
+  :group 'web-mode)
+
 (defcustom web-mode-enable-block-face nil
   "Enable block face (useful for setting a background for example).
 See web-mode-block-face."
@@ -146,24 +162,54 @@
   :group 'web-mode)
 
 (defcustom web-mode-enable-part-face nil
-  "Enable part face (useful for setting a background for example).
-See web-mode-part-face."
+  "Enable part face (useful for setting background of <style> or <script>
+ elements for example). See web-mode-part-face."
+  :type 'boolean
+  :group 'web-mode)
+
+(defcustom web-mode-enable-inlays nil
+  "Enable inlays (e.g. LaTeX) highlighting."
   :type 'boolean
   :group 'web-mode)
 
+(defcustom web-mode-enable-sexp-functions t
+  "Enable specific sexp functions."
+  :type 'boolean
+  :group 'web-mode)
+
+(defcustom web-mode-enable-comment-interpolation t
+  "Enable highlight of keywords like FIXME, TODO, etc. in comments."
+  :type 'list
+  :group 'web-mode)
+
 (defcustom web-mode-enable-string-interpolation t
   "Enable string interpolation fontification (php and erb)."
   :type 'boolean
   :group 'web-mode)
 
+(defcustom web-mode-enable-sql-detection nil
+  "Enable fontification and indentation of sql queries in strings."
+  :type 'boolean
+  :group 'web-mode)
+
 (defcustom web-mode-enable-heredoc-fontification t
-  "Enable heredoc fontification. The identifier should contain JS, JAVASCRIPT or HTML."
+  "Enable heredoc fontification. The identifier should contain JS, JAVASCRIPT, CSS or HTML."
   :type 'boolean
   :group 'web-mode)
 
-(defcustom web-mode-enable-comment-keywords nil
-  "Enable highlight of keywords like FIXME, TODO, etc. in comments."
-  :type 'list
+(defcustom web-mode-enable-element-content-fontification nil
+  "Enable element content fontification. The content of an element can have a face associated."
+  :type 'boolean
+  :group 'web-mode)
+
+(defcustom web-mode-enable-element-tag-fontification nil
+  "Enable tag name fontification."
+  :type 'boolean
+  :group 'web-mode)
+
+(defcustom web-mode-enable-engine-detection nil
+  "Detect such directive -*- engine: ENGINE -*- at the top of the file."
+  :type 'boolean
   :group 'web-mode)
 
 (defcustom web-mode-comment-style 1
@@ -173,17 +219,20 @@
                  (const :tag "force engine comments" 2)))
 
 (defcustom web-mode-indent-style 2
-  "Indentation style.
-with value 2, HTML lines beginning text are also indented (do not forget side effects, e.g. content of a textarea)."
-  :type 'integer
-  :group 'web-mode)
-
-(defcustom web-mode-tag-auto-close-style 1
-  "Tag auto-close style:
-0=no auto-closing
-1=auto-close with </
-2=auto-close with > and </."
-  :type 'integer
+  "Indentation style."
+  :group 'web-mode
+  :type '(choice (const :tag "default (all lines are indented)" 2)
+                 (const :tag "text at the beginning of line is not indented" 1)))
+
+(defcustom web-mode-auto-close-style 1
+  "Auto-close style."
+  :group 'web-mode
+  :type '(choice (const :tag "Auto-close on </" 1)
+                 (const :tag "Auto-close on > and </" 2)))
+
+(defcustom web-mode-extra-expanders '()
+  "A list of additional expanders."
+  :type 'list
   :group 'web-mode)
 
 (defcustom web-mode-extra-auto-pairs '()
@@ -196,75 +245,41 @@
   :type 'list
   :group 'web-mode)
 
-(defcustom web-mode-extra-python-constants '()
-  "A list of additional strings to treat as Python constants."
-  :type 'list
-  :group 'web-mode)
-
-(defcustom web-mode-extra-php-constants '()
-  "A list of additional strings to treat as PHP constants."
-  :type 'list
-  :group 'web-mode)
-
-(defcustom web-mode-extra-php-keywords '()
-  "A list of additional strings to treat as PHP keywords."
+(defcustom web-mode-extra-builtins '()
+  "A list of additional builtins."
   :type 'list
   :group 'web-mode)
 
-(defcustom web-mode-extra-jsp-keywords '()
-  "A list of additional strings to treat as JSP keywords."
-  :type 'list
-  :group 'web-mode)
-
-(defcustom web-mode-extra-python-keywords '()
-  "A list of additional strings to treat as Python keywords."
+(defcustom web-mode-extra-constants '()
+  "A list of additional constants."
   :type 'list
   :group 'web-mode)
 
-(defcustom web-mode-extra-erb-keywords '()
-  "A list of additional strings to treat as ERB keywords."
-  :type 'list
-  :group 'web-mode)
-
-(defcustom web-mode-extra-mason-keywords '()
-  "A list of additional strings to treat as Mason keywords."
+(defcustom web-mode-extra-keywords '()
+  "A list of additional keywords."
   :type 'list
   :group 'web-mode)
 
-(defcustom web-mode-extra-asp-constants '()
-  "A list of additional strings to treat as ASP constants."
+(defcustom web-mode-extra-types '()
+  "A list of additional types."
   :type 'list
   :group 'web-mode)
 
-(defcustom web-mode-extra-asp-keywords '()
-  "A list of additional strings to treat as ASP keywords."
-  :type 'list
-  :group 'web-mode)
-
-(defcustom web-mode-extra-asp-types '()
-  "A list of additional strings to treat as ASP types."
+(defcustom web-mode-tests-directory (concat default-directory "tests/")
+  "Directory containing all the unit tests."
   :type 'list
   :group 'web-mode)
 
-(defcustom web-mode-extra-aspx-keywords '()
-  "A list of additional strings to treat as ASPX keywords."
-  :type 'list
-  :group 'web-mode)
-
-(defcustom web-mode-extra-javascript-keywords '()
-  "A list of additional strings to treat as JS keywords."
+(defcustom web-mode-jsx-depth-faces nil
+;;  '(web-mode-jsx-depth-1-face
+;;    web-mode-jsx-depth-2-face
+;;    web-mode-jsx-depth-3-face
+;;    web-mode-jsx-depth-4-face)
+  "Each jsx depth has is own face."
   :type 'list
   :group 'web-mode)
 
-(defcustom web-mode-extra-razor-keywords '()
-  "A list of additional strings to treat as Razor keywords."
-  :type 'list
-  :group 'web-mode)
-
-(defcustom web-mode-extra-comment-keywords '()
-  "A list of additional strings to treat as comment keywords."
-  :type 'list
-  :group 'web-mode)
+;;---- FACES -------------------------------------------------------------------
 
 (defface web-mode-error-face
   '((t :background "red"))
@@ -303,46 +318,50 @@
 
 (defface web-mode-doctype-face
   '((t :foreground "Grey"))
-  "Face for HTML doctype."
+  "Face for html doctype."
   :group 'web-mode-faces)
 
 (defface web-mode-html-tag-face
-  '((((class color) (min-colors 88) (background dark)) :foreground "Snow4")
-    (((class color) (min-colors 88) (background light)) :foreground "grey15")
-    (((class color) (min-colors 16) (background dark)) :foreground "Snow4")
-    (((class color) (min-colors 16) (background light)) :foreground "grey15")
-    (((class color) (min-colors 8)) :foreground "Snow4")
-    (((type tty) (class mono)) :inverse-video t)
-    (t :foreground "Snow4"))
-  "Face for HTML tags."
+  '((((class color) (min-colors 88) (background dark))  :foreground "Snow4")
+    (((class color) (min-colors 88) (background light)) :foreground "Snow4")
+    (((class color) (min-colors 16) (background dark))  :foreground "Snow4")
+    (((class color) (min-colors 16) (background light)) :foreground "Grey15")
+    (((class color) (min-colors 8))                     :foreground "Snow4")
+    (((type tty) (class mono))                          :inverse-video t)
+    (t                                                  :foreground "Snow4"))
+  "Face for html tags."
   :group 'web-mode-faces)
 
 (defface web-mode-html-tag-custom-face
   '((t :inherit web-mode-html-tag-face))
-  "Face for HTML custom tags (e.g. <polymer-element>)."
+  "Face for html custom tags (e.g. <polymer-element>)."
+  :group 'web-mode-faces)
+
+(defface web-mode-html-tag-namespaced-face
+  '((t :inherit web-mode-block-control-face))
+  "Face for html namespaced tags (e.g. <c:forEach>)."
   :group 'web-mode-faces)
 
 (defface web-mode-html-tag-bracket-face
-;;  '((t :inherit web-mode-html-tag-face))
-  '((((class color) (min-colors 88) (background dark)) :foreground "Snow3")
-    (((class color) (min-colors 88) (background light)) :foreground "grey14")
-    (((class color) (min-colors 16) (background dark)) :foreground "Snow3")
-    (((class color) (min-colors 16) (background light)) :foreground "grey14")
-    (((class color) (min-colors 8)) :foreground "Snow3")
-    (((type tty) (class mono)) :inverse-video t)
-    (t :foreground "Snow3"))
-  "Face for HTML tags angle brackets (< and >)."
+  '((((class color) (min-colors 88) (background dark))  :foreground "Snow3")
+    (((class color) (min-colors 88) (background light)) :foreground "Grey14")
+    (((class color) (min-colors 16) (background dark))  :foreground "Snow3")
+    (((class color) (min-colors 16) (background light)) :foreground "Grey14")
+    (((class color) (min-colors 8))                     :foreground "Snow3")
+    (((type tty) (class mono))                          :inverse-video t)
+    (t                                                  :foreground "Snow3"))
+  "Face for html tags angle brackets (<, > and />)."
   :group 'web-mode-faces)
 
 (defface web-mode-html-attr-name-face
-  '((((class color) (min-colors 88) (background dark)) :foreground "Snow3")
-    (((class color) (min-colors 88) (background light)) :foreground "grey13")
-    (((class color) (min-colors 16) (background dark)) :foreground "Snow3")
-    (((class color) (min-colors 16) (background light)) :foreground "grey13")
-    (((class color) (min-colors 8)) :foreground "Snow3")
-    (((type tty) (class mono)) :inverse-video t)
-    (t :foreground "Snow4"))
-  "Face for HTML attribute names."
+  '((((class color) (min-colors 88) (background dark))  :foreground "Snow3")
+    (((class color) (min-colors 88) (background light)) :foreground "Snow4")
+    (((class color) (min-colors 16) (background dark))  :foreground "Snow3")
+    (((class color) (min-colors 16) (background light)) :foreground "Grey13")
+    (((class color) (min-colors 8))                     :foreground "Snow3")
+    (((type tty) (class mono))                          :inverse-video t)
+    (t                                                  :foreground "Snow4"))
+  "Face for html attribute names."
   :group 'web-mode-faces)
 
 (defface web-mode-html-attr-custom-face
@@ -350,6 +369,11 @@
   "Face for custom attribute names (e.g. data-*)."
   :group 'web-mode-faces)
 
+(defface web-mode-html-attr-engine-face
+  '((t :inherit web-mode-block-delimiter-face))
+  "Face for custom engine attribute names (e.g. ng-*)."
+  :group 'web-mode-faces)
+
 (defface web-mode-html-attr-equal-face
   '((t :inherit web-mode-html-attr-name-face))
   "Face for the = character between name and value."
@@ -357,19 +381,24 @@
 
 (defface web-mode-html-attr-value-face
   '((t :inherit font-lock-string-face))
-  "Face for HTML attribute values."
+  "Face for html attribute values."
   :group 'web-mode-faces)
 
 (defface web-mode-block-attr-name-face
-  '((t :foreground "#8fbc8f")) ;; inherit web-mode-html-attr-name-face))
+  '((t :foreground "#8fbc8f"))
   "Face for block attribute names."
   :group 'web-mode-faces)
 
 (defface web-mode-block-attr-value-face
-  '((t :inherit web-mode-html-attr-value-face))
+  '((t :foreground "#5f9ea0"))
   "Face for block attribute values."
   :group 'web-mode-faces)
 
+(defface web-mode-variable-name-face
+  '((t :inherit font-lock-variable-name-face))
+  "Face for variable names."
+  :group 'web-mode-faces)
+
 (defface web-mode-css-selector-face
   '((t :inherit font-lock-keyword-face))
   "Face for CSS rules."
@@ -405,9 +434,9 @@
   "Face for CSS functions."
   :group 'web-mode-faces)
 
-(defface web-mode-variable-name-face
-  '((t :inherit font-lock-variable-name-face))
-  "Face for variable names."
+(defface web-mode-css-variable-face
+  '((t :inherit web-mode-variable-name-face :slant italic))
+  "Face for CSS vars."
   :group 'web-mode-faces)
 
 (defface web-mode-function-name-face
@@ -415,6 +444,11 @@
   "Face for function names."
   :group 'web-mode-faces)
 
+(defface web-mode-filter-face
+  '((t :inherit font-lock-function-name-face))
+  "Face for function names."
+  :group 'web-mode-faces)
+
 (defface web-mode-function-call-face
   '((t :inherit font-lock-function-name-face))
   "Face for function calls."
@@ -515,20 +549,25 @@
   "Face for whitespaces."
   :group 'web-mode-faces)
 
+(defface web-mode-inlay-face
+  '((((class color) (min-colors 88) (background dark))  :background "Black")
+    (((class color) (min-colors 88) (background light)) :background "LightYellow1")
+    (((class color) (min-colors 16) (background dark))  :background "Brey18")
+    (((class color) (min-colors 16) (background light)) :background "LightYellow1")
+    (((class color) (min-colors 8))                     :background "Black")
+    (((type tty) (class mono))                          :inverse-video t)
+    (t                                                  :background "Grey"))
+  "Face for inlays. Must be used in conjunction with web-mode-enable-inlays."
+  :group 'web-mode-faces)
+
 (defface web-mode-block-face
-  '((((class color) (min-colors 88) (background dark))
-     :background "black") ;""grey18")
-    (((class color) (min-colors 88) (background light))
-     :background "LightYellow1")
-    (((class color) (min-colors 16) (background dark))
-     :background "grey18")
-    (((class color) (min-colors 16) (background light))
-     :background "LightYellow1")
-    (((class color) (min-colors 8))
-     :background "Black")
-    (((type tty) (class mono))
-     :inverse-video t)
-    (t :background "grey"))
+  '((((class color) (min-colors 88) (background dark))  :background "Black")
+    (((class color) (min-colors 88) (background light)) :background "LightYellow1")
+    (((class color) (min-colors 16) (background dark))  :background "Grey18")
+    (((class color) (min-colors 16) (background light)) :background "LightYellow1")
+    (((class color) (min-colors 8))                     :background "Black")
+    (((type tty) (class mono))                          :inverse-video t)
+    (t                                                  :background "Grey"))
   "Face for blocks (useful for setting a background for example).
 Must be used in conjunction with web-mode-enable-block-face."
   :group 'web-mode-faces)
@@ -538,136 +577,267 @@
   "Face for parts."
   :group 'web-mode-faces)
 
+(defface web-mode-script-face
+  '((t :inherit web-mode-part-face))
+  "Face for javascript inside a script element."
+  :group 'web-mode-faces)
+
+(defface web-mode-style-face
+  '((t :inherit web-mode-part-face))
+  "Face for css inside a style element."
+  :group 'web-mode-faces)
+
 (defface web-mode-folded-face
   '((t :underline t))
   "Overlay face for folded."
   :group 'web-mode-faces)
 
+(defface web-mode-bold-face
+  '((t :weight bold))
+  "bold face."
+  :group 'web-mode-faces)
+
+(defface web-mode-italic-face
+  '((t :slant italic))
+  "bold face."
+  :group 'web-mode-faces)
+
+(defface web-mode-underline-face
+  '((t :underline t))
+  "bold face."
+  :group 'web-mode-faces)
+
 (defface web-mode-current-element-highlight-face
   '((t :background "#000000"))
   "Overlay face for element highlight."
   :group 'web-mode-faces)
 
+(defface web-mode-current-column-highlight-face
+  '((t :background "#3e3c36"))
+  "Overlay face for current column."
+  :group 'web-mode-faces)
+
 (defface web-mode-comment-keyword-face
   '((t :weight bold :box t))
   "Comment keywords."
   :group 'web-mode-faces)
 
+(defface web-mode-sql-keyword-face
+  '((t :weight bold :slant italic))
+  "Sql keywords."
+  :group 'web-mode-faces)
+
+(defface web-mode-html-entity-face
+  '((t :slant italic))
+  "Face html entities (e.g. &#8211;, &eacute;)."
+  :group 'web-mode-faces)
+
+(defface web-mode-jsx-depth-1-face
+  '((t :background "#333333"))
+  "jsx depth 1"
+  :group 'web-mode-faces)
+
+(defface web-mode-jsx-depth-2-face
+  '((t :background "#222222"))
+  "jsx"
+  :group 'web-mode-faces)
+
+(defface web-mode-jsx-depth-3-face
+  '((t :background "#111111"))
+  "jsx"
+  :group 'web-mode-faces)
+
+(defface web-mode-jsx-depth-4-face
+  '((t :background "#000000"))
+  "jsx"
+  :group 'web-mode-faces)
+
+;;---- VARS --------------------------------------------------------------------
+
+(defvar font-lock-beg)
+(defvar font-lock-end)
+
+(defvar web-mode-auto-pairs nil)
+(defvar web-mode-block-regexp nil)
+(defvar web-mode-change-beg nil)
+(defvar web-mode-change-end nil)
+(defvar web-mode-chunk-length 64)
+(defvar web-mode-column-overlays nil)
+(defvar web-mode-comments-invisible nil)
+(defvar web-mode-content-type "")
+(defvar web-mode-engine nil)
+(defvar web-mode-engine-attr-regexp nil)
+(defvar web-mode-engine-font-lock-keywords nil)
+(defvar web-mode-engine-token-regexp nil)
+(defvar web-mode-expand-initial-pos nil)
+(defvar web-mode-expand-initial-scroll nil)
+(defvar web-mode-expand-previous-state "")
+(defvar web-mode-font-lock-keywords '(web-mode-font-lock-highlight))
+(defvar web-mode-inhibit-fontification nil)
+(defvar web-mode-inlay-regexp nil)
+(defvar web-mode-is-scratch nil)
+(defvar web-mode-jshint-errors 0)
+(defvar web-mode-minor-engine nil)
+(defvar web-mode-obarray nil)
+(defvar web-mode-overlay-tag-start nil)
+(defvar web-mode-overlay-tag-end nil)
+(defvar web-mode-snippets nil)
+(defvar web-mode-time (current-time))
+
+(defvar web-mode-indentless-elements
+  '("code" "pre" "textarea"))
+
+(defvar web-mode-indentless-attributes
+  '("onclick" "onmouseover" "onmouseout" "onsubmit"))
+
 (defvar web-mode-void-elements
   '("area" "base" "br" "col" "command" "embed" "hr" "img" "input" "keygen"
-    "link" "meta" "param" "source" "track" "wbr")
-  "Void (self-closing) tags.")
-
+    "link" "meta" "param" "source" "track" "wbr"))
+
+(defvar web-mode-part-content-types '("css" "javascript" "json" "jsx"))
+
+(defvar web-mode-javascript-languages '("javascript" "jsx" "ejs"))
+
+;; NOTE: without 'syntax-table forward-word fails (#377)
 (defvar web-mode-scan-properties
-  (list 'tag-beg nil 'tag-end nil 'tag-name nil 'tag-type nil 'tag-attr nil 'tag-attr-end nil
-        'part-side nil 'part-token nil
-        'block-side nil 'block-token nil 'block-beg nil 'block-end nil)
-  "Text properties used for tokens.")
-
-(defvar web-mode-scan-properties2
-  (list 'tag-beg nil 'tag-end nil 'tag-name nil 'tag-type nil 'tag-attr nil 'tag-attr-end nil
-        'part-side nil 'part-token nil)
-  "Text properties used for tokens.")
-
-(defvar web-mode-large-embed-threshold 512
-  "Threshold for large part/block.")
-
-(defvar web-mode-has-any-large-part nil
-  "Does the current buffer has large parts ?")
-
-(defvar web-mode-has-any-large-block nil
-  "Does the current buffer has large blocks ?")
-
-(defvar web-mode-is-scratch nil
-  "Is scratch buffer ?")
-
-(defvar web-mode-time nil
-  "For benchmarking")
-
-(defvar web-mode-start-tag-overlay nil)
-
-(defvar web-mode-end-tag-overlay nil)
-
-(defvar web-mode-expand-initial-pos nil
-  "First mark pos.")
-
-(defvar web-mode-expand-previous-state ""
-  "Last mark state.")
-
-(defvar web-mode-tag-regexp "<\\(/?[[:alpha:]][[:alnum:]-]*\\)"
-  "Regular expression for HTML/XML tag.")
-
-(defvar web-mode-start-tag-regexp "<\\([[:alpha:]][[:alnum:]-]*\\)"
+  (list 'tag-beg 'tag-end 'tag-name 'tag-type
+        'tag-attr 'tag-attr-beg 'tag-attr-end
+        'part-side 'part-token
+        'jsx-beg 'jsx-end 'jsx-depth
+        'block-side 'block-token 'block-controls 'block-beg 'block-end
+        'syntax-table)
+  "Text properties used for code regions/tokens and html nodes.")
+
+(defvar web-mode-start-tag-regexp "<\\([[:alpha:]][[:alnum:]:-]*\\)"
   "Regular expression for HTML/XML start tag.")
 
 (defvar web-mode-whitespaces-regexp
   "^[ \t]\\{2,\\}$\\| \t\\|\t \\|[ \t]+$\\|^[ \n\t]+\\'\\|^[ \t]?[\n]\\{2,\\}"
   "Regular expression for whitespaces.")
 
-(defvar web-mode-engine nil
-  "Template engine")
-
-(defvar web-mode-engine-font-lock-keywords nil
-  "Font-lock keywords associated with the engine.")
+(defvar web-mode-imenu-regexp-list
+  '(("<\\(h[1-9]\\)\\([^>]*\\)>\\([^<]*\\)" 1 3 ">")
+    ("^[ \t]*<\\([@a-z]+\\)[^>]*>? *$" 1 "id=\"\\([a-zA-Z0-9_]+\\)\"" "#" ">"))
+  "Regexps to match imenu items (see http://web-mode.org/doc/imenu.txt)")
+
+(defvar web-mode-indentation-params
+  '(("lineup-args"       . t)
+    ("lineup-calls"      . t)
+    ("lineup-concats"    . t)
+    ("lineup-quotes"     . t)
+    ("lineup-ternary"    . t)
+    ("case-extra-offset" . t)
+    ))
 
 (defvar web-mode-engines
-  '(("angular"    . ("angular.js" "angularjs"))
-    ("asp"        . ())
-    ("aspx"       . ())
-    ("blade"      . ("laravel"))
-    ("closure"    . ("soy"))
-    ("ctemplate"  . ("mustache" "handlebars" "hapax" "ngtemplate" "ember"
-                     "kite" "meteor"))
-    ("django"     . ("dtl" "twig" "swig" "jinja" "jinja2" "erlydtl" "liquid"))
-    ("dust"       . ("dustjs"))
-    ("erb"        . ("eruby" "erubis"))
-    ("go"         . ("gtl"))
-    ("jsp"        . ())
-    ("mason"      . ("poet"))
-    ("python"     . ())
-    ("razor"      . ("play" "play2"))
-    ("underscore" . ("underscorejs"))
-    ("velocity"   . ("vtl" "cheetah"))
-    ("web2py"     . ()))
+  '(("angular"          . ("angularjs" "angular.js"))
+    ("asp"              . ())
+    ("aspx"             . ())
+    ("blade"            . ("laravel"))
+    ("cl-emb"           . ())
+    ("clip"             . ())
+    ("closure"          . ("soy"))
+    ("ctemplate"        . ("mustache" "handlebars" "hapax" "ngtemplate" "ember"
+                           "kite" "meteor" "blaze" "ractive"))
+    ("django"           . ("dtl" "twig" "swig" "jinja" "erlydtl" "liquid"
+                           "clabango" "selmer" "nunjucks"))
+    ("dust"             . ("dustjs"))
+    ("ejs"              . ())
+    ("elixir"           . ("phoenix"))
+    ("erb"              . ("eruby" "erubis"))
+    ("freemarker"       . ())
+    ("go"               . ("gtl"))
+    ("jsp"              . ("grails"))
+    ("mako"             . ())
+    ("marko"            . ())
+    ("mason"            . ("poet"))
+    ("lsp"              . ("lisp"))
+    ("mojolicious"      . ())
+    ("php"              . ())
+    ("python"           . ())
+    ("razor"            . ("play" "play2"))
+    ("riot"             . ())
+    ("template-toolkit" . ())
+    ("smarty"           . ())
+    ("thymeleaf"        . ())
+    ("underscore"       . ("underscore.js"))
+    ("velocity"         . ("vtl" "cheetah" "ssp"))
+    ("web2py"           . ()))
   "Engine name aliases")
 
 (defvar web-mode-content-types
-  '(("css"        . "\\.css\\'\\|\\.css\\.erb\\'")
-    ("javascript" . "\\.js\\'\\|\\.js\\.erb\\'")
-    ("json"       . "\\.\\(json\\|jsonld\\)\\'")
+  '(("css"        . "\\.\\(s?css\\|css\\.erb\\)\\'")
+    ("javascript" . "\\.\\([jt]s\\|[jt]s\\.erb\\)\\'")
+    ("json"       . "\\.\\(api\\|json\\|jsonld\\)\\'")
+    ("jsx"        . "\\.[jt]sx\\'")
+    ("xml"        . "\\.xml\\'")
     ("html"       . "."))
   "content types")
 
+(defvar web-mode-engine-attr-regexps
+  '(("angular"   . "ng-")
+    ("thymeleaf" . "th:"))
+  "Engine custom attributes")
+
+(defvar web-mode-last-enabled-feature nil)
+
+(defvar web-mode-features
+  '(("css-colorization"          . web-mode-enable-css-colorization)
+    ("element-highlight"         . web-mode-enable-current-element-highlight)
+    ("column-highlight"          . web-mode-enable-current-column-highlight)
+    ("whitespace-fontification"  . web-mode-enable-whitespace-fontification)
+    ("element-tag-fontification" . web-mode-enable-element-tag-fontification)
+    ("block-face"                . web-mode-enable-block-face)
+    ("part-face"                 . web-mode-enable-part-face)))
+
+(defvar web-mode-comment-prefixing t)
+
+(defvar web-mode-comment-formats
+  '(("java"       . "/*")
+    ("javascript" . "/*")
+    ("php"        . "/*")
+    ))
+
 (defvar web-mode-engine-file-regexps
   '(("asp"              . "\\.asp\\'")
     ("aspx"             . "\\.as[cp]x\\'")
-    ("angular"          . "angular")
-    ("blade"            . "\\.blade")
+    ("blade"            . "\\.blade\\.php\\'")
+    ("cl-emb"           . "\\.clemb\\'")
+    ("clip"             . "\\.ctml\\'")
     ("closure"          . "\\.soy\\'")
-    ("ctemplate"        . "\\.\\(chtml\\)\\'")
-    ("django"           . "\\.\\(djhtml\\|tmpl\\|dtl\\|liquid\\)\\'")
-    ("django"           . "twig")
+    ("ctemplate"        . "\\.\\(chtml\\|mustache\\)\\'")
+    ("django"           . "\\.\\(djhtml\\|tmpl\\|dtl\\|liquid\\|j2\\)\\'")
     ("dust"             . "\\.dust\\'")
-    ("erb"              . "\\.\\(erb\\|rhtml\\)\\'")
+    ("elixir"           . "\\.eex\\'")
+    ("ejs"              . "\\.ejs\\'")
+    ("erb"              . "\\.\\(erb\\|rhtml\\|erb\\.html\\)\\'")
     ("freemarker"       . "\\.ftl\\'")
     ("go"               . "\\.go\\(html\\|tmpl\\)\\'")
-    ("handlebars"       . "\\(handlebars\\|.\\hbs\\'\\)")
-    ("jsp"              . "\\.jsp\\'")
-    ("mako"             . "\\.mako\\'")
+    ("handlebars"       . "\\.\\(hb\\.html\\|hbs\\)\\'")
+    ("jinja"            . "\\.jinja\\'")
+    ("jsp"              . "\\.[gj]sp\\'")
+    ("lsp"              . "\\.lsp\\'")
+    ("mako"             . "\\.mako?\\'")
+    ("marko"            . "\\.marko\\'")
     ("mason"            . "\\.mas\\'")
-    ("mustache"         . "\\.mustache\\'")
-    ("php"              . "\\.\\(php\\|ctp\\|psp\\|inc\\)\\'")
+    ("mojolicious"      . "\\.epl?\\'")
+    ("php"              . "\\.\\(p[hs]p\\|ctp\\|inc\\)\\'")
     ("python"           . "\\.pml\\'")
-    ("razor"            . "play\\|scala\\|\\.razor\\'\\|\\.cshtml\\'\\|\\.vbhtml\\'")
+    ("razor"            . "\\.\\(cs\\|vb\\)html\\'")
+    ("riot"           .  "\\.tag\\'")
     ("smarty"           . "\\.tpl\\'")
     ("template-toolkit" . "\\.tt.?\\'")
-    ("underscore"       . "\\.underscore\\'")
-    ("velocity"         . "\\.\\(vsl\\|vtl\\|vm\\)\\'")
-    ("web2py"           . "web2py"))
+    ("thymeleaf"        . "\\.thtml\\'")
+    ("velocity"         . "\\.v\\(sl\\|tl\\|m\\)\\'")
+
+    ("django"           . "[st]wig")
+    ("razor"            . "scala")
+
+    )
   "Engine file extensions.")
 
 (defvar web-mode-smart-quotes
-  '("«" . "»")
+  '("«" . "»")
   "Preferred smart quotes")
 
 (defvar web-mode-xml-chars
@@ -677,345 +847,282 @@
   "XML chars")
 
 (defvar web-mode-html-entities
-  '(("egrave" . 232)
-    ("eacute" . 233)
-    ("ecirc"  . 234)
-    ("euml"   . 235)
-    ("agrave" . 224)
-    ("aacute" . 225)
-    ("aelig"  . 230)
-    ("ccedil" . 231)
-    ("times"  . 215)
-    ("quot"   . 34)
-    ("amp"    . 38)
-    ("lt"     . 60)
-    ("gt"     . 62)
-    ("laquo"  . 171)
-    ("raquo"  . 187)
-    ("lsquo"  . 8249)
-    ("rsquo"  . 8250)
-    ("ldquo"  . 8220)
-    ("rdquo"  . 8221)
-    ("lsaquo" . 8249)
-    ("rsaquo" . 8250)
-    ("apos"   . 39)
-    ("frac14" . 188)
-    ("frac12" . 189)
-    ("frac34" . 190)
-    ("para"   . 182)
-    ("middot" . 183)
-    ("ndash"  . 8211)
-    ("mdash"  . 8212)
-    ("bull"   . 8226)
-    ("hellip" . 8230)
-    ("trade"  . 8482)
-    ("larr"   . 8592)
-    ("uarr"   . 8593)
-    ("rarr"   . 8594)
-    ("darr"   . 8595)
-    ("harr"   . 8596)
-    ("crarr"  . 8629)
-    ("and"    . 8743)
-    ("or"     . 8744)
-    ("sdot"   . 8901)
-    )
-  "HTML entities")
-
-;;(message "%S" web-mode-engines-alternate-delimiters)
-
-(defvar web-mode-engines-alternate-delimiters
-  (if (boundp 'web-mode-engines-alternate-delimiters) web-mode-engines-alternate-delimiters '())
-  "Engine delimiters. Useful for engines that provide alternate delimiters.")
-
-(defun web-mode-highlight-whitespaces (beg end)
-  "Scan whitespaces."
-  (save-excursion
-    (goto-char beg)
-    (while (re-search-forward web-mode-whitespaces-regexp end t)
-      (add-text-properties (match-beginning 0) (match-end 0)
-                           '(face web-mode-whitespace-face))
-      ) ;while
-    ))
-
-(defun web-mode-engine-delimiter-open (engine default)
-  "alternative open delimiter"
-;;  (setq engine (if (eq engine t) ))
-  (let (delim)
-    (setq delim (car (cdr (assoc engine web-mode-engines-alternate-delimiters))))
-    (or delim default)
-  ))
-
-(defun web-mode-engine-delimiter-close (engine default)
-  "alternative close delimiter"
-  ;;  (setq engine (if (eq engine t) ))
-  (let (delim)
-    (setq delim (cdr (cdr (assoc engine web-mode-engines-alternate-delimiters))))
-    (or delim default)
-    ))
-
-(defvar web-mode-jshint-errors 0
-  "JSHint errors")
-
-(defvar web-mode-content-type ""
-  "Buffer file type.")
-
-(defvar web-mode-comments-invisible nil
-  "Comments visbility.")
-
-(defvar web-mode-is-narrowed nil
-  "Buffer has been narrowed.")
-
-(defvar web-mode-hook nil
-  "List of functions to be executed with web-mode.")
-
-(defvar web-mode-buffer-highlighted nil
-  "Is buffer highlighted.")
-
-;;    http://webdesign.about.com/od/localization/l/blhtmlcodes-ascii.htm
+  '(("AElig" . 198) ("Aacute" . 193) ("Acirc" . 194) ("Agrave" . 192)
+    ("Alpha" . 913) ("Aring" . 197) ("Atilde" . 195) ("Auml" . 196)
+    ("Beta" . 914)
+    ("Ccedil" . 199) ("Chi" . 935)
+    ("Dagger" . 8225) ("Delta" . 916)
+    ("ETH" . 208) ("Eacute" . 201) ("Ecirc" . 202) ("Egrave" . 200)
+    ("Epsilon" . 917) ("Eta" . 919) ("Euml" . 203)
+    ("Gamma" . 915)
+    ("Iacute" . 205) ("Icirc" . 206) ("Igrave" . 204) ("Iota" . 921)
+    ("Iuml" . 207)
+    ("Kappa" . 922)
+    ("Lambda" . 923)
+    ("Mu" . 924)
+    ("Ntilde" . 209) ("Nu" . 925)
+    ("OElig" . 338) ("Oacute" . 211) ("Ocirc" . 212) ("Ograve" . 210)
+    ("Omega" . 937) ("Omicron" . 927) ("Oslash" . 216) ("Otilde" . 213)
+    ("Ouml" . 214)
+    ("Phi" . 934) ("Pi" . 928) ("Prime" . 8243) ("Psi" . 936)
+    ("Rho" . 929)
+    ("Scaron" . 352) ("Sigma" . 931)
+    ("THORN" . 222) ("Tau" . 932) ("Theta" . 920)
+    ("UArr" . 8657) ("Uacute" . 218) ("Uacute" . 250) ("Ucirc" . 219)
+    ("Ugrave" . 217)  ("Upsih" . 978)
+    ("Upsilon" . 933) ("Uuml" . 220) ("Uuml" . 252)
+    ("Xi" . 926)
+    ("Yacute" . 221) ("Yuml" . 376)
+    ("Zeta" . 918)
+    ("aacute" . 225) ("acirc" . 226) ("acute" . 180) ("aelig" . 230)
+    ("agrave" . 224) ("alefsym" . 8501) ("alpha" . 945) ("amp" . 38)
+    ("ang" . 8736) ("apos" . 39) ("aring" . 229) ("asymp" . 8776)
+    ("atilde" . 227) ("auml" . 228)
+    ("bdquo" . 8222) ("beta" . 946) ("brvbar" . 166) ("bull" . 8226)
+    ("cap" . 8745) ("ccedil" . 231) ("cedil" . 184) ("cent" . 162)
+    ("chi" . 967) ("circ" . 710) ("clubs" . 9827) ("cong" . 8773)
+    ("copy" . 169) ("crarr"  . 8629) ("cup" . 8746) ("curren" . 164)
+    ("dArr" . 8659) ("dagger" . 8224) ("darr" . 8595) ("deg" . 176)
+    ("delta" . 948) ("diams" . 9830) ("divide" . 247)
+    ("eacute" . 233) ("ecirc"  . 234) ("egrave" . 232) ("empty" . 8709)
+    ("emsp" . 8195) ("ensp" . 8194) ("epsilon" . 949) ("equiv" . 8801)
+    ("eta" . 951) ("eth" . 240) ("euml" . 235) ("euro" . 8364) ("exist" . 8707)
+    ("fnof" . 402) ("forall" . 8704) ("frac12" . 189) ("frac14" . 188)
+    ("frac34" . 190) ("frasl" . 8260)
+    ("gamma" . 947) ("ge" . 8805) ("gt" . 62)
+    ("hArr" . 8660) ("harr" . 8596) ("hearts" . 9829) ("hellip" . 8230)
+    ("iacute" . 237) ("icirc" . 238) ("iexcl" . 161) ("igrave" . 236)
+    ("image" . 8465) ("infin" . 8734) ("int" . 8747) ("iota" . 953)
+    ("iquest" . 191) ("isin" . 8712) ("iuml" . 239)
+    ("kappa" . 954)
+    ("lArr" . 8656) ("lambda" . 955) ("lang" . 9001) ("laquo" . 171)
+    ("larr" . 8592) ("lceil" . 8968) ("ldquo" . 8220) ("le" . 8804)
+    ("lfloor" . 8970) ("lowast" . 8727) ("loz" . 9674) ("lrm" . 8206)
+    ("lsaquo" . 8249) ("lsquo" . 8249) ("lt" . 60)
+    ("macr" . 175) ("mdash" . 8212) ("micro" . 181) ("middot" . 183)
+    ("minus" . 8722) ("mu" . 956)
+    ("nabla" . 8711) ("nbsp" . 160) ("ndash" . 8211) ("ne" . 8800)
+    ("ni" . 8715) ("not" . 172) ("notin" . 8713) ("nsub" . 8836)
+    ("ntilde" . 241) ("nu" . 957) ("oacute" . 243) ("ocirc" . 244)
+    ("oelig" . 339) ("ograve" . 242) ("oline" . 8254) ("omega" . 969)
+    ("omicron" . 959) ("oplus" . 8853) ("or" . 8744) ("ordf" . 170)
+    ("ordm" . 186) ("oslash" . 248) ("otilde" . 245) ("otimes" . 8855)
+    ("ouml" . 246)
+    ("para" . 182) ("part" . 8706) ("permil" . 8240) ("perp" . 8869)
+    ("phi" . 966) ("pi" . 960) ("piv" . 982) ("plusmn" . 177) ("pound" . 163)
+    ("prime" . 8242) ("prod" . 8719) ("prop" . 8733) ("psi" . 968)
+    ("quot" . 34)
+    ("rArr" . 8658) ("radic" . 8730) ("rang" . 9002) ("raquo" . 187)
+    ("rarr" . 8594) ("rceil" . 8969) ("rdquo" . 8221) ("real" . 8476)
+    ("reg" . 174) ("rfloor" . 8971) ("rho" . 961) ("rlm" . 8207)
+    ("rsaquo" . 8250) ("rsquo" . 8250) ("sbquo" . 8218)
+    ("scaron" . 353) ("sdot" . 8901) ("sect" . 167) ("shy" . 173)
+    ("sigma" . 963) ("sigmaf" . 962) ("sim" . 8764) ("spades" . 9824)
+    ("sub" . 8834) ("sube" . 8838) ("sum" . 8721) ("sup" . 8835)
+    ("sup1" . 185) ("sup2" . 178) ("sup3" . 179) ("supe" . 8839)
+    ("szlig" . 223)
+    ("tau" . 964) ("there4" . 8756) ("theta" . 952) ("thetasym" . 977)
+    ("thinsp" . 8201) ("thorn" . 254) ("tilde" . 732) ("times" . 215)
+    ("trade" . 8482)
+    ("uarr" . 8593) ("ucirc" . 251) ("ugrave" . 249) ("uml" . 168)
+    ("upsilon" . 965)
+    ("weierp" . 8472)
+    ("xi" . 958)
+    ("yacute" . 253) ("yen" . 165) ("yuml" . 255)
+    ("zeta" . 950) ("zwj" . 8205) ("zwnj" . 8204)))
+
+;; http://webdesign.about.com/od/localization/l/blhtmlcodes-ascii.htm
 (defvar web-mode-display-table
   (let ((table (make-display-table)))
-    (aset table 9  (vector ?\xBB ?\t)) ;tab
-    (aset table 10 (vector ?\xB6 ?\n)) ;line feed
+    (aset table 9  (vector ?\xBB ?\t))
+    (aset table 10 (vector ?\xB6 ?\n))
     (aset table 32 (vector ?\xB7))
     table)
-  "Display table.")
-
-(defvar web-mode-hl-line-mode-flag nil
-  "Is hl-line-mode enabled ?")
-
-(defvar web-mode-blade-active-blocks
-  '("else" "elseif" "foreach" "forelse" "for" "if" "section" "stop" "unless" "while")
-  "Blade controls.")
-
-(defvar web-mode-closure-active-blocks
-  '("call" "case" "default" "deltemplate" "else" "elseif" "for" "foreach"
-    "if" "ifempty" "let" "literal" "msg" "param" "switch" "template")
-  "Closure controls.")
-
-(defvar web-mode-django-active-blocks
-  '("assets" "autoescape" "block" "blocktrans" "cache" "call" "comment"
-    "elif" "else" "elseif" "elsif" "embed" "empty" "filter" "foreach" "for"
-    "ifchanged" "ifequal" "ifnotequal" "if"
-    "macro" "draw" "random" "sandbox" "spaceless" "verbatim" "with")
-  "Django controls.")
-
-(defvar web-mode-go-active-blocks
-  '("else" "end" "if" "range" "with")
-  "Go controls.")
-
-(defvar web-mode-php-active-blocks
-  '("declare" "else" "elseif" "for" "foreach" "if" "while")
-  "PHP controls.")
-
-(defvar web-mode-smarty-active-blocks
-  '("block" "else" "elseif" "foreach" "for" "if" "section" "while")
-  "Smarty controls.")
-
-(defvar web-mode-velocity-active-blocks
-  '("define" "else" "elseif" "end" "for" "foreach" "if" "macro")
-  "Velocity controls.")
-
-(defvar web-mode-web2py-active-blocks
-  '("block" "def" "elif" "else" "end" "except" "finally" "for"
-    "if" "pass" "return" "try" "while")
-  "Web2py active controls")
-
-(defvar web-mode-active-block-regexps
-  (list
-   (cons "asp"        "----")
-   (cons "aspx"       "----")
-   (cons "angular"    "----")
-   (cons "blade"      (concat "@\\(end\\)?" (regexp-opt web-mode-blade-active-blocks t)))
-   (cons "closure"    (concat "{/?" (regexp-opt web-mode-closure-active-blocks t)))
-   (cons "ctemplate"  "{{[#^/]\\([[:alnum:]_]+\\)")
-   (cons "django"     (concat "{%[-]?[ ]+\\(end\\)?" (regexp-opt web-mode-django-active-blocks t)))
-   (cons "dust"       "{[#/:?@><+^]\\([[:alpha:]_]+\\)")
-   (cons "erb"        "<%[-=]?[ ]+\\(.* do \\|for\\|unless\\|end\\|if\\|else\\)")
-   (cons "freemarker" "</?\\([[:alpha:]]+:[[:alpha:]]+\\)\\|[[<]/?[@#]\\([[:alpha:]]+\\)")
-   (cons "go"         (concat "{{[ ]*" (regexp-opt web-mode-go-active-blocks t)))
-   (cons "jsp"        "</?\\([[:alpha:]]+:[[:alpha:]]+\\)\\|<%[ ]+\\(if\\|for\\|while\\|} else {\\)")
-   (cons "mako"       "</?%\\([[:alpha:]]+\\(?:[:][[:alpha:]]+\\)?\\)\\|%[ ]+\\(end\\)?\\(if\\|for\\|elif\\|else\\)")
-   (cons "mason"      "</?%\\(method\\|def\\)")
-   (cons "php"        (concat "<\\?\\(php[ ]+\\|[ ]*\\)?\\(end\\)?" (regexp-opt web-mode-php-active-blocks t)))
-   (cons "razor"      "@\\(main\\|if\\|for\\)")
-   (cons "smarty"     (concat (web-mode-engine-delimiter-open "smarty" "{") "/?" (regexp-opt web-mode-smarty-active-blocks t)))
-   (cons "template-toolkit" (concat "\\[% " (regexp-opt '("foreach" "if" "else" "elsif" "filter" "end") t)))
-   (cons "underscore" "<%")
-   (cons "velocity"   (concat "#" (regexp-opt web-mode-velocity-active-blocks t)))
-   (cons "web2py"     (concat "{{[ ]*" (regexp-opt web-mode-web2py-active-blocks t)))
-   )
-  "Engine control regexps")
-
-(defvar web-mode-close-block-regexps
-  (list
-   '("asp"              . "----")
-   '("aspx"             . "----")
-   '("blade"            . "@\\\(end\\|else\\|stop\\)")
-   '("closure"          . "{\\(/\\|else\\|case\\|default\\|ifempty\\)")
-   '("ctemplate"        . "{{/")
-   '("django"           . "{%[-]?[ ]+\\(end\\|else\\|elseif\\|elsif\\|elif\\|empty\\)")
-   '("dust"             . "{\\(/\\|:else\\)")
-   '("erb"              . "<%[-]?[ ]+\\(end\\|else\\)")
-   '("freemarker"       . "[<[]\\(/#\\|#els\\|#break\\)")
-   '("go"               . "{{[ ]*\\(end\\|else\\)")
-   '("jsp"              . "</\\|<% }")
-   '("mako"             . "</%\\|%[ ]+\\(end\\|elif\\|else\\)")
-   '("mason"            . "</%\\(method\\|def\\)")
-   '("php"              . "<\\?\\(php[ ]+\\|[ ]*\\)?\\(end\\|else\\|}\\)")
-   '("razor"            . "}")
-   (cons "smarty"      (concat (web-mode-engine-delimiter-open "smarty" "{") "\\(/\\|else\\)"))
-   '("template-toolkit" . "\\[% \\(end\\|els\\)")
-   '("underscore"       . "<% }")
-   '("velocity"         . "#\\(end\\|else\\)")
-   '("web2py"           . "{{[ ]*\\(pass\\|elif\\|else\\|except\\|finally\\|return\\|end\\)")
-   )
-  "Close control blocks.")
-
-(defvar web-mode-auto-pairs nil
-  "Auto-Pairs")
-
-(defvar web-mode-closing-blocks nil
-  "Snippets")
-
-(defvar web-mode-engines-closing-blocks
-  '(
-    ("php"       . (("if"      . "<?php endif; ?>")
-                    ("for"     . "<?php endfor; ?>")
-                    ("foreach" . "<?php endforeach; ?>")
-                    ("while"   . "<?php endwhile; ?>")))
-    )
-  "Closing blocks (see web-mode-block-close)"
-  )
+  "Display table used when switching to the whitespace visualization.")
+
+(defvar web-mode-expanders
+  '(("a/" . "<a href=\"|\"></a>")
+    ("b/" . "<table><tbody><tr><td>|</td><td></td></tr></tbody></table>")
+    ("c/" . "<div class=\"|\"></div>")
+    ("d/" . "<div>|</div>")
+    ("e/" . "<em>|</em>")
+    ("f/" . "<form>|</form>")
+    ("g/" . "<strong>|</strong>")
+    ("h/" . "<h1>|</h1>")
+    ("i/" . "<img src=\"|\" />")
+    ("j/" . "<script>|</script>")
+    ("l/" . "<li>|</li>")
+    ("m/" . "<main>|</main>")
+    ("n/" . "<input type=\"|\" />")
+    ("p/" . "<p>|</p>")
+    ("q/" . "<quote>|</quote>")
+    ("s/" . "<span>|</span>")
+    ("t/" . "<td>|</td>")
+    ("u/" . "<ul><li>|</li><li></li></ul>")
+    ("x/" . "<textarea>|</textarea>")
+    ("2/" . "<h2>|</h2>")
+    ("3/" . "<h3>|</h3>")
+    ("?/" . "<?php | ?>")))
 
 (defvar web-mode-engines-auto-pairs
-  '(
-    ("angular"    . (("{{ " " }}")))
-    ("asp"        . (("<% " " %>")))
-    ("aspx"       . (("<% " " %>")
-                     ("<%=" "%>")
-                     ("<%#" "%>")
-                     ("<%$" "%>")
-                     ("<%@" "%>")
-                     ("<%:" "%>")
-                     ("<%-" "- " " --%>")))
-    ("blade"      . (("{{ " " }}")
-                     ("{{-" "- " " --}}")))
-    ("django"     . (("{{ " " }}")
-                     ("{% " " %}")
-                     ("{# " " #}")))
-    ("erb"        . (("<% " " %>")
-                     ("<%=" "%>")
-                     ("<%#" "%>")))
-    ("freemarker" . (("<% " " %>")
-                     ("${ " " }")
-                     ("[% " " %]")
-                     ("[# " " #]")
-                     ("[#-" "- " " --]")))
-    ("jsp"        . (("<% " " %>")
-                     ("<%-" "- " " %>")
-                     ("<%=" "%>")
-                     ("<%!" "%>")
-                     ("<%@" "%>")
-                     ("${ " " }")))
-    ("mako"       . (("<% " " %>")
-                     ("<%!" " " " %>")
-                     ("${ " " }")))
-    ("mason"      . (("<% " " %>")))
-    ("php"        . (("<?p" "hp " " ?>")
-                     ("<? " " ?>")
-                     ("<?=" "?>")))
-    ("underscore" . (("<% " " %>")))
-    ("web2py"     . (("{{ " " }}")
-                     ("{{=" "}}")))
-    (nil          . (("<!-" "- " " -->")))
-
-    )
-  "Engines auto-pairs")
-
-(defvar web-mode-snippets nil
-  "Snippets")
+  '(("angular"          . (("{{ " . " }}")))
+    ("asp"              . (("<% " . " %>")))
+    ("aspx"             . (("<% " . " %>")
+                           ("<%=" . "%>")
+                           ("<%#" . "%>")
+                           ("<%$" . "%>")
+                           ("<%@" . "%>")
+                           ("<%:" . "%>")
+                           ("<%-" . "- | --%>")))
+    ("blade"            . (("{{{" . " | }}}")
+                           ("{{ " . " }}")
+                           ("{!!" . " | !!}")
+                           ("@{{" . " | }}")
+                           ("{{-" . "- | --}}")))
+    ("cl-emb"           . (("<% " . " %>")
+                           ("<%=" . " | %>")
+                           ("<%#" . " | %>")))
+    ("ctemplate"        . (("{{ " . "| }}")
+                           ("{{{" . " | }}}")
+                           ("{~{" . " | }}")
+                           ("{{~" . "{ | }}}")
+                           ("{{!" . "-- | --}}")
+                           ("{{/" . "}}")
+                           ("{{#" . "}}")))
+    ("django"           . (("{{ " . " }}")
+                           ("{% " . " %}")
+                           ("{%-" . " | %}")
+                           ("{# " . " #}")))
+    ("elixir"           . (("<% " . " %>")
+                           ("<%=" . " | %>")
+                           ("<%%" . " | %>")
+                           ("<%#" . " | %>")))
+    ("ejs"              . (("<% " . " %>")
+                           ("<%=" . "%>")
+                           ("<%#" . "%>")
+                           ("<%-" . "%>")))
+    ("erb"              . (("<% " . " %>")
+                           ("<%=" . "%>")
+                           ("<%#" . "%>")
+                           ("<%-" . "%>")))
+    ("freemarker"       . (("<% " . " %>")
+                           ("<#-" . "- | -->")
+                           ("${ " . " }")
+                           ("[% " . " %]")
+                           ("[# " . " #]")
+                           ("[#-" . "- | --]")))
+    ("jsp"              . (("<% " . " %>")
+                           ("<%-" . "- | --%>")
+                           ("<%=" . "%>")
+                           ("<%!" . "%>")
+                           ("<%@" . "%>")
+                           ("${ " . " }")))
+    ("lsp"              . (("<% " . " %>")
+                           ("<%%" . " | %>")
+                           ("<%#" . " | %>")))
+    ("mako"             . (("<% " . " %>")
+                           ("<%!" . " | %>")
+                           ("${ " . " }")))
+    ("marko"            . (("${ " . " }")))
+    ("mason"            . (("<% " . " %>")
+                           ("<& " . " &>")))
+    ("mojolicious"      . (("<% " . " %>")
+                           ("<%=" . " | %>")
+                           ("<%%" . " | %>")
+                           ("<%#" . " | %>")))
+    ("php"              . (("<?p" . "hp | ?>")
+                           ("<? " . " ?>")
+                           ("<?=" . "?>")))
+    ("template-toolkit" . (("[% " . " %]")
+                           ("[%-" . " | %]")
+                           ("[%#" . " | %]")))
+    ("riot"             . (("={ " . " }")))
+    ("underscore"       . (("<% " . " %>")))
+    ("web2py"           . (("{{ " . " }}")
+                           ("{{=" . "}}")))
+    (nil                . (("<!-" . "- | -->")))
+    ))
 
 (defvar web-mode-engines-snippets
-  '(("erb" . (("if" . ("<% if " . " %>\n\n<% end %>"))))
-    ("php" . (("if"      . ("<?php if ("      . "): ?>\n\n<?php endif; ?>"))
-              ("while"   . ("<?php while ("   . "): ?>\n\n<?php endwhile; ?>"))
-              ("for"     . ("<?php for ("     . " ; ; ): ?>\n\n<?php endfor; ?>"))
-              ("foreach" . ("<?php foreach (" . " as ): ?>\n\n<?php endforeach; ?>"))
-              ("switch"  . ("<?php switch ("  . "): ?>\n<?php case 1: ?>\n\n<?php break ;?>\n<?php case 2: ?>\n\n<?php break ;?>\n<?php endswitch;?>"))))
-    ("django" . (("block"   . ("{% block "   . " %}\n\n{% endblock %}"))
-                 ("comment" . ("{% comment " . " %}\n\n{% endcomment %}"))
-                 ("cycle"   . ("{% cycle "   . " as  %}\n\n{% endcycle  %}"))
-                 ("filter"  . ("{% filter "  . " %}\n\n{% endfilter %}"))
-                 ("for"     . ("{% for "     . " in  %}\n\n{% endfor %}"))
-                 ("if"      . ("{% if "      . " %}\n\n{% endif %}"))))
-    (nil . (("html5" . ("<!doctype html>\n<html>\n<head>\n<title></title>\n<meta charset=\"utf-8\" />\n</head>\n<body>\n" . "\n</body>\n</html>"))
-            ("table" . ("<table><tbody>\n<tr>\n<td>" . "</td>\n<td></td>\n</tr>\n</tbody></table>"))
-            ("ul"    . ("<ul>\n<li>" . "</li>\n<li></li>\n</ul>"))))
-    )
-  "Engines snippets")
-
-(defvar web-mode-block-regexps
+  '(("ejs" . (("for"     . "<% for (|) { %>\n\n<% } %>")
+              ("if"      . "<% if (|) { %>\n\n<% } %>")))
+    ("erb" . (("each"    . "<% |.each do  %>\n\n<% end %>")
+              ("if"      . "<% if | %>\n\n<% end %>")
+              ("when"    . "<% when | %>\n\n<% end %>")
+              ("unless"  . "<% unless | %>\n\n<% end %>")))
+    ("php" . (("if"      . "<?php if (|): ?>\n\n<?php endif; ?>")
+              ("while"   . "<?php while (|): ?>\n\n<?php endwhile; ?>")
+              ("for"     . "<?php for (| ; ; ): ?>\n\n<?php endfor; ?>")
+              ("foreach" . "<?php foreach (| as ): ?>\n\n<?php endforeach; ?>")
+              ("each"    . "<?php foreach (| as ): ?>\n\n<?php endforeach; ?>")
+              ("switch"  . "<?php switch (|): ?>\n<?php case 1: ?>\n\n<?php break ;?>\n<?php case 2: ?>\n\n<?php break ;?>\n<?php endswitch;?>")))
+    ("django" . (("block"      . "{% block | %}\n\n{% endblock %}")
+                 ("comment"    . "{% comment | %}\n\n{% endcomment %}")
+                 ("cycle"      . "{% cycle | as  %}\n\n{% endcycle  %}")
+                 ("filter"     . "{% filter | %}\n\n{% endfilter %}")
+                 ("for"        . "{% for | in  %}\n\n{% endfor %}")
+                 ("if"         . "{% if | %}\n\n{% endif %}")
+                 ("ifequal"    . "{% ifequal | %}\n\n{% endifequal %}")
+                 ("ifnotequal" . "{% ifnotequal | %}\n\n{% endifnotequal %}")
+                 ("safe"       . "{% safe | %}\n\n{% endsafe %}")))
+    ("template-toolkit" . (("if"      . "[% IF | %]\n\n[% END %]")))
+    (nil . (("html5" . "<!doctype html>\n<html>\n<head>\n<title></title>\n<meta charset=\"utf-8\" />\n</head>\n<body>\n|\n</body>\n</html>")
+            ("table" . "<table><tbody>\n<tr>\n<td>|</td>\n<td></td>\n</tr>\n</tbody></table>")
+            ("ul"    . "<ul>\n<li>|</li>\n<li></li>\n</ul>")))
+    ))
+
+(defvar web-mode-engine-token-regexps
+  (list
+   '("asp"         . "//\\|/\\*\\|\"\\|'")
+   '("ejs"         . "//\\|/\\*\\|\"\\|'")
+   '("erb"         . "\"\\|'\\|#\\|<<[-]?['\"]?\\([[:alnum:]_]+\\)['\"]?")
+   '("lsp"         . "\"\\|#|\\|;")
+   '("mako"        . "\"\\|'\\|#")
+   '("mason"       . "\"\\|'\\|#")
+   '("mojolicious" . "\"\\|'")
+   '("php"         . "//\\|/\\*\\|#\\|\"\\|'\\|<<<['\"]?\\([[:alnum:]]+\\)['\"]?")
+   '("python"      . "\"\\|'\\|#")
+   '("web2py"      . "\"\\|'"))
+  "Engine regexps used to identify tokens (strings / comments) in blocks.")
+
+(defvar web-mode-engine-open-delimiter-regexps
   (list
    '("angular"          . "{{")
-   '("asp"              . "<%")
-   '("aspx"             . "<%")
-   '("blade"            . "{{.\\|^[ \t]*@[[:alpha:]]")
+   '("asp"              . "<%\\|</?[[:alpha:]]+:[[:alpha:]]+\\|</?[[:alpha:]]+Template")
+   '("aspx"             . "<%.")
+   '("blade"            . "{{.\\|{!!\\|@{{\\|@[[:alpha:]]")
+   '("cl-emb"           . "<%")
    '("closure"          . "{.\\|/\\*\\| //")
-   '("ctemplate"        . "[$]?{{.")
-   '("django"           . "{[#{%].")
+   '("clip"             . "</?c:[[:alpha:]-]+")
+   '("ctemplate"        . "[$]?{[{~].")
+   '("django"           . "{[#{%]")
    '("dust"             . "{.")
-   '("erb"              . "<%.\\|^%.")
-   '("freemarker"       . "<%\\|${\\|</?[[:alpha:]]+:[[:alpha:]]\\|</?[@#].\\|\\[/?[@#].")
+   '("elixir"           . "<%.")
+   '("ejs"              . "<%")
+   '("erb"              . "<%\\|^%.")
+   '("freemarker"       . "<%\\|${\\|</?[[:alpha:]]+:[[:alpha:]]\\|</?[@#]\\|\\[/?[@#].")
    '("go"               . "{{.")
-   '("jsp"              . "<%\\|${\\|</?[[:alpha:]]+:[[:alpha:]]")
-   '("mako"             . "</?%\\|${\\|^[ \t]*% \\|^[ \t]*##")
-   '("mason"            . "</&>\\|</%def\\|</%method\\|<%[[:alpha:]]+\\|<[%&]\\|^%.")
+   '("jsp"              . "<%\\|${")
+   '("lsp"              . "<%")
+   '("mako"             . "</?%\\|${\\|^[ \t]*%.\\|^[ \t]*##")
+   '("marko"            . "${")
+   '("mason"            . "</?[&%]\\|^%.")
+   '("mojolicious"      . "<%\\|^[ \t]*%.")
    '("php"              . "<\\?")
    '("python"           . "<\\?")
-   '("razor"            . "@.")
-   (cons "smarty"       (concat (web-mode-engine-delimiter-open "smarty" "{") "[[:alpha:]#$/*\"]"))
-   '("template-toolkit" . "\\[[%#]")
+   '("razor"            . "@.\\|^[ \t]*}")
+   '("riot"             . "{.")
+   '("smarty"           . "{[[:alpha:]#$/*\"]")
+   '("template-toolkit" . "\\[%.\\|%%#")
    '("underscore"       . "<%")
-   '("velocity"         . "^[ \t]*#[[:alpha:]#*]\\|$[[:alpha:]!{]")
-   '("web2py"           . "{{")
-   )
-  "Engine block regexps.")
-
-(defvar web-mode-block-electric-chars
-  (list
-   (cons "blade"      '(?\{ ?\@))
-   (cons "closure"    '(?\{ ?\/))
-   (cons "ctemplate"  '(?\{ ?\$))
-   (cons "django"     '(?\{))
-   (cons "dust"       '(?\{))
-   (cons "erb"        '(?\%))
-   (cons "freemarker" '(?\[))
-   (cons "go"         '(?\{))
-   (cons "jsp"        '(?\$))
-   (cons "razor"      '(?\@))
-   (cons "smarty"     '(?\{))
-   (cons "velocity"   '(?\# ?\$)))
-  "Block electric chars.")
-
-(defvar web-mode-block-regexp nil
-  "Regular expression for identifying blocks.")
-
-(defvar web-mode-active-block-regexp nil
-  "Engine control regexp")
-
-(defvar web-mode-close-block-regexp nil
-  "Engine end control regexp")
-
-(defvar web-mode-engine-control-matcher nil
-  "Engine control match")
-
-(defvar web-mode-electric-chars nil
-  "electric chars")
+   '("velocity"         . "#[[:alpha:]#*]\\|$[[:alpha:]!{]")
+   '("web2py"           . "{{"))
+  "Engine regexps used to identify blocks.")
 
 (defvar web-mode-normalization-rules
   '(("tag-case"          . "lower-case")
     ("attr-case"         . "lower-case")
-    ("special-chars"     . "unicode") ; "unicode" "entities"
+    ("special-chars"     . "unicode") ;"unicode" "entities"
     ("css-indentation"   . t)
     ("smart-apostrophes" . t)
     ("smart-quotes"      . t)
@@ -1023,61 +1130,148 @@
     ("indentation"       . t))
   "Normalization rules")
 
+(defvar web-mode-element-tag-faces
+  (list
+   '("h1"     . web-mode-underline-face)
+   '("h2"     . web-mode-underline-face)
+   '("h3"     . web-mode-underline-face)
+   '("h4"     . web-mode-underline-face)
+   '("title"  . web-mode-underline-face)
+   '("em"     . web-mode-italic-face)
+   '("strong" . web-mode-bold-face)
+   ))
+
+(defvar web-mode-element-content-faces
+  (list
+   '("h1"     . web-mode-underline-face)
+   '("h2"     . web-mode-underline-face)
+   '("h3"     . web-mode-underline-face)
+   '("h4"     . web-mode-underline-face)
+   '("title"  . web-mode-underline-face)
+   '("em"     . web-mode-italic-face)
+   '("strong" . web-mode-bold-face)
+   ))
+
 (defvar web-mode-comment-keywords
   (regexp-opt
-   (append web-mode-extra-comment-keywords
-           '("FIXME" "TODO" "BUG" "KLUDGE" "WORKAROUND"
-             "OPTIMIZE" "HACK" "REFACTOR")))
-  "Comment keywords.")
+   (append
+    (cdr (assoc "comment" web-mode-extra-keywords))
+    '("FIXME" "TODO" "BUG" "KLUDGE" "WORKAROUND" "OPTIMIZE" "HACK" "REFACTOR" "REVIEW"))))
+
+(defvar web-mode-file-extensions
+  (list
+   '("\.png$" 0 nil)
+   '("\.jpe?g$" 0 nil)
+   '("\.gif$" 0 nil)
+   '("\.svg$" 1 nil)
+   '("\.js$" 2 t)
+   '("\.css$" 3 t))
+  "List of regexps matching filetypes in `web-mode-file-link'. Second value of each list should be the index of list containing matching tags in `web-mode-file-elements', and the third one should be t if the link is supposed to be in head or nil.")
+
+(defvar web-mode-links
+  '(("<img src=\"|\" />" . "\\.\\(png\\|jpe?g\\|gif\\)$")
+    ("<object data=\"|\" type=\"image/svg+xml\"></object>" . "\\.svg$")
+    ("<script type=\"text/javascript\" src=\"|\"></script>" . "\\.js$")
+    ("<link rel=\"stylesheet\" type=\"text/css\" href=\"|\" />" . "\\.css$"))
+  "List of tags to be used by `web-mode-file-link'.")
+
+(defvar web-mode-file-elements
+  (list
+   '("<img src=\"" "\" />")
+   '("<object data=\"" "\" type=\"image/svg+xml\"></object>")
+   '("<script type=\"text/javascript\" src=\"" "\"></script>")
+   '("<link rel=\"stylesheet\" type=\"text/css\" href=\"" "\" />"))
+  "List of tags to be used by `web-mode-file-link'.")
+
+(defvar web-mode-sql-queries
+  (regexp-opt
+   '("SELECT" "INSERT" "UPDATE" "DELETE" "select" "insert" "update" "delete")))
+
+(defvar web-mode-sql-keywords
+  (regexp-opt
+   (append
+    (cdr (assoc "sql" web-mode-extra-keywords))
+    '("SELECT" "INSERT" "UPDATE" "DELETE"
+      "FROM" "WHERE" "GROUP BY" "LIMIT" "HAVING" "JOIN" "LEFT" "INNER"
+      "FULL" "OUTER" "VALUES" "ORDER BY" "SEPARATOR" "ASC" "DESC"
+      "AND" "OR" "ON"))))
 
 (defvar web-mode-python-constants
   (regexp-opt
-   (append web-mode-extra-python-constants
-           '("True" "False" "None" "__debug__" "NotImplemented" "Ellipsis")))
-  "Python constants.")
+   (append
+    (cdr (assoc "python" web-mode-extra-constants))
+    '("True" "False" "None" "__debug__" "NotImplemented" "Ellipsis"))))
+
+(defvar web-mode-erlang-constants
+  (regexp-opt
+   (append
+    (cdr (assoc "erlang" web-mode-extra-constants))
+    '("true" "false"))))
+
+(defvar web-mode-erlang-keywords
+  (regexp-opt
+   (append
+    (cdr (assoc "erlang" web-mode-extra-keywords))
+    '("else" "if" "do" "end"))))
+
+(defvar web-mode-cl-emb-constants
+  (regexp-opt
+   '("nil" "t" "raw" "escape")))
+
+(defvar web-mode-cl-emb-keywords
+  (regexp-opt
+   '("if" "else" "endif" "unless" "endunless" "var" "repeat"
+     "endrepeat" "loop" "endloop" "include" "call" "with"
+     "endwith" "set" "genloop" "endgenloop" "insert")))
+
+(defvar web-mode-lsp-constants
+  (regexp-opt
+   '("nil" "t")))
+
+(defvar web-mode-lsp-keywords
+  (regexp-opt
+   '("dolist" "let" "while" "cond" "when" "progn" "if"
+     "dotimes" "unless" "lambda"
+     "loop" "for" "and" "or" "in" "do" "defun")))
 
 (defvar web-mode-php-constants
   (regexp-opt
-   (append web-mode-extra-php-constants
-           '("TRUE" "FALSE" "NULL" "true" "false" "null"
-             "STR_PAD_LEFT" "STR_PAD_RIGHT"
-             "ENT_COMPAT" "ENT_QUOTES" "ENT_NOQUOTES" "ENT_IGNORE"
-             "ENT_SUBSTITUTE" "ENT_DISALLOWED" "ENT_HTML401" "ENT_XML1"
-             "ENT_XHTML" "ENT_HTML5"
-             "LIBXML_NOBLANKS")))
-  "PHP constants.")
+   (append
+    (cdr (assoc "php" web-mode-extra-constants))
+    '("TRUE" "FALSE" "NULL" "true" "false" "null"
+      "STR_PAD_LEFT" "STR_PAD_RIGHT"
+      "ENT_COMPAT" "ENT_QUOTES" "ENT_NOQUOTES" "ENT_IGNORE"
+      "ENT_SUBSTITUTE" "ENT_DISALLOWED" "ENT_HTML401" "ENT_XML1"
+      "ENT_XHTML" "ENT_HTML5" "JSON_PRETTY_PRINT"
+      "LIBXML_NOBLANKS"))))
 
 (defvar web-mode-php-keywords
   (regexp-opt
-   (append web-mode-extra-php-keywords
-           '("and" "array" "as" "break"
-             "callable" "case" "catch"  "catch all" "class" "const" "continue"
-             "default" "die" "do"
-             "echo" "else" "elseif" "empty"
-             "endfor" "endforeach" "endif" "endswitch" "endwhile" "exit" "extends"
-             "finally" "for" "foreach" "function" "global" "goto"
-             "if" "include" "include_once" "instanceof" "interface" "isset"
-             "list" "next" "new" "or"
-             "private" "protected" "public"
-             "require" "require_once" "return" "static" "switch" "try" "throw" "unset" "use"
-             "var" "when" "while" "xor" "yield")))
-  "PHP keywords.")
+   (append
+    (cdr (assoc "php" web-mode-extra-keywords))
+    '("and" "array" "as" "break"
+      "callable" "case" "catch"  "catch all" "class" "const" "continue"
+      "default" "die" "do" "echo" "else" "elseif" "empty"
+      "endfor" "endforeach" "endif" "endswitch" "endwhile" "exit" "extends"
+      "finally" "for" "foreach" "function" "global" "goto"
+      "if" "include" "include_once" "instanceof" "interface" "isset"
+      "list" "next" "new" "or" "private" "protected" "public"
+      "require" "require_once" "return" "static" "switch" "try" "throw"
+      "unset" "use" "var" "when" "while" "xor" "yield"))))
 
 (defvar web-mode-php-types
   (eval-when-compile
     (regexp-opt
      '("array" "bool" "boolean" "char" "const" "double" "float"
-       "int" "integer" "long" "mixed" "object" "real" "string"
-       "Exception")))
-  "PHP types.")
+       "int" "integer" "long" "mixed" "object" "real" "string"))))
 
 (defvar web-mode-css-at-rules
   (eval-when-compile
     (regexp-opt
      '("charset" "import" "media" "page" "font-face"
        "namespace" "supports" "document"
-       "keyframes" "-moz-keyframes" "-webkit-keyframes")))
-  "CSS at-rules.")
+       "keyframes" "-moz-keyframes" "-webkit-keyframes"
+       "mixin"))))
 
 (defvar web-mode-css-pseudo-classes
   (eval-when-compile
@@ -1087,198 +1281,189 @@
        "hover" "lang" "last-child" "last-of-type" "left" "link"
        "not" "nth-child" "nth-last-child" "nth-last-of-type" "nth-of-type"
        "only-child" "only-of-type"
-       "right" "root" "selection" "target" "visited")))
-  "CSS pseudo-classes (and pseudo-elements).")
+       "right" "root" "selection" "target" "visited"))))
 
 (defvar web-mode-python-keywords
   (regexp-opt
-   (append web-mode-extra-python-keywords
-           '("and" "as" "assert" "break" "class" "continue" "def"
-             "del" "elif" "else" "except" "finally" "for" "from"
-             "global" "if" "import" "in" "is" "lambda"
-             "nonlocal" "not" "or" "pass" "raise" "return"
-             "try" "while" "with" "yield")))
-  "Python keywords.")
+   (append
+    (cdr (assoc "python" web-mode-extra-keywords))
+    '("and" "as" "assert" "break" "class" "continue" "def" "del"
+      "elif" "else" "except" "finally" "for" "from" "global"
+      "if" "import" "in" "is" "lambda" "nonlocal" "not" "or" "pass"
+      "raise" "return" "try" "while" "with" "yield"))))
 
 (defvar web-mode-jsp-keywords
   (regexp-opt
-   (append web-mode-extra-jsp-keywords
-           '("case" "catch" "do" "else" "end" "false" "for" "function"
-             "if" "in" "include" "new"
-             "package" "page" "private" "protected" "public"
-             "return" "tag" "taglib" "throw" "throws" "true" "try"
-             "void" "while")))
-  "JSP keywords.")
+   (append
+    (cdr (assoc "jsp" web-mode-extra-keywords))
+    '("case" "catch" "do" "else" "end" "false" "for" "function"
+      "if" "in" "include"
+      "new" "package" "page" "private" "protected" "public"
+      "return" "tag" "taglib" "throw" "throws" "true" "try" "void" "while"))))
 
 (defvar web-mode-erb-keywords
   (regexp-opt
-   (append web-mode-extra-erb-keywords
-           '("alias" "and" "begin" "break" "case" "class"
-             "def" "defined?" "do" "elsif" "else" "end" "ensure"
-             "fail" "for" "if" "in" "module" "next" "not"
-             "or" "redo" "rescue" "retry" "return" "then" "super"
-             "unless" "undef" "until" "when" "while" "yield"
-             "__ENCODING__" "__FILE__" "__LINE__"
-             )))
-  "ERB keywords.")
+   (append
+    (cdr (assoc "erb" web-mode-extra-keywords))
+    '("alias" "and" "begin" "break" "case" "class" "def" "defined?" "do"
+      "elsif" "else" "end" "ensure" "fail" "for" "if" "in"
+      "module" "next" "not" "or" "redo" "rescue" "retry" "return"
+      "then" "super" "unless" "undef" "until" "when" "while" "yield"
+      "__ENCODING__" "__FILE__" "__LINE__"))))
 
 (defvar web-mode-mason-keywords
   (regexp-opt
-   (append web-mode-extra-mason-keywords
-           '("and" "base" "close" "die" "each" "else" "elsif" "eval" "exists"
-             "foreach" "grep" "if" "length" "local"
-             "my" "next" "open" "or" "package" "pop"
-             "ref" "return" "stat" "sub"
-             "tie" "undef" "unless" "use"
-             "while"
-             )))
-  "Mason keywords.")
+   (append
+    (cdr (assoc "mason" web-mode-extra-keywords))
+    '("and" "base" "close" "die" "each" "else" "elsif" "eval" "exists"
+      "foreach" "grep" "if" "length" "local" "my" "next" "open" "or"
+      "package" "pop" "ref" "return" "stat" "sub" "tie"
+      "undef" "unless" "use" "while"))))
 
 (defvar web-mode-erb-builtins
   (regexp-opt
-   '("__callee__" "__dir__" "__method__"
-     "abort" "at_exit" "autoload" "autoload?"
-     "binding" "block_given?" "caller" "catch"
-     "eval" "exec" "exit" "exit!" "fail" "fork" "format"
-     "lambda" "load" "loop" "open"
-     "p" "print" "printf" "proc" "putc" "puts"
-     "raise" "rand" "readline" "readlines" "require" "require_relative"
-     "sleep" "spawn" "sprintf" "srand" "syscall" "system"
-     "throw" "trap" "warn"
-     "alias_method" "attr" "attr_accessor" "attr_reader" "attr_writer"
-     "define_method" "extend" "include" "module_function"
-     "prepend" "private" "protected" "public"
-     "refine" "using"
-
-     "error_message_on" "error_messages_for" "form" "input"
-     "auto_discovery_link_tag" "image_tag" "javascript_include_tag"
-     "stylesheet_link_tag" "image_path" "path_to_image"" "
-     "javascript_path" "path_to_javascript" "register_javascript_expansion"
-     "register_javascript_include_default" "register_stylesheet_expansion"
-     "stylesheet_path" "path_to_stylesheet" "atom_feed" "entry" "updated"
-     "benchmark" "cache" "capture" "content_for" "distance_of_time_in_words"
-     "distance_of_time_in_words_to_now" "time_ago_in_words" "date_select"
-     "datetime_select" "time_select" "select_date" "select_datetime"
-     "select_day" "select_hour" "select_minute" "select_month" "select_second"
-     "select_time" "select_year" "debug"
-     "check_box" "fields_for" "file_field" "form_for" "hidden_field"
-     "label" "password_field" "radio_button" "text_area" "text_field"
-     "check_box_tag" "field_set_tag" "file_field_tag" "form_tag"
-     "hidden_field_tag" "image_submit_tag" "label_tag" "password_field_tag"
-     "radio_button_tag" "select_tag" "submit_tag" "text_area_tag"
-     "text_field_tag"
-     "collection_select" "country_options_for_select" "country_select"
-     "option_groups_from_collection_for_select" "options_for_select"
-     "options_from_collection_for_select" "select"
-     "time_zone_options_for_select"
-     "time_zone_select" "button_to_function" "define_javascript_functions"
-     "escape_javascript" "javascript_tag" "link_to_function"" "
-     "number_to_currency" "number_to_human_size" "number_to_percentage"
-     "number_to_phone" "number_with_delimiter" "number_with_precision"
-     "evaluate_remote_response" "form_remote_for" "form_remote_tag"
-     "link_to_remote" "observe_field" "observe_field"
-     "periodically_call_remote"
-     "remote_form_for" "remote_function" "submit_to_remote" "update_page"
-     "update_page_tag" "dom_class" "dom_id" "partial_path" "sanitize"
-     "sanitize_css" "strip_links" "strip_tags"
-     "cdata_section" "content_tag" "escape_once" "tag"
-     "auto_link" "concat" "cycle" "excerpt" "highlight" "markdown" "pluralize"
-     "reset_cycle" "simple_format" "textilize" "textilize_without_paragraph"
-     "truncate" "word_wrap" "button_to" "current_page?" "link_to" "link_to_if"
-     "link_to_unless" "link_to_unless_current" "mail_to" "url_for"
-     "action_name" "atom_feed" "audio_path" "audio_tag"
-     "content_tag_for" "controller" "controller_name" "action_name"
-     "controller_path" "convert_to_model" "cookies" "csrf_meta_tag"
-     "csrf_meta_tags" "headers"
-     "current_cycle" "div_for" "email_field" "email_field_tag"
-     "favicon_link_tag" "flash" "l" "button_tag"
-     "grouped_collection_select" "grouped_options_for_select"
-     "image_alt" "j" "javascript_cdata_section"
-     "localize" "logger" "number_field"
-     "number_field_tag" "number_to_human" "params" "path_to_audio"
-     "path_to_video" "phone_field" "phone_field_tag" "provide"
-     "range_field" "range_field_tag" "raw" "render" "request"
-     "request_forgery_protection_token" "response" "safe_concat"
-     "safe_join" "search_field" "search_field_tag"
-     "session" "t" "telephone_field" "telephone_field_tag"
-     "time_tag" "translate" "url_field" "url_field_tag"
-     "url_options" "video_path" "video_tag"
-
-     ))
-  "ERB builtins.")
+   (append
+    (cdr (assoc "erb" web-mode-extra-builtins))
+
+    '("__callee__" "__dir__" "__method__"
+      "abort" "at_exit" "autoload" "autoload?"
+      "binding" "block_given?" "caller" "catch"
+      "eval" "exec" "exit" "exit!" "fail" "fork" "format"
+      "lambda" "load" "loop" "open"
+      "p" "print" "printf" "proc" "putc" "puts"
+      "raise" "rand" "readline" "readlines" "require" "require_relative"
+      "sleep" "spawn" "sprintf" "srand" "syscall" "system"
+      "throw" "trap" "warn"
+      "alias_method" "attr" "attr_accessor" "attr_reader" "attr_writer"
+      "define_method" "extend" "include" "module_function"
+      "prepend" "private" "protected" "public"
+      "refine" "using"
+
+      "error_message_on" "error_messages_for" "form" "input"
+      "auto_discovery_link_tag" "image_tag" "javascript_include_tag"
+      "stylesheet_link_tag" "image_path" "path_to_image"" "
+      "javascript_path" "path_to_javascript" "register_javascript_expansion"
+      "register_javascript_include_default" "register_stylesheet_expansion"
+      "stylesheet_path" "path_to_stylesheet" "atom_feed" "entry" "updated"
+      "benchmark" "cache" "capture" "content_for" "distance_of_time_in_words"
+      "distance_of_time_in_words_to_now" "time_ago_in_words" "date_select"
+      "datetime_select" "time_select" "select_date" "select_datetime"
+      "select_day" "select_hour" "select_minute" "select_month" "select_second"
+      "select_time" "select_year" "debug"
+      "check_box" "fields_for" "file_field" "form_for" "hidden_field"
+      "label" "password_field" "radio_button" "text_area" "text_field"
+      "check_box_tag" "field_set_tag" "file_field_tag" "form_tag"
+      "hidden_field_tag" "image_submit_tag" "label_tag" "password_field_tag"
+      "radio_button_tag" "select_tag" "submit_tag" "text_area_tag"
+      "text_field_tag"
+      "collection_select" "country_options_for_select" "country_select"
+      "option_groups_from_collection_for_select" "options_for_select"
+      "options_from_collection_for_select" "select"
+      "time_zone_options_for_select"
+      "time_zone_select" "button_to_function" "define_javascript_functions"
+      "escape_javascript" "javascript_tag" "link_to_function"" "
+      "number_to_currency" "number_to_human_size" "number_to_percentage"
+      "number_to_phone" "number_with_delimiter" "number_with_precision"
+      "evaluate_remote_response" "form_remote_for" "form_remote_tag"
+      "link_to_remote" "observe_field" "observe_field"
+      "periodically_call_remote"
+      "remote_form_for" "remote_function" "submit_to_remote" "update_page"
+      "update_page_tag" "dom_class" "dom_id" "partial_path" "sanitize"
+      "sanitize_css" "strip_links" "strip_tags"
+      "cdata_section" "content_tag" "escape_once" "tag"
+      "auto_link" "concat" "cycle" "excerpt" "highlight" "markdown" "pluralize"
+      "reset_cycle" "simple_format" "textilize" "textilize_without_paragraph"
+      "truncate" "word_wrap" "button_to" "current_page?" "link_to" "link_to_if"
+      "link_to_unless" "link_to_unless_current" "mail_to" "url_for"
+      "action_name" "atom_feed" "audio_path" "audio_tag"
+      "content_tag_for" "controller" "controller_name" "action_name"
+      "controller_path" "convert_to_model" "cookies" "csrf_meta_tag"
+      "csrf_meta_tags" "headers"
+      "current_cycle" "div_for" "email_field" "email_field_tag"
+      "favicon_link_tag" "flash" "l" "button_tag"
+      "grouped_collection_select" "grouped_options_for_select"
+      "image_alt" "j" "javascript_cdata_section"
+      "localize" "logger" "number_field"
+      "number_field_tag" "number_to_human" "params" "path_to_audio"
+      "path_to_video" "phone_field" "phone_field_tag" "provide"
+      "range_field" "range_field_tag" "raw" "render" "request"
+      "request_forgery_protection_token" "response" "safe_concat"
+      "safe_join" "search_field" "search_field_tag"
+      "session" "t" "telephone_field" "telephone_field_tag"
+      "time_tag" "translate" "url_field" "url_field_tag"
+      "url_options" "video_path" "video_tag" "simple_form_for"
+
+      ))))
 
 (defvar web-mode-asp-constants
   (regexp-opt
-   (append web-mode-extra-asp-constants
-           '("adAsyncExecute" "adAsyncFetch" "adAsyncFetchNonBlocking" "adCmdFile" "adCmdStoredProc" "adCmdTable" "adCmdTableDirect" "adCmdText" "adCmdUnknown"
-             "adCmdUnspecified" "adExecuteNoRecords" "adExecuteRecord" "adExecuteStream" "adLockBatchOptimistic" "adLockOptimistic" "adLockPessimistic"
-             "adLockReadOnly" "adLockUnspecified" "adOpenDynamic" "adOpenForwardOnly" "adOpenKeyset" "adOpenStatic" "adOpenUnspecified" "adOptionUnspecified"
-             "Empty" "Nothing" "Null" "True" "False"
-             "vbBack" "vbCr" "vbCrLf" "vbFormFeed" "vbLf" "vbNewLine" "vbNullChar" "vbNullString" "vbObjectError" "vbScript" "vbTab" "vbVerticalTab")))
-  "ASP constants.")
+   (append
+    (cdr (assoc "asp" web-mode-extra-constants))
+    '("adAsyncExecute" "adAsyncFetch" "adAsyncFetchNonBlocking" "adCmdFile"
+      "adCmdStoredProc" "adCmdTable" "adCmdTableDirect" "adCmdText" "adCmdUnknown"
+      "adCmdUnspecified" "adExecuteNoRecords" "adExecuteRecord" "adExecuteStream"
+      "adLockBatchOptimistic" "adLockOptimistic" "adLockPessimistic"
+      "adLockReadOnly" "adLockUnspecified" "adOpenDynamic" "adOpenForwardOnly"
+      "adOpenKeyset" "adOpenStatic" "adOpenUnspecified" "adOptionUnspecified"
+      "Empty" "Nothing" "Null" "True" "False"
+      "vbBack" "vbCr" "vbCrLf" "vbFormFeed" "vbLf" "vbNewLine" "vbNullChar"
+      "vbNullString" "vbObjectError" "vbScript" "vbTab" "vbVerticalTab"))))
 
 (defvar web-mode-asp-keywords
   (regexp-opt
-   (append web-mode-extra-asp-keywords
-           '("Abs" "And" "Array" "Asc" "Atn"
-             "CBool" "CByte" "CCur" "CDate" "CDbl" "CInt" "CLng" "CSng" "CStr"
-             "Call" "Case" "Chr" "Class" "Const" "Cos" "CreateObject"
-             "Date" "DateAdd" "DateDiff" "DatePart" "DateSerial" "DateValue"
-             "Day" "Dim" "Do"
-             "Each" "Else" "ElseIf" "End" "Erase" "Err" "Eval" "Exit" "Exp"
-             "Explicit"
-             "Filter" "Fix" "For" "FormatCurrency" "FormatDateTime"
-             "FormatNumber" "FormatPercent" "Function"
-             "GetLocale" "GetObject" "GetRef" "Hex" "Hour"
-             "If" "In" "InStr" "InStrRev" "InputBox" "Int" "IsArray" "IsDate"
-             "IsEmpty" "IsNull" "IsNumeric" "IsObject" "Join"
-             "LBound" "LCase" "LTrim" "Language" "Left" "Len" "Let"
-             "LoadPicture" "Log" "Loop"
-             "Mid" "Minute" "Month" "MonthName" "MsgBox"
-             "New" "Next" "Not" "Now"
-             "Oct" "On" "Option" "Or" "Preserve" "Private" "Public"
-             "RGB" "RTrim" "Redim" "Rem" "Replace" "Right" "Rnd" "Round"
-             "ScriptEngine" "ScriptEngineBuildVersion"
-             "ScriptEngineMajorVersion" "ScriptEngineMinorVersion"
-             "Second" "Select" "Set" "SetLocale" "Sgn" "Sin" "Space" "Split"
-             "Sqr" "StrComp" "StrReverse" "String" "Sub"
-             "Tan" "Then" "Time" "TimeSerial" "TimeValue" "Timer" "To" "Trim"
-             "TypeName"
-             "UBound" "UCase" "Until" "VarType"
-             "Weekday" "WeekdayName" "Wend" "With" "While" "Year")))
-  "ASP keywords.")
+   (append
+    (cdr (assoc "asp" web-mode-extra-keywords))
+    '("Abs" "And" "Array" "Asc" "Atn"
+      "CBool" "CByte" "CCur" "CDate" "CDbl" "CInt" "CLng" "CSng" "CStr"
+      "Call" "Case" "Chr" "Class" "Const" "Cos" "CreateObject"
+      "Date" "DateAdd" "DateDiff" "DatePart" "DateSerial" "DateValue"
+      "Day" "Dim" "Do"
+      "Each" "Else" "ElseIf" "End" "Erase" "Err" "Eval" "Exit" "Exp"
+      "Explicit"
+      "Filter" "Fix" "For" "FormatCurrency" "FormatDateTime"
+      "FormatNumber" "FormatPercent" "Function"
+      "GetLocale" "GetObject" "GetRef" "Hex" "Hour"
+      "If" "In" "InStr" "InStrRev" "InputBox" "Int" "IsArray" "IsDate"
+      "IsEmpty" "IsNull" "IsNumeric" "IsObject" "Join"
+      "LBound" "LCase" "LTrim" "Language" "Left" "Len" "Let"
+      "LoadPicture" "Log" "Loop"
+      "Mid" "Minute" "Month" "MonthName" "MsgBox"
+      "New" "Next" "Not" "Now"
+      "Oct" "On" "Option" "Or" "Preserve" "Private" "Public"
+      "RGB" "RTrim" "Redim" "Rem" "Replace" "Right" "Rnd" "Round"
+      "ScriptEngine" "ScriptEngineBuildVersion"
+      "ScriptEngineMajorVersion" "ScriptEngineMinorVersion"
+      "Second" "Select" "Set" "SetLocale" "Sgn" "Sin" "Space" "Split"
+      "Sqr" "StrComp" "StrReverse" "String" "Sub"
+      "Tan" "Then" "Time" "TimeSerial" "TimeValue" "Timer" "To" "Trim"
+      "TypeName"
+      "UBound" "UCase" "Until" "VarType"
+      "Weekday" "WeekdayName" "Wend" "With" "While" "Year"))))
 
 (defvar web-mode-asp-types
   (regexp-opt
-   (append web-mode-extra-asp-types
-           '("Application" "ASPError" "Request" "Response" "Server" "Session")))
-  "ASP types.")
+   (append
+    (cdr (assoc "asp" web-mode-extra-types))
+    '("Application" "ASPError" "Request" "Response" "Server" "Session"))))
 
 (defvar web-mode-aspx-keywords
   (regexp-opt
-   (append web-mode-extra-aspx-keywords
-           '("case" "catch" "do" "else" "end"
-             "for" "foreach" "function"
-             "if" "in" "include"
-             "new" "package" "page" "return"
-             "tag" "throw" "throws" "try" "while")))
-  "ASP.Net keywords.")
+   (append
+    (cdr (assoc "aspx" web-mode-extra-keywords))
+    '("case" "catch" "do" "else" "end" "for" "foreach" "function"
+      "if" "in" "include" "new" "package" "page" "return"
+      "tag" "throw" "throws" "try" "while"))))
 
 (defvar web-mode-smarty-keywords
-  (regexp-opt
-   '("as"))
-  "Smarty keywords.")
+  (regexp-opt '("as")))
 
 (defvar web-mode-velocity-keywords
   (eval-when-compile
-    (regexp-opt
-     '("in")))
-  "Velocity keywords.")
+    (regexp-opt '("in" "true" "false"))))
 
 (defvar web-mode-freemarker-keywords
   (eval-when-compile
-    (regexp-opt
-     '("as" "list"))))
+    (regexp-opt '("as" "list"))))
 
 (defvar web-mode-go-keywords
   (eval-when-compile
@@ -1289,54 +1474,87 @@
   (eval-when-compile
     (regexp-opt
      '("and" "call" "html" "index" "js" "len" "not" "or"
-       "print" "printf" "println" "urlquery")))
-  "Go functions.")
+       "print" "printf" "println" "urlquery"))))
 
 (defvar web-mode-closure-keywords
   (eval-when-compile
-    (regexp-opt
-     '("in" "and" "not" "or")
-     ))
-  "Closure keywords")
+    (regexp-opt '("in" "and" "not" "or"))))
+
+(defvar web-mode-django-control-blocks
+  '(
+
+    "assets" "autoescape"
+    "block" "blocktrans"
+    "cache" "call" "capture" "comment"
+    "draw"
+    "embed"
+    "filter" "for" "foreach" "form"
+    "if" "ifchanged" "ifequal" "ifnotequal"
+    "macro"
+    "random" "raw"
+    "safe" "sandbox" "spaceless"
+    "tablerow"
+    "unless"
+    "verbatim"
+    "with"
+
+    "endassets" "endautoescape"
+    "endblock" "endblocktrans"
+    "endcache" "endcall" "endcapture" "endcomment"
+    "draw"
+    "endembed"
+    "endfilter" "endfor" "endforeach" "endform"
+    "endif" "endifchanged" "endifequal" "endifnotequal"
+    "endmacro"
+    "endrandom" "endraw"
+    "endsafe" "endsandbox"  "endspaceless"
+    "endtablerow"
+    "endunless"
+    "endverbatim"
+    "endwith"
+
+    ;; "set" "endset" ;#504
+
+    "csrf_token" "cycle" "debug"
+    "elif" "else" "elseif" "elsif" "empty" "extends"
+    "firstof" "include" "load" "lorem" "now" "regroup" "ssi"
+    "trans" "templatetag" "url" "widthratio"
+
+    ))
+
+(defvar web-mode-django-control-blocks-regexp
+  (regexp-opt web-mode-django-control-blocks t))
 
 (defvar web-mode-django-keywords
   (eval-when-compile
     (regexp-opt
-     '("and" "as" "autoescape" "block" "blocktrans" "break"
-       "cache" "call" "comment" "context" "continue" "csrf_token" "cycle"
-       "debug" "do"
-       "embed" "empty" "else" "elseif" "elsif" "elif"
-       "endautoescape" "endblock" "endblocktrans" "endcomment"
-       "endcache" "endcall" "endembed" "endfilter" "endfor" "endif"
-       "endifchanged" "endifequal" "endifnotequal" "endmacro" "endrandom" "endraw"
-       "endsandbox" "endset" "endspaceless" "endtrans" "endverbatim" "endwith"
-       "extends" "filter" "firstof" "flush" "for" "from"
-       "if" "ifchanged" "ifequal" "ifnotequal" "ignore" "import"
-       "in" "include" "is"
-       "load" "macro" "missing" "none" "not" "now" "or" "pluralize"
-       "random" "raw" "regroup" "trans"
-       "sandbox" "set" "spaceless" "ssi" "static" "templatetag" "trans"
-       "use" "url" "var" "verbatim" "widthratio" "with"
-
-       "assign" "capture" "endcapture" "case" "layout" "tablerow" "endtablerow" ;;liquid
-       "unless" "endunless" ;; liquid
-
-       )))
-  "Django keywords.")
+     '("and" "as" "assign"
+       "break"
+       "cache" "call" "case" "context" "continue"
+       "do"
+       "flush" "from"
+       "ignore" "import" "in" "is"
+       "layout" "load"
+       "missing"
+       "none" "not"
+       "or"
+       "pluralize"
+       "random"
+       "set" ;#504
+       "unless" "use"
+       "var"
+       ))))
 
 (defvar web-mode-django-types
   (eval-when-compile
-    (regexp-opt
-     '("null" "empty" "false" "true"
-       ))))
+    (regexp-opt '("null" "false" "true"))))
 
 (defvar web-mode-directives
   (eval-when-compile
     (regexp-opt
      '("include" "page" "taglib"
        "Assembly" "Control" "Implements" "Import"
-       "Master" "OutputCache" "Page" "Reference" "Register")))
-  "Directives.")
+       "Master" "OutputCache" "Page" "Reference" "Register"))))
 
 (defvar web-mode-template-toolkit-keywords
   (regexp-opt
@@ -1345,223 +1563,64 @@
      "foreach" "get" "if" "in" "include" "insert" "is" "last"
      "macro" "meta" "or" "perl" "process" "rawperl" "return"
      "set" "stop" "switch" "tags" "throw" "try"
-     "unless" "use" "while" "wrapper"))
-  "Template-toolkit keywords")
+     "unless" "use" "while" "wrapper")))
+
+(defvar web-mode-perl-keywords
+  (regexp-opt
+   '("__DATA__" "__END__" "__FILE__" "__LINE__" "__PACKAGE__"
+     "and" "cmp" "continue" "CORE" "do" "else" "elsif" "eq" "exp"
+     "for" "foreach" "ge" "gt" "if" "le" "lock" "lt" "m" "ne" "no"
+     "or" "package" "q" "qq" "qr" "qw" "qx" "s" "sub"
+     "tr" "unless" "until" "while" "xor" "y"
+     "my" "use" "print" "say")))
 
 (defvar web-mode-javascript-keywords
   (regexp-opt
-   (append web-mode-extra-javascript-keywords
-           '("break" "case" "catch" "class" "const" "continue"
-             "debugger" "default" "delete" "do" "else" "enum" "eval"
-             "export" "extends" "finally" "for" "function" "if"
-             "implements" "import" "in" "instanceof" "interface" "let"
-             "new" "package" "private" "protected" "public"
-             "return" "static" "super" "switch" "throw"
-             "try" "typeof" "var" "void" "while" "with" "yield"
-             )))
-  "JavaScript keywords.")
+   (append
+    (cdr (assoc "javascript" web-mode-extra-keywords))
+    '("break" "case" "catch" "class" "const" "continue"
+      "debugger" "default" "delete" "do" "else" "enum" "eval"
+      "export" "extends" "finally" "for" "from" "function" "get" "if"
+      "implements" "import" "in" "instanceof" "interface" "let"
+      "new" "of" "package" "private" "protected" "public"
+      "return" "set" "static" "super" "switch"
+      "throw" "try" "typeof" "var" "void" "while" "with" "yield"))))
 
 (defvar web-mode-javascript-constants
   (regexp-opt
-   '("false" "null" "undefined"
-     "Infinity" "NaN"
-     "true" "arguments" "this"
-     ))
-  "JavaScript constants.")
+   '("false" "null" "undefined" "Infinity" "NaN" "true" "arguments" "this")))
 
 (defvar web-mode-razor-keywords
   (regexp-opt
-   (append web-mode-extra-razor-keywords
-           '("false" "true" "foreach" "if" "else" "in" "var" "for" "display" "main"
-             ;; scala
-             "match" "case"
-             "Html")))
-  "Razor keywords.")
-
-(defvar web-mode-dust-font-lock-keywords
-  (list
-   '("/?}\\|{[#/:?@><+^]?" 0 'web-mode-preprocessor-face)
-   '("{[#:/?@><+^]\\([[:alpha:]_]+\\)" 1 'web-mode-block-control-face)
-   '(":\\([[:alpha:]]+\\)" 1 'web-mode-keyword-face)
-   '("\\<\\([[:alpha:]_]+=\\)\\(\"[^\"]*\"\\|[[:alnum:]_]*\\)"
-     (1 'web-mode-block-attr-name-face)
-     (2 'web-mode-block-attr-value-face))
-   '("\\\([[:alnum:]_]+\\)" 0 'web-mode-variable-name-face)
-   ))
-
-(defvar web-mode-template-toolkit-font-lock-keywords
-  (list
-   '("\\[%[-+]?\\|[-+=]?%\\]" 0 'web-mode-preprocessor-face)
-   (cons (concat "\\<\\(" web-mode-template-toolkit-keywords "\\)\\>")
-         '(1 'web-mode-keyword-face))
-   '("\\\([[:alpha:]][[:alnum:]_]+\\)[ ]?(" 1 'web-mode-function-call-face)
-   '("\\\([[:alpha:]][[:alnum:]_]+\\)" 0 'web-mode-variable-name-face)
-   ))
-
-(defvar web-mode-smarty-font-lock-keywords
-  (list
-   (cons (concat
-          "[#]?"
-          (web-mode-engine-delimiter-close "smarty" "}")
-          "\\|"
-          (web-mode-engine-delimiter-open "smarty" "{")
-          "[/#]?")
-         '(0 'web-mode-preprocessor-face))
-   (cons (concat "[ ]\\(" web-mode-smarty-keywords "\\)[ ]")
-         '(1 'web-mode-keyword-face))
-   (cons (concat (web-mode-engine-delimiter-open "smarty" "{") "/?\\([[:alpha:]_]+\\)")
-         '(1 'web-mode-block-control-face))
-   '("\\<\\([$]\\)\\([[:alnum:]_]+\\)" (1 nil) (2 'web-mode-variable-name-face))
-   '("\\<\\(\\sw+\\)[ ]?(" 1 'web-mode-function-call-face)
-   '(" \\(\\sw+[ ]?=\\)" 1 'web-mode-param-name-face)
-   '(" \\(\\sw+\\)[ }]" 1 'web-mode-param-name-face)
-   '("|\\([[:alnum:]_]+\\)" 1 'web-mode-function-call-face)
-   '("\\(->\\)\\(\\sw+\\)" (1 nil) (2 'web-mode-variable-name-face))
-   '("[.]\\([[:alnum:]_-]+\\)[ ]?(" 1 'web-mode-function-call-face)
-   '("[.]\\([[:alnum:]_]+\\)" 1 'web-mode-variable-name-face)
-   '("#\\([[:alnum:]_]+\\)#" 1 'web-mode-variable-name-face)
-   ))
-
-(defvar web-mode-velocity-font-lock-keywords
-  (list
-   '("\\([#]\\)\\([[:alpha:]]+\\)\\>"
-     (1 'web-mode-preprocessor-face)
-     (2 'web-mode-block-control-face))
-   (cons (concat "[ ]\\(" web-mode-velocity-keywords "\\)[ ]") '(1 'web-mode-keyword-face t t))
-   '("#macro([ ]*\\([[:alpha:]]+\\)[ ]+" 1 'web-mode-function-name-face)
-   '("[.]\\([[:alnum:]_-]+\\)" 1 'web-mode-variable-name-face)
-   '("\\<\\($[!]?[{]?\\)\\([[:alnum:]_-]+\\)[}]?" (1 nil) (2 'web-mode-variable-name-face))
-   ))
-
-(defvar web-mode-mako-tag-font-lock-keywords
-  (list
-   '("</?%\\([[:alpha:]:]+\\)" 1 'web-mode-block-control-face)
-   '("</?%\\|/?>" 0 'web-mode-preprocessor-face)
-   '("\\<\\([[:alpha:]]+=\\)\\(\"[^\"]*\"\\)"
-     (1 'web-mode-block-attr-name-face t t)
-     (2 'web-mode-block-attr-value-face t t))
-   ))
-
-(defvar web-mode-mako-block-font-lock-keywords
-  (list
-   '("<%!?\\|%>" 0 'web-mode-preprocessor-face)
-   '("\\(%\\)" 1 'web-mode-preprocessor-face)
-   '("\\<\\(\\sw+\\)[ ]?(" 1 'web-mode-function-call-face)
-   (cons (concat "\\<\\(" web-mode-python-constants "\\)\\>") '(1 'web-mode-constant-face))
-   (cons (concat "\\<\\(" web-mode-python-keywords "\\)\\>") '(1 'web-mode-keyword-face))
-   (cons (concat "\\<\\(endfor\\|endif\\|endwhile\\)\\>") '(1 'web-mode-keyword-face))
-   ))
-
-(defvar web-mode-web2py-font-lock-keywords
-  (list
-   '("{{[=]?\\|}}" 0 'web-mode-preprocessor-face)
-   '("\\<\\(\\sw+\\)[ ]?(" 1 'web-mode-function-call-face)
-   (cons (concat "\\<\\(" web-mode-python-constants "\\)\\>") '(1 'web-mode-constant-face))
-   (cons (concat "\\<\\(" web-mode-python-keywords "\\)\\>") '(1 'web-mode-keyword-face))
-   (cons (concat "\\<\\(block\\|extend\\|super\\|end\\|include\\)\\>") '(1 'web-mode-keyword-face))
-   ))
-
-(defvar web-mode-django-expr-font-lock-keywords
-  (list
-;;   '("\\({{\\)[ ]?" 1 'web-mode-preprocessor-face)
-;;   '("[ ]?\\(}}\\)" 1 'web-mode-preprocessor-face)
-   '("|[ ]?\\([[:alpha:]_]+\\)\\>" 1 'web-mode-function-call-face)
-   (cons (concat "\\<\\(" web-mode-django-types "\\)\\>") '(1 'web-mode-type-face))
-   '("\\<\\([[:alpha:]_]+\\)[ ]?(" 1 'web-mode-function-call-face)
-   '("[[:alnum:]_]+" 0 'web-mode-variable-name-face)
-   ))
-
-(defvar web-mode-django-code-font-lock-keywords
-  (list
-;;   '("{%\\|%}" 0 'web-mode-preprocessor-face)
-   (cons (concat "\\<\\(" web-mode-django-keywords "\\)\\>") '(1 'web-mode-keyword-face))
-   (cons (concat "\\<\\(" web-mode-django-types "\\)\\>") '(1 'web-mode-type-face))
-   '("|[ ]?\\([[:alpha:]_]+\\)\\>" 1 'web-mode-function-call-face)
-;;   (cons (concat "|[ ]?\\(" web-mode-django-filters "\\)\\>") '(1 'web-mode-function-name-face t t))
-   '("\\<\\([[:alpha:]_]+\\)[ ]?(" 1 'web-mode-function-call-face)
-   '("[[:alnum:]_]+" 0 'web-mode-variable-name-face)
-   ))
-
-(defvar web-mode-ctemplate-font-lock-keywords
-  (list
-   '("${{\\|{{[>#/{%^&]?\\|[}]?}}" 0 'web-mode-preprocessor-face)
-   '("{{[#/>][ ]*\\([[:alnum:]_-]+\\)" 1 'web-mode-block-control-face)
-   '("[[:alnum:]_]" 0 'web-mode-variable-name-face)
-   '("[ ]+\\([[:alnum:]_]+=\\)" 1 'web-mode-param-name-face t t)
-   '("[:=]\\([[:alpha:]_]+\\)" 1 'web-mode-function-call-face t t)
-   ))
-
-(defvar web-mode-razor-font-lock-keywords
-  (list
-   '("@" 0 'web-mode-preprocessor-face)
-   (cons (concat "\\<\\(" web-mode-razor-keywords "\\)\\>")
-         '(1 'web-mode-keyword-face t t))
-;;   '("\\([[:alnum:]]+\\):" 1 'web-mode-symbol-face)
-   '("@\\([[:alnum:]_.]+\\)[ ]?(" 1 'web-mode-function-call-face)
-   '("@\\([[:alnum:]_.]+\\)" 1 'web-mode-variable-name-face)
-;;   '("<\\([[:alnum:]_]+\\)>" 1 'web-mode-type-face)
-;;   '("\\<\\([[:alnum:].]+\\)[ ]+[{[:alpha:]]+" 1 'web-mode-type-face)
-;;   '("[[:alnum:]_]+" 0 'web-mode-variable-name-face)
-   ))
-
-(defvar web-mode-closure-font-lock-keywords
-  (list
-   '("{/?\\|/?}" 0 'web-mode-preprocessor-face)
-   '("{/?\\([[:alpha:]]+\\)" 1 'web-mode-block-control-face)
-   '("{param[ ]+\\([[:alnum:]]+\\)" 1 'web-mode-symbol-face)
-   '("\\<\\(true\\|false\\|null\\)\\>" 1 'web-mode-type-face)
-   (cons (concat "\\<\\(" web-mode-closure-keywords "\\)\\>")
-         '(1 'web-mode-keyword-face))
-   '("{\\(alias\\|call\\|delcall\\|delpackage\\|deltemplate\\|namespace\\|template\\)[ ]+\\([[:alnum:].]+\\)" 2 'web-mode-constant-face)
-   '("\\(allowemptydefault\\|data\\|desc\\|meaning\\|autoescape\\|private\\|variant\\)=" 0 'web-mode-block-attr-name-face)
-   '("|\\([[:alpha:]]+\\)" 1 'web-mode-function-call-face)
-   '("\\<\\([[:alnum:]]+\\)[ ]?(" 1 'web-mode-function-call-face)
-   '("$\\([[:alnum:]._]+\\)" 1 'web-mode-variable-name-face)
-   ))
-
-(defvar web-mode-go-font-lock-keywords
-  (list
-   '("{{\\|}}" 0 'web-mode-preprocessor-face)
-   '("{{\\([[:alpha:]]+\\)" 1 'web-mode-block-control-face)
-   (cons (concat "\\<\\(" web-mode-go-keywords "\\)\\>")
-         '(1 'web-mode-keyword-face))
-   (cons (concat "\\<\\(" web-mode-go-functions "\\)\\>")
-         '(1 'web-mode-function-call-face))
-   '("[$.]\\([[:alnum:]_]+\\)" 1 'web-mode-variable-name-face t t)
-   ))
-
-(defvar web-mode-expression-font-lock-keywords
-  (list
-   '("<%\\$\\|%>" 0 'web-mode-preprocessor-face)
-   '("[[:alpha:]_]" 0 'web-mode-variable-name-face)
-   ))
-
-(defvar web-mode-angular-font-lock-keywords
-  (list
-   '("{{\\|}}" 0 'web-mode-preprocessor-face)
-   '("[[:alpha:]_]" 0 'web-mode-variable-name-face)
-   ))
+   (append
+    (cdr (assoc "razor" web-mode-extra-keywords))
+    '("false" "true" "foreach" "if" "else" "in" "var" "for" "display"
+      "match" "case" "to"
+      "Html"))))
 
 (defvar web-mode-selector-font-lock-keywords
   (list
-   (cons (concat "@\\(" web-mode-css-at-rules "\\)\\>")
-         '(1 'web-mode-css-at-rule-face))
-   '("\\<\\(all\|braille\\|embossed\\|handheld\\|print\\|projection\\|screen\\|speech\\|tty\\|tv\\|and\\|or\\)\\>" 1 'web-mode-keyword-face)
-   (cons (concat ":\\(" web-mode-css-pseudo-classes "\\)\\>")
-         '(1 'web-mode-css-pseudo-class-face))
-   '("[[:alnum:]-]+" 0 'web-mode-css-selector-face)
-   '("\\[.*?\\]\\|(.*?)" 0 nil t t)
-   '("url(\\(.+?\\))" 1 'web-mode-string-face)
+   '("$[[:alnum:]-]+" 0 'web-mode-css-variable-face)
+   (cons (concat "@\\(" web-mode-css-at-rules "\\)\\_>")
+         '(0 'web-mode-css-at-rule-face))
+   '("\\_<\\(all\|braille\\|embossed\\|handheld\\|print\\|projection\\|screen\\|speech\\|tty\\|tv\\|and\\|or\\)\\_>"
+     1 'web-mode-keyword-face)
+   '("[^,]+" 0 'web-mode-css-selector-face)
+   (cons (concat ":\\(" web-mode-css-pseudo-classes "\\)\\(([^)]*)\\)?")
+         '(0 'web-mode-css-pseudo-class-face t t))
    ))
 
 (defvar web-mode-declaration-font-lock-keywords
   (list
-   (cons (concat "@\\(" web-mode-css-at-rules "\\)\\>") '(1 'web-mode-css-at-rule-face))
-   '("url(\\([^)]+\\)" 1 'web-mode-string-face)
-   '("\\([[:alpha:]-]+\\)[ ]?:" 1 'web-mode-css-property-name-face)
+   '("--[[:alnum:]-]+" 0 'web-mode-css-variable-face)
+   '("$[[:alnum:]-]+" 0 'web-mode-css-variable-face)
+   (cons (concat "@\\(" web-mode-css-at-rules "\\)\\_>") '(1 'web-mode-css-at-rule-face))
+   '("\\([[:alpha:]-]+\\)[ ]?:" 0 'web-mode-css-property-name-face)
    '("\\([[:alpha:]-]+\\)[ ]?(" 1 'web-mode-css-function-face)
    '("#[[:alnum:]]\\{1,6\\}" 0 'web-mode-css-color-face t t)
    '("![ ]?important" 0 'web-mode-css-priority-face t t)
+   '("\\([^,]+\\)[ ]+{" 1 'web-mode-css-selector-face)
+   '("'[^']*'\\|\"[^\"]*\"" 0 'web-mode-string-face t t)
    ))
 
 (defvar web-mode-html-font-lock-keywords
@@ -1574,64 +1633,229 @@
 
 (defvar web-mode-javascript-font-lock-keywords
   (list
-   (cons (concat "\\<\\(" web-mode-javascript-keywords "\\)\\>")
-         '(0 'web-mode-keyword-face))
-   (cons (concat "\\<\\(" web-mode-javascript-constants "\\)\\>")
-         '(0 'web-mode-constant-face))
-   '("\\<\\(new\\|instanceof\\) \\([[:alnum:]_.]+\\)\\>" 2 'web-mode-type-face)
-   '("\\<\\([[:alnum:]_]+\\):[ ]*function[ ]*(" 1 'web-mode-function-name-face)
-   '("\\<function[ ]+\\([[:alnum:]_]+\\)" 1 'web-mode-function-name-face)
-   '("\\<var[ ]+\\([[:alnum:]_]+\\)" 1 'web-mode-variable-name-face)
-;;   '("\\<\\(var\\)\\>[ ]+"
-;;     (1 'web-mode-keyword-face)
-;;     ("\\([[:alnum:]_]+\\)\\([ ]*=[^,;]*\\)?[,; ]" nil nil (1 'web-mode-variable-name-face)))
-   '("\\<\\(function\\)[ ]*("
+   '("@\\([[:alnum:]_]+\\)\\_>" 0 'web-mode-keyword-face)
+   ;;(cons (concat "\\_<\\(" web-mode-javascript-keywords "\\)\\_>") '(0 'web-mode-keyword-face))
+   (cons (concat "\\([ \t}{(]\\|^\\)\\(" web-mode-javascript-keywords "\\)\\_>") '(0 'web-mode-keyword-face))
+   (cons (concat "\\_<\\(" web-mode-javascript-constants "\\)\\_>") '(0 'web-mode-constant-face))
+   '("\\_<\\(new\\|instanceof\\|class\\|extends\\) \\([[:alnum:]_.]+\\)\\_>" 2 'web-mode-type-face)
+   '("\\_<\\([[:alnum:]_]+\\):[ ]*function[ ]*(" 1 'web-mode-function-name-face)
+   '("\\_<\\(function\\|get\\|set\\)[ ]+\\([[:alnum:]_]+\\)"
      (1 'web-mode-keyword-face)
+     (2 'web-mode-function-name-face))
+   '("([ ]*\\([[:alnum:]_]+\\)[ ]*=>" 1 'web-mode-function-name-face)
+   '("[ ]*\\([[:alnum:]_]+\\)[ ]*=[ ]*([^)]*)[ ]*=>[ ]*{" 1 'web-mode-function-name-face)
+   '("\\_<\\(var\\|let\\|const\\)[ ]+\\([[:alnum:]_]+\\)" 2 'web-mode-variable-name-face)
+   '("\\([[:alnum:]_]+\\)[ ]*=> [{(]" 1 'web-mode-variable-name-face)
+   '("\\(function\\|[,=]\\|^\\)[ ]*("
      ("\\([[:alnum:]_]+\\)\\([ ]*=[^,)]*\\)?[,)]" nil nil (1 'web-mode-variable-name-face)))
    '("\\([[:alnum:]_]+\\):" 1 'web-mode-variable-name-face)
+   '("\\_<\\([[:alnum:]_-]+\\)[ ]?(" 1 'web-mode-function-call-face)
+   ))
+
+(defvar web-mode-html-tag-font-lock-keywords
+  (list
+   '("\\(</?\\)\\([[:alnum:]]+\\)"
+     (1 'web-mode-html-tag-bracket-face)
+     (2 'web-mode-html-tag-face))
+   '("\"[^\"]*\"" 0 'web-mode-html-attr-value-face)
+   '("\\([[:alnum:]]+\\)" 1 'web-mode-html-attr-name-face)
+   '("/?>" 0 'web-mode-html-tag-bracket-face)
+  ))
+
+(defvar web-mode-dust-font-lock-keywords
+  (list
+   '("{[#:/?@><+^]\\([[:alpha:]_.]+\\)" 1 'web-mode-block-control-face)
+   '(":\\([[:alpha:]]+\\)" 1 'web-mode-keyword-face)
+   '("\\_<\\([[:alnum:]_]+=\\)\\(\"[^\"]*\"\\|[[:alnum:]_]*\\)"
+     (1 'web-mode-block-attr-name-face)
+     (2 'web-mode-block-attr-value-face))
+   '("\\\([[:alnum:]_.]+\\)" 0 'web-mode-variable-name-face)
+   ))
+
+(defvar web-mode-template-toolkit-font-lock-keywords
+  (list
+   (cons (concat "\\_<\\(" web-mode-template-toolkit-keywords "\\)\\_>") '(1 'web-mode-keyword-face))
+   '("\\\([[:alpha:]][[:alnum:]_]+\\)[ ]?(" 1 'web-mode-function-call-face)
+   '("\\\([[:alpha:]][[:alnum:]_]+\\)" 0 'web-mode-variable-name-face)
+   ))
+
+(defvar web-mode-smarty-font-lock-keywords
+  (list
+   (cons (concat "[ ]\\(" web-mode-smarty-keywords "\\)[ ]") '(1 'web-mode-keyword-face))
+   '("{/?\\([[:alpha:]_]+\\)" 1 'web-mode-block-control-face)
+   '("\\([}{]\\)" 0 'web-mode-block-delimiter-face)
+   '("\\_<\\([$]\\)\\([[:alnum:]_]+\\)" (1 nil) (2 'web-mode-variable-name-face))
+   '("\\_<\\(\\sw+\\)[ ]?(" 1 'web-mode-function-call-face)
+   '(" \\(\\sw+[ ]?=\\)" 1 'web-mode-param-name-face)
+   '(" \\(\\sw+\\)[ }]" 1 'web-mode-param-name-face)
+   '("|\\([[:alnum:]_]+\\)" 1 'web-mode-function-call-face)
+   '("\\(->\\)\\(\\sw+\\)" (1 nil) (2 'web-mode-variable-name-face))
+   '("[.]\\([[:alnum:]_-]+\\)[ ]?(" 1 'web-mode-function-call-face)
+   '("[.]\\([[:alnum:]_]+\\)" 1 'web-mode-variable-name-face)
+   '("#\\([[:alnum:]_]+\\)#" 1 'web-mode-variable-name-face)
+   ))
+
+(defvar web-mode-velocity-font-lock-keywords
+  (list
+   '("#{?\\([[:alpha:]_]+\\)\\_>" (1 'web-mode-block-control-face))
+   (cons (concat "\\_<\\(" web-mode-velocity-keywords "\\)\\_>") '(1 'web-mode-keyword-face t t))
+   '("#macro([ ]*\\([[:alpha:]]+\\)[ ]+" 1 'web-mode-function-name-face)
+   '("[.]\\([[:alnum:]_-]+\\)" 1 'web-mode-variable-name-face)
+   '("\\_<\\($[!]?[{]?\\)\\([[:alnum:]_-]+\\)[}]?" (1 nil) (2 'web-mode-variable-name-face))
+   ))
+
+(defvar web-mode-mako-tag-font-lock-keywords
+  (list
+   '("</?%\\([[:alpha:]:]+\\)" 1 'web-mode-block-control-face)
+   '("\\_<\\([[:alpha:]]+=\\)\\(\"[^\"]*\"\\)"
+     (1 'web-mode-block-attr-name-face t t)
+     (2 'web-mode-block-attr-value-face t t))
+   ))
+
+(defvar web-mode-mako-block-font-lock-keywords
+  (list
+   '("\\_<\\(\\sw+\\)[ ]?(" 1 'web-mode-function-call-face)
+   (cons (concat "\\_<\\(" web-mode-python-constants "\\)\\_>") '(1 'web-mode-constant-face))
+   (cons (concat "\\_<\\(" web-mode-python-keywords "\\)\\_>") '(1 'web-mode-keyword-face))
+   (cons (concat "\\_<\\(endfor\\|endif\\|endwhile\\)\\_>") '(1 'web-mode-keyword-face))
+   ))
+
+(defvar web-mode-web2py-font-lock-keywords
+  (list
+   '("\\_<\\(\\sw+\\)[ ]?(" 1 'web-mode-function-call-face)
+   (cons (concat "\\_<\\(" web-mode-python-constants "\\)\\_>") '(1 'web-mode-constant-face))
+   (cons (concat "\\_<\\(" web-mode-python-keywords "\\)\\_>") '(1 'web-mode-keyword-face))
+   (cons (concat "\\_<\\(block\\|extend\\|super\\|end\\|include\\)\\_>") '(1 'web-mode-keyword-face))
+   ))
+
+(defvar web-mode-django-expr-font-lock-keywords
+  (list
+   '("|[ ]?\\([[:alpha:]_]+\\)\\_>" 1 'web-mode-filter-face)
+   (cons (concat "\\_<\\(" web-mode-django-types "\\)\\_>") '(1 'web-mode-type-face))
+   '("\\_<\\([[:alpha:]_]+\\)[ ]?(" 1 'web-mode-function-call-face)
+   '("[[:alnum:]_]+" 0 'web-mode-variable-name-face)
+   ))
+
+(defvar web-mode-django-code-font-lock-keywords
+  (list
+   (cons (concat "{%[ ]*\\(" web-mode-django-control-blocks-regexp "\\)[ %]") '(1 'web-mode-block-control-face))
+   '("{%[ ]*\\(end[[:alpha:]]+\\)\\_>" 1 'web-mode-block-control-face) ;#504
+   (cons (concat "\\_<\\(" web-mode-django-keywords "\\)\\_>") '(1 'web-mode-keyword-face))
+   (cons (concat "\\_<\\(" web-mode-django-types "\\)\\_>") '(1 'web-mode-type-face))
+   '("|[ ]?\\([[:alpha:]_]+\\)\\_>" 1 'web-mode-function-call-face)
+   '("\\_<\\([[:alpha:]_]+\\)[ ]?(" 1 'web-mode-function-call-face)
+   '("[[:alnum:]_.]+" 0 'web-mode-variable-name-face)
+   '("[[:alnum:]_]+\\([.][[:alnum:]_]+\\)+" 0 'web-mode-variable-name-face t t)
+   ))
+
+(defvar web-mode-ctemplate-font-lock-keywords
+  (list
+   '("{[~]?{[#/>]?[ ]*\\([[:alnum:]_-]+\\)" 1 'web-mode-block-control-face)
+   '("[ \t]+\\([[:alnum:]_-]+\\)=\\([[:alnum:]_.]+\\|\"[^\"]+\"\\|'[^']+'\\|\([^)]+\)\\)"
+     (1 'web-mode-block-attr-name-face)
+     (2 'web-mode-block-attr-value-face))
+   '("\"[^\"]+\"" 0 'web-mode-block-string-face)
+   ))
+
+(defvar web-mode-razor-font-lock-keywords
+  (list
+   '("@\\([[:alnum:]_.]+\\)[ ]*[({]" 1 'web-mode-block-control-face)
+   (cons (concat "\\_<\\(" web-mode-razor-keywords "\\)\\_>") '(1 'web-mode-keyword-face))
+   '("\\_<\\(String\\)\\_>" 1 'web-mode-type-face)
+   '("\\([[:alnum:]]+:\\)" 1 'web-mode-symbol-face)
+   '("\\(@[[:alnum:]_.]+\\)" 1 'web-mode-variable-name-face)
+   ))
+
+(defvar web-mode-riot-font-lock-keywords
+  (list
+   '("\\(parent\\|opts\\|tags\\|this\\)\\.\\([[:alnum:]_.]+\\)"
+     (1 'web-mode-constant-face)
+     (2 'web-mode-variable-name-face))
+   '("\\([[:alnum:]_.]+\\)" 0 'web-mode-variable-name-face)
+   ))
+
+(defvar web-mode-closure-font-lock-keywords
+  (list
+   '("{/?\\([[:alpha:]]+\\)" 1 'web-mode-block-control-face)
+   '("{param[ ]+\\([[:alnum:]]+\\)" 1 'web-mode-symbol-face)
+   '("\\_<\\(true\\|false\\|null\\)\\_>" 1 'web-mode-type-face)
+   (cons (concat "\\_<\\(" web-mode-closure-keywords "\\)\\_>") '(1 'web-mode-keyword-face))
+   '("{\\(alias\\|call\\|delcall\\|delpackage\\|deltemplate\\|namespace\\|template\\)[ ]+\\([[:alnum:].]+\\)" 2 'web-mode-constant-face)
+   '("\\(allowemptydefault\\|data\\|desc\\|meaning\\|autoescape\\|private\\|variant\\)=" 0 'web-mode-block-attr-name-face)
+   '("|\\([[:alpha:]]+\\)" 1 'web-mode-function-call-face)
+   '("\\_<\\([[:alnum:]]+\\)[ ]?(" 1 'web-mode-function-call-face)
+   '("$\\([[:alnum:]._]+\\)" 1 'web-mode-variable-name-face)
+   ))
+
+(defvar web-mode-go-font-lock-keywords
+  (list
+   '("{{[ ]*\\([[:alpha:]]+\\)" 1 'web-mode-block-control-face)
+   (cons (concat "\\_<\\(" web-mode-go-keywords "\\)\\_>") '(1 'web-mode-keyword-face))
+   (cons (concat "\\_<\\(" web-mode-go-functions "\\)\\_>") '(1 'web-mode-function-call-face))
+   '("[$.]\\([[:alnum:]_]+\\)" 1 'web-mode-variable-name-face t t)
+   ))
+
+(defvar web-mode-expression-font-lock-keywords
+  (list
+   '("[[:alpha:]_]" 0 'web-mode-variable-name-face)
+   ))
+
+(defvar web-mode-angular-font-lock-keywords
+  (list
+   '("[[:alpha:]_]" 0 'web-mode-variable-name-face)
    ))
 
 (defvar web-mode-underscore-font-lock-keywords
   (list
-   '("<%[-=]?\\|%>" 0 'web-mode-preprocessor-face)
-   (cons (concat "\\<\\(" web-mode-javascript-keywords "\\)\\>")
-         '(0 'web-mode-keyword-face))
-   '("\\<\\(_\.[[:alpha:]]+\\)(" 1 'web-mode-function-call-face)
-   '("\\<new \\([[:alnum:]_.]+\\)\\>" 1 'web-mode-type-face)
-   '("\\<\\([[:alnum:]_]+\\):[ ]*function[ ]*(" 1 'web-mode-function-name-face)
-   '("\\<\\(var\\)\\>[ ]+\\([[:alnum:]_]+\\)"
+   (cons (concat "\\_<\\(" web-mode-javascript-keywords "\\)\\_>") '(0 'web-mode-keyword-face))
+   '("\\_<\\(_\.[[:alpha:]]+\\)(" 1 'web-mode-function-call-face)
+   '("\\_<new \\([[:alnum:]_.]+\\)\\_>" 1 'web-mode-type-face)
+   '("\\_<\\([[:alnum:]_]+\\):[ ]*function[ ]*(" 1 'web-mode-function-name-face)
+   '("\\_<\\(var\\)\\_>[ ]+\\([[:alnum:]_]+\\)"
      (1 'web-mode-keyword-face)
      (2 'web-mode-variable-name-face))
    ))
 
+(defvar web-mode-engine-tag-font-lock-keywords
+  (list
+   '("</?\\([[:alpha:]]+\\(?:Template\\|[:.][[:alpha:]-]+\\)\\)" 1 'web-mode-block-control-face)
+   '("\\_<\\([[:alpha:]-]+=\\)\\(\"[^\"]*\"\\)"
+     (1 'web-mode-block-attr-name-face t t)
+     (2 'web-mode-block-attr-value-face t t))
+   '("\\_<\\([[:alpha:]-]+=\\)\\('[^']*\'\\)"
+     (1 'web-mode-block-attr-name-face t t)
+     (2 'web-mode-block-attr-value-face t t))
+   ))
+
+(defvar web-mode-jsp-font-lock-keywords
+  (list
+   '("\\(throws\\|new\\|extends\\)[ ]+\\([[:alnum:].]+\\)" 2 'web-mode-type-face)
+   (cons (concat "\\_<\\(" web-mode-jsp-keywords "\\)\\_>") '(0 'web-mode-keyword-face))
+   '("\\_<\\([[:alnum:]._]+\\)[ ]?(" 1 'web-mode-function-call-face)
+   '("@\\(\\sw*\\)" 1 'web-mode-variable-name-face)
+   '("\\_<\\([[:alnum:].]+\\)[ ]+[{[:alpha:]]+" 1 'web-mode-type-face)
+   ))
+
 (defvar web-mode-asp-font-lock-keywords
   (list
-   '("<%=?\\|%>" 0 'web-mode-preprocessor-face)
-   (cons (concat "\\<\\(" web-mode-asp-keywords "\\)\\>")
-         '(0 'web-mode-keyword-face))
-   (cons (concat "\\<\\(" web-mode-asp-types "\\)\\>")
-         '(0 'web-mode-type-face))
-   (cons (concat "\\<\\(" web-mode-asp-constants "\\)\\>")
-         '(0 'web-mode-constant-face))
+   (cons (concat "\\_<\\(" web-mode-asp-keywords "\\)\\_>") '(0 'web-mode-keyword-face))
+   (cons (concat "\\_<\\(" web-mode-asp-types "\\)\\_>") '(0 'web-mode-type-face))
+   (cons (concat "\\_<\\(" web-mode-asp-constants "\\)\\_>") '(0 'web-mode-constant-face))
    '("\\(Class\\|new\\) \\([[:alnum:]_]+\\)" 2 'web-mode-type-face)
    '("Const \\([[:alnum:]_]+\\)" 1 'web-mode-constant-face)
-   '("\\<dim\\>"
+   '("\\_<dim\\_>"
      (0 'web-mode-keyword-face)
      ("[[:alnum:]_]+" nil nil (0 'web-mode-variable-name-face)))
-   '("\\<\\(public\\|private\\|sub\\|function\\)\\> \\([[:alnum:]_]+\\)[ ]*("
-     2 'web-mode-function-name-face)
-   '("\\<\\(public\\|private\\|dim\\)\\> \\([[:alnum:]_]+\\)"
-     2 'web-mode-variable-name-face)
+   '("\\_<\\(public\\|private\\|sub\\|function\\)\\_> \\([[:alnum:]_]+\\)[ ]*(" 2 'web-mode-function-name-face)
+   '("\\_<\\(public\\|private\\|dim\\)\\_> \\([[:alnum:]_]+\\)" 2 'web-mode-variable-name-face)
    ))
 
 (defvar web-mode-aspx-font-lock-keywords
   (list
-   '("<%[:=#]?\\|%>" 0 'web-mode-preprocessor-face)
-   (cons (concat "\\<\\(" web-mode-aspx-keywords "\\)\\>") '(0 'web-mode-keyword-face))
-   '("\\<\\([[:alnum:].]+\\)[ ]+[[:alpha:]]+" 1 'web-mode-type-face)
+   (cons (concat "\\_<\\(" web-mode-aspx-keywords "\\)\\_>") '(0 'web-mode-keyword-face))
+   '("\\_<\\([[:alnum:].]+\\)[ ]+[[:alpha:]]+" 1 'web-mode-type-face)
    ))
 
-;;Unified Expression Language
 (defvar web-mode-uel-font-lock-keywords
   (list
    '("[$#{]{\\|}" 0 'web-mode-preprocessor-face)
@@ -1646,64 +1870,44 @@
    '("\".+\"\\|'.*'" 0 'web-mode-string-face)
    ))
 
+(defvar web-mode-marko-font-lock-keywords
+  (list
+   '("[[:alnum:]_]+" 0 'web-mode-variable-name-face)
+   ))
+
 (defvar web-mode-freemarker-square-font-lock-keywords
   (list
-   '("\\[/?[#@]\\|/?>\\|/?\\]" 0 'web-mode-preprocessor-face)
    '("\\[/?[#@]\\([[:alpha:]_.]*\\)" 1 'web-mode-block-control-face)
    '("#\\(macro\\|function\\) \\([[:alpha:]]+\\)" 2 'web-mode-function-name-face)
-   (cons (concat "\\<\\(" web-mode-freemarker-keywords "\\)\\>")
-         '(1 'web-mode-keyword-face))
-   '("\\<\\([[:alnum:]._]+\\)[ ]?(" 1 'web-mode-function-call-face)
+   (cons (concat "\\_<\\(" web-mode-freemarker-keywords "\\)\\_>") '(1 'web-mode-keyword-face))
+   '("\\_<\\([[:alnum:]._]+\\)[ ]?(" 1 'web-mode-function-call-face)
    '("[[:alpha:]]\\([[:alnum:]_]+\\)?" 0 'web-mode-variable-name-face)
    ))
 
 (defvar web-mode-freemarker-font-lock-keywords
   (list
-   '("</?[#@]\\|/?>\\|/?>" 0 'web-mode-preprocessor-face)
    '("</?[#@]\\([[:alpha:]_.]*\\)" 1 'web-mode-block-control-face)
    '("#\\(macro\\|function\\) \\([[:alpha:]]+\\)" 2 'web-mode-function-name-face)
-   (cons (concat "\\<\\(" web-mode-freemarker-keywords "\\)\\>") '(1 'web-mode-keyword-face))
-   '("\\<\\([[:alnum:]._]+\\)[ ]?(" 1 'web-mode-function-call-face)
+   (cons (concat "\\_<\\(" web-mode-freemarker-keywords "\\)\\_>") '(1 'web-mode-keyword-face))
+   '("\\_<\\([[:alnum:]._]+\\)[ ]?(" 1 'web-mode-function-call-face)
    '("[[:alpha:]]\\([[:alnum:]_]+\\)?" 0 'web-mode-variable-name-face)
    ))
 
-;;TODO : definir web-mode-block-attr-name-face et web-mode-block-attr-name-face
-(defvar web-mode-jsp-tag-font-lock-keywords
-  (list
-   '("</?\\|/?>" 0 'web-mode-preprocessor-face)
-   '("</?\\([[:alpha:]]+:[[:alpha:]]+\\)" 1 'web-mode-block-control-face)
-   '("\\<\\([[:alpha:]]+=\\)\\(\"[^\"]*\"\\)"
-     (1 'web-mode-block-attr-name-face t t)
-     (2 'web-mode-block-attr-value-face t t))
-   ))
-
-(defvar web-mode-jsp-font-lock-keywords
-  (list
-   '("-?%>\\|<%\\(!\\|=\\|#=\\)?" 0 'web-mode-preprocessor-face)
-   '("\\(throws\\|new\\|extends\\)[ ]+\\([[:alnum:].]+\\)" 2 'web-mode-type-face)
-   (cons (concat "\\<\\(" web-mode-jsp-keywords "\\)\\>") '(0 'web-mode-keyword-face))
-   '("\\<\\([[:alnum:]._]+\\)[ ]?(" 1 'web-mode-function-call-face)
-   '("@\\(\\sw*\\)" 1 'web-mode-variable-name-face)
-   '("\\<\\([[:alnum:].]+\\)[ ]+[{[:alpha:]]+" 1 'web-mode-type-face)
-   ))
-
 (defvar web-mode-directive-font-lock-keywords
   (list
-   '("<%@\\|%>" 0 'web-mode-preprocessor-face)
    '("<%@[ ]*\\([[:alpha:]]+\\)[ ]+" 1 'web-mode-block-control-face)
-   '("\\<\\([[:alpha:]]+=\\)\\(\"[^\"]*\"\\)"
+   '("\\_<\\([[:alpha:]]+=\\)\\(\"[^\"]*\"\\)"
      (1 'web-mode-block-attr-name-face t t)
      (2 'web-mode-block-attr-value-face t t))
    ))
 
 (defvar web-mode-erb-font-lock-keywords
   (list
-;;   '("-?%>\\|^%\\|<%[=-]?" 0 'web-mode-preprocessor-face)
    '("[^:]\\(:[[:alnum:]_]+\\)" 1 'web-mode-symbol-face)
    '("\\([[:alnum:]_]+:\\)[ ]+" 1 'web-mode-symbol-face)
-   (cons (concat "\\<\\(" web-mode-erb-builtins "\\)\\>") '(0 'web-mode-builtin-face))
-   (cons (concat "\\<\\(" web-mode-erb-keywords "\\)\\>") '(0 'web-mode-keyword-face))
-   '("\\<\\(self\\|true\\|false\\|nil\\)\\>" 0 'web-mode-variable-name-face)
+   (cons (concat "\\_<\\(" web-mode-erb-builtins "\\)\\_>") '(0 'web-mode-builtin-face))
+   (cons (concat "\\_<\\(" web-mode-erb-keywords "\\)\\_>") '(0 'web-mode-keyword-face))
+   '("\\_<\\(self\\|true\\|false\\|nil\\)\\_>" 0 'web-mode-variable-name-face)
    '("[@$]@?\\([[:alnum:]_]+\\)" 0 'web-mode-variable-name-face)
    '("class[ ]+\\([[:alnum:]_]+\\)" 1 'web-mode-type-face)
    '("def[ ]+\\([[:alnum:]_]+\\)" 1 'web-mode-function-name-face)
@@ -1711,67 +1915,112 @@
    '("/[^/]+/" 0 'web-mode-string-face)
    ))
 
+(defvar web-mode-ejs-font-lock-keywords
+  web-mode-javascript-font-lock-keywords)
+
 (defvar web-mode-python-font-lock-keywords
   (list
-   '("<\\?\\|\\?>" 0 'web-mode-preprocessor-face)
-   (cons (concat "\\<\\(" web-mode-python-keywords "\\)\\>") '(0 'web-mode-keyword-face))
+   (cons (concat "\\_<\\(" web-mode-python-keywords "\\)\\_>") '(0 'web-mode-keyword-face))
    ))
 
-(defvar web-mode-mason-font-lock-keywords
+(defvar web-mode-erlang-font-lock-keywords
   (list
-   '("^\\(%\\)" 1 'web-mode-preprocessor-face)
-   '("<%\\(def\\|method\\).*>" 0 'web-mode-preprocessor-face)
-   '("</&>\\|</?%[[:alpha:]]+>\\|<[%&]|?\\|[%&]>" 0 'web-mode-preprocessor-face)
-   (cons (concat "\\<\\(" web-mode-mason-keywords "\\)\\>")
-         '(0 'web-mode-keyword-face))
+   (cons (concat "\\_<\\(" web-mode-erlang-keywords "\\)\\_>") '(0 'web-mode-keyword-face))
+   (cons (concat "\\_<\\(" web-mode-erlang-constants "\\)\\_>") '(0 'web-mode-constant-face))
+   '("@\\([[:alnum:]_]+\\)" 0 'web-mode-variable-name-face)
+   '("[ ]\\(:[[:alnum:]-_]+\\)" 1 'web-mode-symbol-face)
+   ))
+
+(defvar web-mode-mason-code-font-lock-keywords
+  (list
+   (cons (concat "\\_<\\(" web-mode-mason-keywords "\\)\\_>") '(0 'web-mode-keyword-face))
    '("sub[ ]+\\([[:alnum:]_]+\\)" 1 'web-mode-function-name-face)
-   '(" | \\([hun]+\\) " 1 'web-mode-function-name-face)
-   '("\\<\\([[:alnum:]_]+\\)[ ]?::" 1 'web-mode-type-face)
+   '("\\_<\\([[:alnum:]_]+\\)[ ]?::" 1 'web-mode-type-face)
    '("\\([@]\\)\\([[:alnum:]#_]*\\)" (1 nil) (2 'web-mode-variable-name-face))
-   '("\\<\\([$%]\\)\\([[:alnum:]@#_]*\\)" (1 nil) (2 'web-mode-variable-name-face))
+   '("\\_<\\([$%]\\)\\([[:alnum:]@#_]*\\)" (1 nil) (2 'web-mode-variable-name-face))
    '("{\\([[:alnum:]_]+\\)}" 1 'web-mode-variable-name-face)
-   '("\\<\\(\\sw+\\)[ ]?(" 1 'web-mode-function-call-face)
+   '("\\_<\\(\\sw+\\)[ ]?(" 1 'web-mode-function-call-face)
    '("[[:alnum:]_][ ]?::[ ]?\\([[:alnum:]_]+\\)" 1 'web-mode-variable-name-face)
    '("->[ ]?\\([[:alnum:]_]+\\)" 1 'web-mode-variable-name-face)
+   '("\\(?:method\\|def\\) \\([[:alnum:]._]+\\)" 1 'web-mode-function-name-face)
+   '("|[ ]*\\([[:alnum:],]+\\)[ ]*%>" 1 'web-mode-filter-face)
+   ))
+
+(defvar web-mode-mason-block-font-lock-keywords
+  (list
+   '("<[/]?%\\([[:alpha:]]+\\)" 1 'web-mode-block-control-face)
+   '("[[:alpha:]]" 0 'web-mode-block-attr-value-face)
+   ))
+
+(defvar web-mode-mojolicious-font-lock-keywords
+  (list
+   (cons (concat "\\_<\\(" web-mode-perl-keywords "\\)\\_>") '(0 'web-mode-keyword-face))
+   '("\\_<\\(begin\\|end\\)\\_>" 1 'web-mode-constant-face)
+   '("\\_<\\([$]\\)\\([[:alnum:]_]*\\)" (1 nil) (2 'web-mode-variable-name-face))
+   ))
+
+(defvar web-mode-lsp-font-lock-keywords
+  (list
+   (cons (concat "\\_<\\(" web-mode-lsp-keywords "\\)\\_>") '(0 'web-mode-keyword-face))
+   (cons (concat "\\_<\\(" web-mode-lsp-constants "\\)\\_>") '(1 'web-mode-constant-face))
+   '("[ ]\\(:[[:alnum:]-_]+\\)" 1 'web-mode-symbol-face)
+   '("(defun \\([[:alnum:]-:]+\\)" 1 'web-mode-function-name-face)
+   '("(defvar \\([[:alnum:]-:]+\\)" 1 'web-mode-variable-name-face)
+   ))
+
+(defvar web-mode-cl-emb-font-lock-keywords
+  (list
+   (cons (concat "\\_<\\(" web-mode-cl-emb-keywords "\\)\\_>") '(0 'web-mode-keyword-face))
+   (cons (concat "\\_<\\(" web-mode-cl-emb-constants "\\)\\_>") '(0 'web-mode-constant-face))
+   '("\\(@\\)" 1 'web-mode-function-call-face)
+   (list (concat "\\(@" web-mode-cl-emb-keywords "\\)[ ]+\\([[:alnum:]_]+\\)")
+         '(1 'web-mode-keyword-face)
+         '(2 'web-mode-variable-name-face))
    ))
 
 (defvar web-mode-php-font-lock-keywords
   (list
-;;   '("<\\?\\(php\\|=\\)?\\|\\?>" 0 'web-mode-preprocessor-face)
-   (cons (concat "\\<\\(" web-mode-php-keywords "\\)\\>") '(0 'web-mode-keyword-face))
-   (cons (concat "(\\<\\(" web-mode-php-types "\\)\\>") '(1 'web-mode-type-face))
-   (cons (concat "\\<\\(" web-mode-php-constants "\\)\\>") '(0 'web-mode-constant-face))
+   (cons (concat "\\_<\\(" web-mode-php-keywords "\\)\\_>") '(0 'web-mode-keyword-face))
+   (cons (concat "(\\_<\\(" web-mode-php-types "\\)\\_>") '(1 'web-mode-type-face))
+   (cons (concat "\\_<\\(" web-mode-php-constants "\\)\\_>") '(0 'web-mode-constant-face))
    '("function[ ]+\\([[:alnum:]_]+\\)" 1 'web-mode-function-name-face)
-   '("\\<\\(\\sw+\\)[ ]?(" 1 'web-mode-function-call-face)
+   '("\\_<\\([[:alnum:]_]+\\)[ ]?(" 1 'web-mode-function-call-face)
    '("[[:alnum:]_][ ]?::[ ]?\\([[:alnum:]_]+\\)" 1 'web-mode-constant-face)
    '("->[ ]?\\([[:alnum:]_]+\\)" 1 'web-mode-variable-name-face)
-   '("\\<\\([[:alnum:]_]+\\)[ ]?::" 1 'web-mode-type-face)
-   '("\\<\\(instanceof\\|class\\|extends\\|new\\)[ ]+\\([[:alnum:]_]+\\)" 2 'web-mode-type-face)
-   '("\\<\\([$]\\)\\([[:alnum:]_]*\\)" (1 nil) (2 'web-mode-variable-name-face))
+   '("\\_<\\([[:alnum:]_]+\\)[ ]?::" 1 'web-mode-type-face)
+   '("\\_<\\(instanceof\\|class\\|extends\\|new\\)[ ]+\\([[:alnum:]_]+\\)" 2 'web-mode-type-face)
+   '("\\_<\\([$]\\)\\([[:alnum:]_]*\\)" (1 nil) (2 'web-mode-variable-name-face))
+   ))
+
+(defvar web-mode-latex-font-lock-keywords
+  (list
+   '("[[:alnum:]_]+" 0 'web-mode-function-name-face t t)
    ))
 
 (defvar web-mode-blade-font-lock-keywords
   (append
    (list
-    '("{{\\|}}" 0 'web-mode-preprocessor-face)
-    '("\\(@\\)\\([[:alpha:]_]+\\)"
-      (1 'web-mode-preprocessor-face)
-      (2 'web-mode-block-control-face)))
+    '("@\\([[:alpha:]_]+\\)" (1 'web-mode-block-control-face)))
    web-mode-php-font-lock-keywords))
 
 (defvar web-mode-engines-font-lock-keywords
   '(("angular"          . web-mode-angular-font-lock-keywords)
-    ("asp"              . web-mode-asp-font-lock-keywords)
     ("blade"            . web-mode-blade-font-lock-keywords)
+    ("cl-emb"           . web-mode-cl-emb-font-lock-keywords)
     ("closure"          . web-mode-closure-font-lock-keywords)
     ("ctemplate"        . web-mode-ctemplate-font-lock-keywords)
     ("dust"             . web-mode-dust-font-lock-keywords)
+    ("elixir"           . web-mode-erlang-font-lock-keywords)
+    ("ejs"              . web-mode-ejs-font-lock-keywords)
     ("erb"              . web-mode-erb-font-lock-keywords)
     ("go"               . web-mode-go-font-lock-keywords)
-    ("mason"            . web-mode-mason-font-lock-keywords)
+    ("lsp"              . web-mode-lsp-font-lock-keywords)
+    ("marko"            . web-mode-marko-font-lock-keywords)
+    ("mojolicious"      . web-mode-mojolicious-font-lock-keywords)
     ("php"              . web-mode-php-font-lock-keywords)
     ("python"           . web-mode-python-font-lock-keywords)
     ("razor"            . web-mode-razor-font-lock-keywords)
+    ("riot"             . web-mode-riot-font-lock-keywords)
     ("smarty"           . web-mode-smarty-font-lock-keywords)
     ("template-toolkit" . web-mode-template-toolkit-font-lock-keywords)
     ("underscore"       . web-mode-underscore-font-lock-keywords)
@@ -1779,18 +2028,36 @@
     ("velocity"         . web-mode-velocity-font-lock-keywords))
   "Engines font-lock keywords")
 
+(defvar web-mode-before-auto-complete-hooks nil
+  "List of functions to run before triggering the auto-complete library.
+
+Auto-complete sources will sometimes need some tweaking to work
+nicely with web-mode. This hook gives users the chance to adjust
+the environment as needed for ac-sources, right before they're used.")
+
+(defvar web-mode-ignore-ac-start-advice nil
+  "If not nil 'defadvice' for 'ac-start' will be ignored.
+
+Can be set inside a hook in 'web-mode-before-auto-complete-hooks' to
+non nil to ignore the defadvice which sets ac-sources according to current
+language. This is needed if the corresponding auto-completion triggers
+another auto-completion with different ac-sources (e.g. ac-php)")
+
+(defvar web-mode-ac-sources-alist nil
+  "alist mapping language names to ac-sources for that language.")
+
 (defvar web-mode-syntax-table
   (let ((table (make-syntax-table)))
-    (modify-syntax-entry ?_ "w" table)
-
+    (modify-syntax-entry ?- "_" table)
+    (modify-syntax-entry ?_ "_" table) ;#563
     (modify-syntax-entry ?< "." table)
     (modify-syntax-entry ?> "." table)
     (modify-syntax-entry ?& "." table)
     (modify-syntax-entry ?/ "." table)
     (modify-syntax-entry ?= "." table)
-
+    (modify-syntax-entry ?% "." table)
     table)
-  "Syntax table in use in web-mode buffers.")
+  "Syntax table used to reveal whitespaces.")
 
 (defvar web-mode-map
   (let ((map (make-sparse-keymap)))
@@ -1798,6 +2065,7 @@
     (define-key map [menu-bar wm]             (cons "Web-Mode" (make-sparse-keymap)))
     (define-key map [menu-bar wm dom]         (cons "Dom" (make-sparse-keymap)))
     (define-key map [menu-bar wm blk]         (cons "Block" (make-sparse-keymap)))
+    (define-key map [menu-bar wm attr]        (cons "Html Attr" (make-sparse-keymap)))
     (define-key map [menu-bar wm tag]         (cons "Html Tag" (make-sparse-keymap)))
     (define-key map [menu-bar wm elt]         (cons "Html Element" (make-sparse-keymap)))
 
@@ -1806,7 +2074,7 @@
     (define-key map [menu-bar wm dom dom-xpa] '(menu-item "XPath" web-mode-dom-xpath))
     (define-key map [menu-bar wm dom dom-tra] '(menu-item "Traverse" web-mode-dom-traverse))
     (define-key map [menu-bar wm dom dom-err] '(menu-item "Show error(s)" web-mode-dom-errors-show))
-    (define-key map [menu-bar wm dom dom-ent] '(menu-item "Replace HTML entities" web-mode-dom-entities-replace))
+    (define-key map [menu-bar wm dom dom-ent] '(menu-item "Replace html entities" web-mode-dom-entities-replace))
     (define-key map [menu-bar wm dom dom-quo] '(menu-item "Replace dumb quotes" web-mode-dom-quotes-replace))
     (define-key map [menu-bar wm dom dom-apo] '(menu-item "Replace apostrophes" web-mode-dom-apostrophes-replace))
     (define-key map [menu-bar wm dom dom-nor] '(menu-item "Normalise" web-mode-dom-normalize))
@@ -1815,15 +2083,24 @@
     (define-key map [menu-bar wm blk blk-pre] '(menu-item "Previous" web-mode-block-previous))
     (define-key map [menu-bar wm blk blk-nex] '(menu-item "Next" web-mode-block-next))
     (define-key map [menu-bar wm blk blk-kil] '(menu-item "Kill" web-mode-block-kill))
-    (define-key map [menu-bar wm blk blk-end] '(menu-item "End" web-mode-block-beginning))
+    (define-key map [menu-bar wm blk blk-end] '(menu-item "End" web-mode-block-end))
     (define-key map [menu-bar wm blk blk-clo] '(menu-item "Close" web-mode-block-close))
     (define-key map [menu-bar wm blk blk-beg] '(menu-item "Beginning" web-mode-block-beginning))
 
+    (define-key map [menu-bar wm attr attr-ins] '(menu-item "Insert" web-mode-attribute-insert))
+    (define-key map [menu-bar wm attr attr-end] '(menu-item "End" web-mode-attribute-end))
+    (define-key map [menu-bar wm attr attr-beg] '(menu-item "Beginning" web-mode-attribute-beginning))
+    (define-key map [menu-bar wm attr attr-sel] '(menu-item "Select" web-mode-attribute-select))
+    (define-key map [menu-bar wm attr attr-kil] '(menu-item "Kill" web-mode-attribute-kill))
+    (define-key map [menu-bar wm attr attr-nex] '(menu-item "Next" web-mode-attribute-next))
+    (define-key map [menu-bar wm attr attr-pre] '(menu-item "Previous" web-mode-attribute-previous))
+    (define-key map [menu-bar wm attr attr-tra] '(menu-item "Transpose" web-mode-attribute-transpose))
+
+    (define-key map [menu-bar wm tag tag-beg] '(menu-item "Sort Attributes" web-mode-tag-attributes-sort))
     (define-key map [menu-bar wm tag tag-sel] '(menu-item "Select" web-mode-tag-select))
     (define-key map [menu-bar wm tag tag-pre] '(menu-item "Previous" web-mode-tag-previous))
     (define-key map [menu-bar wm tag tag-nex] '(menu-item "Next" web-mode-tag-next))
-    (define-key map [menu-bar wm tag tag-mat] '(menu-item "Match" web-mode-tag-match))
-    (define-key map [menu-bar wm tag tag-end] '(menu-item "End" web-mode-tag-beginning))
+    (define-key map [menu-bar wm tag tag-end] '(menu-item "End" web-mode-tag-end))
     (define-key map [menu-bar wm tag tag-beg] '(menu-item "Beginning" web-mode-tag-beginning))
 
     (define-key map [menu-bar wm elt elt-wra] '(menu-item "Wrap" web-mode-element-wrap))
@@ -1839,23 +2116,34 @@
     (define-key map [menu-bar wm elt elt-end] '(menu-item "End" web-mode-element-end))
     (define-key map [menu-bar wm elt elt-inn] '(menu-item "Content (select)" web-mode-element-content-select))
     (define-key map [menu-bar wm elt elt-clo] '(menu-item "Close" web-mode-element-close))
+    (define-key map [menu-bar wm elt elt-ins] '(menu-item "Insert" web-mode-element-insert))
     (define-key map [menu-bar wm elt elt-dup] '(menu-item "Clone" web-mode-element-clone))
     (define-key map [menu-bar wm elt elt-cfo] '(menu-item "Children fold" web-mode-element-children-fold-or-unfold))
     (define-key map [menu-bar wm elt elt-chi] '(menu-item "Child" web-mode-element-child))
     (define-key map [menu-bar wm elt elt-beg] '(menu-item "Beginning" web-mode-element-beginning))
 
     (define-key map [menu-bar wm fol]         '(menu-item "Fold/Unfold" web-mode-fold-or-unfold))
+    (define-key map [menu-bar wm hig]         '(menu-item "Highlight buffer" web-mode-buffer-highlight))
     (define-key map [menu-bar wm ind]         '(menu-item "Indent buffer" web-mode-buffer-indent))
-    (define-key map [menu-bar wm nav]         '(menu-item "Tag/Block navigation" web-mode-tag-match))
+    (define-key map [menu-bar wm nav]         '(menu-item "Tag/Block navigation" web-mode-navigate))
     (define-key map [menu-bar wm exp]         '(menu-item "Mark and Expand" web-mode-mark-and-expand))
     (define-key map [menu-bar wm spa]         '(menu-item "Toggle whitespaces" web-mode-whitespaces-show))
     (define-key map [menu-bar wm sni]         '(menu-item "Insert snippet" web-mode-snippet-insert))
 
     ;;--------------------------------------------------------------------------
-    ;; "C-c letter"  are reserved for users
-
+    ;; "C-c <LETTER>" are reserved for users
+
+    (define-key map (kbd "C-c C-a b") 'web-mode-attribute-beginning)
+    (define-key map (kbd "C-c C-a e") 'web-mode-attribute-end)
+    (define-key map (kbd "C-c C-a i") 'web-mode-attribute-insert)
+    (define-key map (kbd "C-c C-a n") 'web-mode-attribute-next)
+    (define-key map (kbd "C-c C-a s") 'web-mode-attribute-select)
+    (define-key map (kbd "C-c C-a k") 'web-mode-attribute-kill)
+    (define-key map (kbd "C-c C-a p") 'web-mode-attribute-previous)
+    (define-key map (kbd "C-c C-a t") 'web-mode-attribute-transpose)
+
+    (define-key map (kbd "C-c C-b b") 'web-mode-block-beginning)
     (define-key map (kbd "C-c C-b c") 'web-mode-block-close)
-    (define-key map (kbd "C-c C-b b") 'web-mode-block-beginning)
     (define-key map (kbd "C-c C-b e") 'web-mode-block-end)
     (define-key map (kbd "C-c C-b k") 'web-mode-block-kill)
     (define-key map (kbd "C-c C-b n") 'web-mode-block-next)
@@ -1863,19 +2151,21 @@
     (define-key map (kbd "C-c C-b s") 'web-mode-block-select)
 
     (define-key map (kbd "C-c C-d a") 'web-mode-dom-apostrophes-replace)
-    (define-key map (kbd "C-c C-d n") 'web-mode-dom-normalize)
     (define-key map (kbd "C-c C-d d") 'web-mode-dom-errors-show)
     (define-key map (kbd "C-c C-d e") 'web-mode-dom-entities-replace)
+    (define-key map (kbd "C-c C-d n") 'web-mode-dom-normalize)
     (define-key map (kbd "C-c C-d q") 'web-mode-dom-quotes-replace)
     (define-key map (kbd "C-c C-d t") 'web-mode-dom-traverse)
     (define-key map (kbd "C-c C-d x") 'web-mode-dom-xpath)
 
+    (define-key map (kbd "C-c C-e /") 'web-mode-element-close)
+    (define-key map (kbd "C-c C-e a") 'web-mode-element-content-select)
     (define-key map (kbd "C-c C-e b") 'web-mode-element-beginning)
     (define-key map (kbd "C-c C-e c") 'web-mode-element-clone)
     (define-key map (kbd "C-c C-e d") 'web-mode-element-child)
     (define-key map (kbd "C-c C-e e") 'web-mode-element-end)
     (define-key map (kbd "C-c C-e f") 'web-mode-element-children-fold-or-unfold)
-    (define-key map (kbd "C-c C-e i") 'web-mode-element-content-select)
+    (define-key map (kbd "C-c C-e i") 'web-mode-element-insert)
     (define-key map (kbd "C-c C-e k") 'web-mode-element-kill)
     (define-key map (kbd "C-c C-e m") 'web-mode-element-mute-blanks)
     (define-key map (kbd "C-c C-e n") 'web-mode-element-next)
@@ -1887,6 +2177,7 @@
     (define-key map (kbd "C-c C-e v") 'web-mode-element-vanish)
     (define-key map (kbd "C-c C-e w") 'web-mode-element-wrap)
 
+    (define-key map (kbd "C-c C-t a") 'web-mode-tag-attributes-sort)
     (define-key map (kbd "C-c C-t b") 'web-mode-tag-beginning)
     (define-key map (kbd "C-c C-t e") 'web-mode-tag-end)
     (define-key map (kbd "C-c C-t m") 'web-mode-tag-match)
@@ -1896,49 +2187,44 @@
 
     ;;--------------------------------------------------------------------------
 
-    (define-key map (kbd "C-;")       'web-mode-comment-or-uncomment)
     (define-key map (kbd "M-;")       'web-mode-comment-or-uncomment)
 
+    ;;C-c C-a : attribute
     ;;C-c C-b : block
     ;;C-c C-d : dom
     ;;C-c C-e : element
     (define-key map (kbd "C-c C-f")   'web-mode-fold-or-unfold)
+    (define-key map (kbd "C-c C-h")   'web-mode-buffer-highlight)
     (define-key map (kbd "C-c C-i")   'web-mode-buffer-indent)
     (define-key map (kbd "C-c C-j")   'web-mode-jshint)
+    (define-key map (kbd "C-c C-l")   'web-mode-file-link)
     (define-key map (kbd "C-c C-m")   'web-mode-mark-and-expand)
+    (define-key map (kbd "C-c C-n")   'web-mode-navigate)
+    (define-key map (kbd "C-c C-r")   'web-mode-reload)
     (define-key map (kbd "C-c C-s")   'web-mode-snippet-insert)
     ;;C-c C-t : tag
     (define-key map (kbd "C-c C-w")   'web-mode-whitespaces-show)
 
-    ;; compatibility with nxml
-    ;;(define-key map (kbd "M-C-u")     'web-mode-element-parent)
-    ;;(define-key map (kbd "M-C-d")     'web-mode-element-child)
-    ;;(define-key map (kbd "M-C-n")     'web-mode-element-next)
-    ;;(define-key map (kbd "M-C-p")     'web-mode-element-previous)
-
-    ;;(define-key map (kbd "C-c /")     'web-mode-element-close)
-    ;;(define-key map (kbd "C-c <")     'web-mode-element-beginning)
-    ;;(define-key map (kbd "C-c >")     'web-mode-element-end)
-
-    ;;(define-key map (kbd "C-c C-c")   'web-mode-block-close)
-    ;;(define-key map (kbd "C-c C-n")   'web-mode-tag-match)
-    ;;(define-key map (kbd "C-c C-p")   'web-mode-element-parent)
-    ;;(define-key map (kbd "C-c C-v")   'web-mode-dom-traverse)
-
     map)
   "Keymap for `web-mode'.")
 
-;;--- compatibility
+;;---- COMPATIBILITY -----------------------------------------------------------
 
 (eval-and-compile
 
-  (defalias 'web-mode-prog-mode
-    (if (fboundp 'prog-mode) 'prog-mode 'fundamental-mode))
-
+  ;; compatibility with emacs 22
+  (defun web-mode-string-match-p (regexp string &optional start)
+    "Same as `string-match' except it does not change the match data."
+    (let ((inhibit-changing-match-data t))
+      (string-match regexp string start)))
+
+  (unless (fboundp 'string-match-p)
+    (fset 'string-match-p (symbol-function 'web-mode-string-match-p)))
+
+  ;; compatibility with emacs < 23.3
   (if (fboundp 'with-silent-modifications)
       (defalias 'web-mode-with-silent-modifications 'with-silent-modifications)
     (defmacro web-mode-with-silent-modifications (&rest body)
-      "For compatibility with Emacs pre 23.3"
       `(let ((old-modified-p (buffer-modified-p))
              (inhibit-modification-hooks t)
              (buffer-undo-list t))
@@ -1946,100 +2232,111 @@
              ,@body
            (set-buffer-modified-p old-modified-p)))))
 
+  ;; compatibility with emacs < 24
+  (defalias 'web-mode-prog-mode
+    (if (fboundp 'prog-mode) 'prog-mode 'fundamental-mode))
+
+  ;; compatibility with emacs < 24.3
+  (defun web-mode-buffer-narrowed-p ()
+    (if (fboundp 'buffer-narrowed-p)
+        (buffer-narrowed-p)
+      (/= (- (point-max) (point-min)) (buffer-size))))
+
+  ;; compatibility with emacs < 24.3
+  (unless (fboundp 'setq-local)
+    (defmacro setq-local (var val)
+    `(set (make-local-variable ',var) ,val)))
+
   ) ;eval-and-compile
 
-(defvar web-mode-font-lock-keywords
-  '(web-mode-font-lock-highlight))
-
-(defun web-mode-font-lock-extend-region ()
-  (save-excursion
-;;    (message "before : font-lock-beg=%S - font-lock-end=%S" font-lock-beg font-lock-end)
-    (setq font-lock-beg (or (web-mode-previous-tag-at-bol-pos font-lock-beg)
-                            (point-min))
-          font-lock-end (or (web-mode-next-tag-at-eol-pos font-lock-end)
-                            (point-max)))
-;;    (message "after : font-lock-beg=%S - font-lock-end=%S" font-lock-beg font-lock-end)
-    nil))
-
-(defun web-mode-font-lock-highlight (limit)
-  "font-lock matcher"
-;;  (message "web-mode-font-lock-highlight : point=%S limit=%S" (point) limit)
-  (web-mode-highlight-region (point) limit)
-  nil)
+;;---- MAJOR MODE --------------------------------------------------------------
 
 ;;;###autoload
 (define-derived-mode web-mode web-mode-prog-mode "Web"
-  "Major mode for editing web templates (HTML documents with embedded parts and blocks)."
-
+  "Major mode for editing web templates."
+
+  (make-local-variable 'web-mode-attr-indent-offset)
+  (make-local-variable 'web-mode-attr-value-indent-offset)
   (make-local-variable 'web-mode-auto-pairs)
-  (make-local-variable 'web-mode-buffer-highlighted)
+  (make-local-variable 'web-mode-block-regexp)
+  (make-local-variable 'web-mode-change-beg)
+  (make-local-variable 'web-mode-change-end)
+  (make-local-variable 'web-mode-code-indent-offset)
+  (make-local-variable 'web-mode-column-overlays)
+  (make-local-variable 'web-mode-comment-formats)
   (make-local-variable 'web-mode-comment-style)
   (make-local-variable 'web-mode-content-type)
+  (make-local-variable 'web-mode-css-indent-offset)
   (make-local-variable 'web-mode-display-table)
+  (make-local-variable 'web-mode-django-control-blocks)
+  (make-local-variable 'web-mode-django-control-blocks-regexp)
+  (make-local-variable 'web-mode-enable-block-face)
+  (make-local-variable 'web-mode-enable-inlays)
+  (make-local-variable 'web-mode-enable-part-face)
+  (make-local-variable 'web-mode-enable-sexp-functions)
   (make-local-variable 'web-mode-engine)
-  (make-local-variable 'web-mode-block-regexps)
-  (make-local-variable 'web-mode-enable-block-face)
-  (make-local-variable 'web-mode-enable-part-face)
+  (make-local-variable 'web-mode-engine-attr-regexp)
   (make-local-variable 'web-mode-engine-file-regexps)
+  (make-local-variable 'web-mode-engine-open-delimiter-regexps)
+  (make-local-variable 'web-mode-engine-token-regexp)
   (make-local-variable 'web-mode-expand-initial-pos)
+  (make-local-variable 'web-mode-expand-initial-scroll)
   (make-local-variable 'web-mode-expand-previous-state)
-  (make-local-variable 'web-mode-has-any-large-block)
-  (make-local-variable 'web-mode-has-any-large-part)
-  (make-local-variable 'web-mode-hl-line-mode-flag)
   (make-local-variable 'web-mode-indent-style)
-  (make-local-variable 'web-mode-is-narrowed)
+  (make-local-variable 'web-mode-indentless-attributes)
+  (make-local-variable 'web-mode-indentless-elements)
+  (make-local-variable 'web-mode-inhibit-fontification)
+  (make-local-variable 'web-mode-is-scratch)
   (make-local-variable 'web-mode-jshint-errors)
-  (make-local-variable 'web-mode-block-regexp)
-  (make-local-variable 'web-mode-start-tag-overlay)
-  (make-local-variable 'web-mode-end-tag-overlay)
+  (make-local-variable 'web-mode-last-enabled-feature)
+  (make-local-variable 'web-mode-markup-indent-offset)
+  (make-local-variable 'web-mode-minor-engine)
+  (make-local-variable 'web-mode-overlay-tag-end)
+  (make-local-variable 'web-mode-overlay-tag-start)
+  (make-local-variable 'web-mode-sql-indent-offset)
   (make-local-variable 'web-mode-time)
 
-  (make-local-variable 'after-change-functions)
-  (make-local-variable 'change-major-mode-hook)
+  (make-local-variable 'comment-end)
+  (make-local-variable 'comment-region-function)
+  (make-local-variable 'comment-start)
   (make-local-variable 'fill-paragraph-function)
-;;  (make-local-variable 'font-lock-beg)
+  (make-local-variable 'font-lock-beg)
   (make-local-variable 'font-lock-defaults)
-;;  (make-local-variable 'font-lock-end)
   (make-local-variable 'font-lock-extend-region-functions)
-  (make-local-variable 'font-lock-maximum-size)
+  (make-local-variable 'font-lock-end)
   (make-local-variable 'font-lock-support-mode)
+  (make-local-variable 'font-lock-unfontify-region-function)
   (make-local-variable 'imenu-case-fold-search)
   (make-local-variable 'imenu-create-index-function)
   (make-local-variable 'imenu-generic-expression)
   (make-local-variable 'indent-line-function)
-
-  (setq fill-paragraph-function 'web-mode-fill-paragraph
+  (make-local-variable 'parse-sexp-lookup-properties)
+  (make-local-variable 'uncomment-region-function)
+  (make-local-variable 'yank-excluded-properties)
+
+  (setq comment-end "-->"
+        comment-region-function 'web-mode-comment-or-uncomment-region
+        comment-start "<!--"
+        fill-paragraph-function 'web-mode-fill-paragraph
         font-lock-defaults '(web-mode-font-lock-keywords t)
-        font-lock-extend-region-functions '(web-mode-font-lock-extend-region)
+        font-lock-extend-region-functions '(web-mode-extend-region)
         font-lock-support-mode nil
-        font-lock-maximum-size nil
-        ;;        font-lock-fontify-buffer-function 'web-mode-scan-buffer
-        ;;        font-lock-unfontify-buffer-function 'web-mode-scan-buffer
+        font-lock-unfontify-region-function 'web-mode-unfontify-region
         imenu-case-fold-search t
         imenu-create-index-function 'web-mode-imenu-index
-        indent-line-function 'web-mode-indent-line)
-
-;;  (remove-hook 'after-change-functions 'font-lock-after-change-function t)
-
-  (if (and (fboundp 'global-hl-line-mode)
-           global-hl-line-mode)
-      (setq web-mode-hl-line-mode-flag t))
-
-  (when web-mode-enable-current-element-highlight
-    (add-hook 'post-command-hook 'web-mode-highlight-current-element nil t))
-
-  (add-hook 'after-change-functions 'web-mode-on-after-change t t)
+        indent-line-function 'web-mode-indent-line
+        parse-sexp-lookup-properties t
+        yank-excluded-properties t
+        uncomment-region-function 'web-mode-comment-or-uncomment-region)
+
+  (substitute-key-definition 'indent-new-comment-line
+                             'web-mode-comment-indent-new-line
+                             web-mode-map global-map)
+
+  (add-hook 'after-change-functions 'web-mode-on-after-change nil t)
+  (add-hook 'after-save-hook        'web-mode-on-after-save t t)
   (add-hook 'change-major-mode-hook 'web-mode-on-exit nil t)
-
-  (add-hook 'after-save-hook
-            '(lambda ()
-               (when web-mode-is-scratch
-                 (web-mode-guess-engine-and-content-type)
-                 (web-mode-scan-buffer)
-;;                 (message "-->%S" (buffer-file-name))
-                 )
-               nil)
-            t t)
+  (add-hook 'post-command-hook      'web-mode-on-post-command nil t)
 
   (cond
    ((boundp 'yas-after-exit-snippet-hook)
@@ -2052,329 +2349,81 @@
               t t))
    )
 
-  (when web-mode-enable-whitespaces
+  (when web-mode-enable-whitespace-fontification
     (web-mode-whitespaces-on))
 
+  (when web-mode-enable-sexp-functions
+    (setq-local forward-sexp-function 'web-mode-forward-sexp))
+
   (web-mode-guess-engine-and-content-type)
-;;  (web-mode-scan-buffer)
-
-  (web-mode-scan-region (point-min) (point-max))
+  (setq web-mode-change-beg (point-min)
+        web-mode-change-end (point-max))
+  (when (> (point-max) 256000)
+    (web-mode-buffer-highlight))
+
+  (when (and (boundp 'hs-special-modes-alist)
+             (not (assoc major-mode hs-special-modes-alist)))
+    (add-to-list 'hs-special-modes-alist '(web-mode "{" "}" "/[*/]" web-mode-forward-sexp nil))
+    ) ;when
 
   )
 
-;; (add-hook 'web-mode-hook
-;;           (lambda()
-;;             (let (mode modes found)
-;;               (setq modes '(esk-add-watchwords
-;;                             esk-pretty-lambdas
-;;                             fic-ext-mode
-;;                             fic-mode
-;;                             global-whitespace-mode
-;;                             idle-highlight-mode
-;;                             rainbow-mode
-;;                             whitespace-mode))
-;; ;;              (message "==> %S" column-number-mode)
-;; ;;              (when (and (boundp 'global-whitespace-mode) global-whitespace-mode)
-;; ;;                (message "==> %S" global-whitespace-mode))
-;;               (dolist (mode modes)
-;; ;;                (message "> %S (%S) has been disabled" mode (symbol-value mode))
-;;                 (when (and (boundp mode) (symbol-value mode))
-;;                   (message "=> %S (%S) has been disabled" mode (symbol-value mode))
-;;                   (setq found t)
-;;                   (funcall mode -1))
-;;                 ) ;dolist
-;;               (when found
-;;                 (web-mode-scan-buffer)
-;;                 )
-;;               )))
-
-(defun web-mode-yasnippet-exit-hook ()
-  "Yasnippet exit hook"
-  (when (and (boundp 'yas-snippet-beg) (boundp 'yas-snippet-end))
-;;    (web-mode-highlight-region yas-snippet-beg yas-snippet-end)
-    (indent-region yas-snippet-beg yas-snippet-end)))
-
-(defun web-mode-forward-sexp (&optional arg)
-  "Move forward."
-  (interactive "p")
-  (unless arg (setq arg 1))
-  (cond
-   ((> arg 0)
-    (while
-        (progn
-          (web-mode-tag-next)
-          (> (setq arg (1- arg)) 0))))
-   ((< arg 0)
-    (while
-        (progn
-          (web-mode-tag-previous)
-          (< (setq arg (1+ arg)) 0))))
-   ))
-
-(defun web-mode-set-engine (engine)
-  "set engine"
-  (interactive
-   (list (completing-read
-          "Engine: "
-          (let (engines elt)
-            (dolist (elt web-mode-engines)
-              (setq engines (append engines (list (car elt)))))
-            engines))))
-  (setq web-mode-content-type "html"
-        web-mode-engine engine)
-  (web-mode-on-engine-setted)
-  (web-mode-scan-buffer))
-
-(defun web-mode-on-engine-setted ()
-  "engine setted"
-  (let (elt elts engines)
-;;    (when (string= web-mode-engine "razor") (setq web-mode-enable-block-face t))
-    (cond
-     ((member web-mode-content-type '("css" "javascript" "json"))
-      (setq web-mode-has-any-large-part t))
-     ((member web-mode-content-type '("php"))
-      (setq web-mode-has-any-large-block nil))
-     ) ;cond
-
-    (setq web-mode-electric-chars nil)
-    (when (string= web-mode-content-type "html")
-      (unless (string= web-mode-engine "none")
-        (setq web-mode-active-block-regexp
-              (cdr (assoc web-mode-engine web-mode-active-block-regexps)))
-        (setq web-mode-close-block-regexp
-              (cdr (assoc web-mode-engine web-mode-close-block-regexps)))
-        (setq web-mode-engine-control-matcher
-              (intern-soft (concat "web-mode-match-" web-mode-engine "-block")))
-        )
-      (setq web-mode-electric-chars
-            (append '(?\<)
-                    (cdr (assoc web-mode-engine web-mode-block-electric-chars)))
-            )
-      ) ;when
-
-    (setq elt (assoc web-mode-engine web-mode-block-regexps))
-    (if elt
-        (setq web-mode-block-regexp (cdr elt))
-      (setq web-mode-engine "none"))
-
-    (unless (boundp 'web-mode-extra-auto-pairs)
-      (setq web-mode-extra-auto-pairs nil))
-
-    (setq web-mode-auto-pairs
-          (append
-           (cdr (assoc web-mode-engine web-mode-engines-auto-pairs))
-           (cdr (assoc nil web-mode-engines-auto-pairs))
-           (cdr (assoc web-mode-engine web-mode-extra-auto-pairs))
-           (cdr (assoc nil web-mode-extra-auto-pairs))))
-
-    (unless (boundp 'web-mode-extra-snippets)
-      (setq web-mode-extra-snippets nil))
-
-    (setq elts
-          (append
-           (cdr (assoc web-mode-engine web-mode-extra-snippets))
-           (cdr (assoc nil             web-mode-extra-snippets))
-           (cdr (assoc web-mode-engine web-mode-engines-snippets))
-           (cdr (assoc nil             web-mode-engines-snippets))))
-
-    (dolist (elt elts)
-      (unless (assoc (car elt) web-mode-snippets)
-        (setq web-mode-snippets (append (list elt) web-mode-snippets)))
-      )
-
-;;    (message "wms=%S" web-mode-snippets)
-
-    (setq web-mode-closing-blocks (cdr (assoc web-mode-engine web-mode-engines-closing-blocks)))
-
-    (setq web-mode-engine-font-lock-keywords
-          (symbol-value (cdr (assoc web-mode-engine web-mode-engines-font-lock-keywords))))
-
-;;    (message "%S" (symbol-value (cdr (assoc web-mode-engine web-mode-engines-font-lock-keywords))))
-
-    ;;  (message "%S\n%S\n%S\n%S" web-mode-active-block-regexp web-mode-close-block-regexp web-mode-engine-control-matcher web-mode-electric-chars )
-
-    ))
-
-(defun web-mode-guess-engine-and-content-type ()
-  "Try to guess the server engine and the content type."
-  (let (buff-name elt found)
-    (setq buff-name (buffer-file-name))
-    (unless buff-name (setq buff-name (buffer-name)))
-    (setq web-mode-is-scratch (string= buff-name "*scratch*"))
-    (setq web-mode-content-type nil)
-
-    (when (boundp 'web-mode-content-types-alist)
-      (setq found nil)
-      (dolist (elt web-mode-content-types-alist)
-        (when (and (not found) (string-match-p (cdr elt) buff-name))
-          (setq web-mode-content-type (car elt)
-                found t))
-        )
-      )
-    (unless web-mode-content-type
-      (setq found nil)
-      (dolist (elt web-mode-content-types)
-        (when (and (not found) (string-match-p (cdr elt) buff-name))
-          (setq web-mode-content-type (car elt)
-                found t))
-        )
-      )
-    (when (boundp 'web-mode-engines-alist)
-      (setq found nil)
-      (dolist (elt web-mode-engines-alist)
-        (cond
-         ((stringp (cdr elt))
-          (when (string-match-p (cdr elt) buff-name)
-            (setq web-mode-engine (car elt))))
-         ((functionp (cdr elt))
-          (when (funcall (cdr elt))
-            (setq web-mode-engine (car elt))))
-         ) ;cond
-        ) ;dolist
-      ) ;when
-    (unless web-mode-engine
-      (setq found nil)
-      (dolist (elt web-mode-engine-file-regexps)
-;;          (message "%S %S" (cdr elt) buff-name)
-        (when (and (not found) (string-match-p (cdr elt) buff-name))
-          (setq web-mode-engine (car elt)
-                found t))
-        )
-      )
-    (when web-mode-engine
-      (setq found nil)
-      (dolist (elt web-mode-engines)
-        (when (and (not found) (member web-mode-engine (cdr elt)))
-          (setq web-mode-engine (car elt)
-                found t))
-        )
-      )
-    (when (and (null found)
-               (string-match-p "php" (buffer-substring-no-properties
-                                      (line-beginning-position)
-                                      (line-end-position))))
-      (setq web-mode-engine "php"
-            found t)
-      )
-
-;;    (message "engine=%S" web-mode-engine)
-
-    (web-mode-on-engine-setted)
-    ))
-
-(defun web-mode-imenu-index ()
-  "Return a table of contents."
-  (let (toc-index)
-    (save-excursion
-      (goto-char (point-min))
-      (while (re-search-forward "<h\\([1-9]\\)\\([^>]*\\)>\\([^<]*\\)" nil t)
-	(setq toc-index
-	      (cons (cons (concat (make-string
-				   (* 2 (1- (string-to-number (match-string 1))))
-				   ?\s)
-				  (match-string 3))
-			  (line-beginning-position))
-		    toc-index))))
-    (nreverse toc-index)))
-
-(defun web-mode-scan-buffer ()
-  "Scan entine buffer."
-  (interactive)
-  (web-mode-scan-region (point-min) (point-max))
-  (font-lock-fontify-buffer))
-
-;;(syntax-propertize)
+;;---- DEFUNS ------------------------------------------------------------------
 
 (defun web-mode-scan-region (beg end &optional content-type)
   "Identify nodes/parts/blocks and syntactic symbols (strings/comments)."
-  (interactive)
-
-;;  (message "lexing buffer from %d to %d" beg end)
+  ;;(message "scan-region: beg(%d) end(%d) content-type(%S)" beg end content-type)
   (web-mode-with-silent-modifications
    (save-excursion
      (save-restriction
        (save-match-data
-         (let ((inhibit-modification-hooks t)
-               (inhibit-point-motion-hooks t)
+         (let ((inhibit-point-motion-hooks t)
                (inhibit-quit t))
-           (setq beg (if web-mode-is-narrowed 1 beg))
-           (remove-text-properties beg end web-mode-scan-properties)
+           (remove-list-of-text-properties beg end web-mode-scan-properties)
            (cond
-            ((member web-mode-content-type '("javascript" "json" "css"))
-             (web-mode-scan-blocks beg end)
-             (web-mode-scan-part beg end)
+            ((and content-type (string= content-type "php"))
+;;             (web-mode-block-scan beg end)
              )
-            ((string= web-mode-engine "none")
-             (web-mode-scan-tags beg end)
-             (web-mode-scan-parts beg end)
-             )
-            ;; ((and content-type (member content-type '("css")))
-            ;;  (remove-text-properties beg end web-mode-scan-properties2)
-            ;;  (web-mode-scan-blocks beg end)
-            ;;  (web-mode-scan-part beg end)
-            ;;  )
-            ;; ((and content-type (member content-type '("asp")))
-            ;;  (remove-text-properties beg end '(block-token nil font nil))
-            ;;  (web-mode-scan-block beg end)
-            ;;  )
+            ((and content-type (member content-type web-mode-part-content-types))
+             (put-text-property beg end 'part-side
+                                (cond
+                                 ((string= content-type "javascript") 'javascript)
+                                 ((string= content-type "json") 'json)
+                                 ((string= content-type "jsx") 'jsx)
+                                 ((string= content-type "css") 'css)
+                                 ))
+             (web-mode-scan-blocks beg end)
+             (web-mode-part-scan beg end content-type))
+            ((member web-mode-content-type web-mode-part-content-types)
+             (web-mode-scan-blocks beg end)
+             (web-mode-part-scan beg end))
+            ((string= web-mode-engine "riot")
+             (web-mode-scan-elements beg end)
+             (web-mode-scan-blocks beg end)
+             (web-mode-process-parts beg end 'web-mode-part-scan))
             (t
              (web-mode-scan-blocks beg end)
-             (web-mode-scan-tags beg end)
-             (web-mode-scan-parts beg end)
-             )
+             (web-mode-scan-elements beg end)
+             (web-mode-process-parts beg end 'web-mode-part-scan))
             ) ;cond
-           (web-mode-trace "web-mode-scan-region")
-           ))))))
-
-;;(defvar web-mode-aa t)
-
-(defun web-mode-highlight-region (beg end &optional content-type)
-  "Identify code blocks (clientside and serverside) and syntactic symbols (strings/comments)."
-  (interactive)
-  (web-mode-with-silent-modifications
-   (save-excursion
-     (save-restriction
-       (save-match-data
-         (let ((inhibit-modification-hooks t)
-               (inhibit-point-motion-hooks t)
-               (inhibit-quit t))
-           (setq beg (if web-mode-is-narrowed 1 beg))
-           (remove-text-properties beg end '(font-lock-face nil))
-           (cond
-            ((member web-mode-content-type '("javascript" "json" "css"))
-             (web-mode-highlight-part beg end)
-             (web-mode-highlight-blocks beg end))
-            ((string= web-mode-engine "none")
-             (web-mode-highlight-tags beg end)
-             (web-mode-highlight-parts beg end))
-            ;; ((and content-type (member content-type '("css")))
-            ;;  (web-mode-highlight-part beg end)
-            ;;  (web-mode-highlight-blocks beg end))
-            ;; ((and content-type (member content-type '("asp")))
-            ;;  (web-mode-highlight-block beg end)
-            ;;  )
-            (t
-             (web-mode-highlight-tags beg end)
-             (web-mode-highlight-parts beg end)
-             (web-mode-highlight-blocks beg end))
-            ) ;cond
-           (when web-mode-enable-whitespaces
-             (web-mode-highlight-whitespaces beg end))
+           (cons beg end)
            ))))))
 
 (defun web-mode-scan-blocks (reg-beg reg-end)
   "Identifies blocks (with block-side, block-beg, block-end text properties)."
   (save-excursion
 
-    (let ((i 0) open close closing-string start sub1 sub2 pos tagopen l tmp delim-open delim-close)
+    (let ((i 0) open close closing-string start sub1 sub2 pos tagopen tmp delim-open delim-close part-beg part-end tagclose)
 
       (goto-char reg-beg)
 
-      ;;     (message "%S: %Sx%S" (point) reg-beg reg-end)
-      ;;      (message "regexp=%S" web-mode-block-regexp)
-      (while (and (< i 1200)
+      ;;(message "%S: %Sx%S" (point) reg-beg reg-end)
+      ;;(message "regexp=%S" web-mode-block-regexp)
+      (while (and (< i 2000)
                   (> reg-end (point))
-                  (re-search-forward web-mode-block-regexp reg-end t))
+                  web-mode-block-regexp
+                  (re-search-forward web-mode-block-regexp reg-end t)
+                  (not (eobp)))
 
         (setq i (1+ i)
               closing-string nil
@@ -2384,34 +2433,46 @@
               delim-open nil
               delim-close nil
               pos nil)
-        (setq l (length tagopen))
-
-        (when (member (string-to-char tagopen) '(?\s ?\t))
-          (setq tagopen (replace-regexp-in-string "\\`[ \t]*" "" tagopen))
-;;          (message "tagopen=%s (%S)" tagopen (point))
-          (setq open (+ open (- l (length tagopen))))
-          (setq l (length tagopen))
-          )
-
-        (setq sub1 (substring tagopen 0 1)
-              sub2 (substring tagopen 0 (if (>= l 2) 2 1)))
-
+
+        (let ((l (length tagopen)))
+          (when (member (string-to-char tagopen) '(?\s ?\t))
+            (setq tagopen (replace-regexp-in-string "\\`[ \t]*" "" tagopen))
+            (setq open (+ open (- l (length tagopen))))
+            (setq l (length tagopen))
+            )
+          (setq sub1 (substring tagopen 0 1)
+                sub2 (substring tagopen 0 (if (>= l 2) 2 1)))
+          )
+        ;;(message " found block #(%S) at pos=(%S), part-type=(%S)" i open (get-text-property open 'part-side))
         (cond
 
          ((string= web-mode-engine "php")
-          (unless (member (char-after (point)) '(?\x ?\X)) ;; (looking-at-p "xml ")
+          (unless (member (char-after) '(?x ?X))
             (setq closing-string '("<\\?". "\\?>")))
           (cond
-           ((eq (char-after (+ (point) 2)) ?\=)
+           ((looking-at-p "<?php")
+            (setq delim-open "<?php"))
+           ((eq (char-after) ?\=)
             (setq delim-open "<?="))
-           ((eq (char-after (+ (point) 2)) ?p)
-            (setq delim-open "<?php"))
            (t
             (setq delim-open "<?"))
            ) ;cond
           (setq delim-close "?>")
           ) ;php
 
+         ((string= web-mode-engine "erb")
+          (cond
+           ((string= sub2 "<%")
+            (setq closing-string '("<%". "%>")
+                  delim-open "<%[=-]?"
+                  delim-close "[-]?%>")
+            )
+           (t
+            (setq closing-string "EOL"
+                  delim-open "%"))
+           )
+          ) ;erb
+
          ((string= web-mode-engine "django")
           (cond
            ((string= sub2 "{{")
@@ -2420,80 +2481,201 @@
                   delim-close "}}"))
            ((string= sub2 "{%")
             (setq closing-string "%}"
-                  delim-open "{%"
-                  delim-close "%}"))
+                  delim-open "{%[+-]?"
+                  delim-close "[-]?%}"))
            (t
             (setq closing-string "#}"))
            )
           ) ;django
 
-         ((string= web-mode-engine "erb")
+         ((string= web-mode-engine "ejs")
+          (setq closing-string "%>"
+                delim-open "<%[=-]?"
+                delim-close "[-]?%>")
+          ) ;ejs
+
+         ((string= web-mode-engine "lsp")
+          (setq closing-string "%>"
+                delim-open "<%[%#]?"
+                delim-close "%>")
+          ) ;lsp
+
+         ((string= web-mode-engine "mako")
           (cond
-           ((string= sub2 "<%")
-            (setq closing-string "%>")
-            (cond
-             ((eq (char-before) ?\=) (setq delim-open "<%="))
-             ((eq (char-before) ?\-) (setq delim-open "<%-"))
-             (t                      (setq delim-open "<%"))
-             ) ;cond
-;;            (message "%S %S" (point) delim-open)
-            (setq delim-close "%>")
-            )
+           ((and (string= tagopen "<%")
+                 (member (char-after) '(?\s ?\n ?\!)))
+            (setq closing-string "%>"
+                  delim-open "<%[!]?"
+                  delim-close "%>"))
+           ((member sub2 '("<%" "</"))
+            (setq closing-string ">"
+                  delim-open "</?%"
+                  delim-close "/?>"))
+           ((string= sub2 "${")
+            (setq closing-string "}"
+                  delim-open "${"
+                  delim-close "}"))
            (t
             (setq closing-string "EOL"
                   delim-open "%"))
            )
-          ) ;erb
-
-         ((string= web-mode-engine "mako")
+          ) ;mako
+
+         ((string= web-mode-engine "cl-emb")
           (cond
-           ((member tagopen '("<% " "<%!"))
+           ((string= tagopen "<%#")
+            (setq closing-string "#%>"))
+           ((string= sub2 "<%")
+            (setq closing-string "%>"
+                  delim-open "<%[=%]?"
+                  delim-close "%>"))
+           )
+          ) ;cl-emb
+
+         ((string= web-mode-engine "elixir")
+          (cond
+           ((string= tagopen "<%#")
             (setq closing-string "%>"))
-           ((member sub2 '("<%" "</"))
-            (setq closing-string ">"))
-           ((string= sub2 "${")
-            (setq closing-string "}"))
+           ((string= sub2 "<%")
+            (setq closing-string "%>"
+                  delim-open "<%[=%]?"
+                  delim-close "%>"))
+           )
+          ) ;elixir
+
+         ((string= web-mode-engine "mojolicious")
+          (cond
+           ((string= tagopen "<%#")
+            (setq closing-string "%>"))
+           ((string= sub2 "<%")
+            (setq closing-string "%>"
+                  delim-open "<%\\(==\\|[=%]\\)?"
+                  delim-close "%>"))
+           ((string= sub2 "%#")
+            (setq closing-string "EOL"))
            (t
-            (setq closing-string "EOL"))
+            (setq closing-string "EOL"
+                  delim-open "%\\(==\\|[=%]\\)?"))
            )
-          ) ;mako
+          ) ;mojolicious
 
          ((string= web-mode-engine "ctemplate")
-          (setq closing-string "}}")
+          (cond
+           ((member tagopen '("{{{" "{{~"))
+            (setq closing-string "}~?}}"
+                  delim-open "{{~?{"
+                  delim-close "}~?}}")
+            )
+           ((string= tagopen "{~{")
+            (setq closing-string "}~?}"
+                  delim-open "{~{"
+                  delim-close "}~?}")
+            )
+           ((string= tagopen "{{!")
+            (setq closing-string (if (looking-at-p "--") "--}}" "}}"))
+            )
+           ((string= sub2 "{{")
+            (setq closing-string "}~?}"
+                  delim-open "{{[>#/%^&]?"
+                  delim-close "}~?}"))
+           (t
+            (setq closing-string "}}"
+                  delim-open "${{"
+                  delim-close "}}"))
+           )
           ) ;ctemplate
 
-         ((or (string= web-mode-engine "asp")
-              (string= web-mode-engine "aspx"))
-          (setq closing-string "%>")
+         ((string= web-mode-engine "aspx")
+          (setq closing-string "%>"
+                delim-open "<%[:=#@$]?"
+                delim-close "%>")
+          ) ;aspx
+
+         ((string= web-mode-engine "asp")
+          (cond
+           ((string= sub2 "<%")
+            (setq closing-string "%>"
+                  delim-open "<%[:=#@$]?"
+                  delim-close "%>"))
+           (t
+            (setq closing-string ">"
+                  delim-open "</?"
+                  delim-close "/?>"))
+           )
           ) ;asp
 
+         ((string= web-mode-engine "jsp")
+          (cond
+           ((looking-at-p "--")
+            (setq closing-string "--%>"))
+           ((string= sub2 "<%")
+            (setq closing-string "%>"
+                  delim-open "<%\\([!=@]\\|#=\\)?"
+                  delim-close "[-]?%>"))
+           ((string= sub2 "${")
+            (setq closing-string "}"
+                  delim-open "${"
+                  delim-close "}"))
+           )
+          ) ;jsp
+
+         ((string= web-mode-engine "clip")
+          (setq closing-string ">"
+                delim-open "</?"
+                delim-close "/?>")
+          ) ;clip
+
          ((string= web-mode-engine "blade")
           (cond
            ((string= tagopen "{{-")
             (setq closing-string "--}}"))
+           ((string= tagopen "{!!")
+            (setq closing-string "!!}"
+                  delim-open "{!!"
+                  delim-close "!!}"))
+           ((string= tagopen "@{{")
+            (setq closing-string nil))
+           ((string= tagopen "{{{")
+            (setq closing-string "}}}"
+                  delim-open "{{{"
+                  delim-close "}}}"))
            ((string= sub2 "{{")
-            (setq closing-string "}}"))
+            (setq closing-string "}}"
+                  delim-open "{{"
+                  delim-close "}}"))
+           ((looking-at-p "[[:alnum:]]+\\.[[:alpha:]]+")
+            )
+           ((string= sub2 "@y")
+            (setq closing-string ")"
+                  delim-open "@"))
            ((string= sub1 "@")
-            (setq closing-string "EOL"))
+            (setq closing-string "EOL"
+                  delim-open "@"))
            )
           ) ;blade
 
          ((string= web-mode-engine "smarty")
-;;          (message "l=%S tagopen=%S" l tagopen)
-          (setq l (length (web-mode-engine-delimiter-open web-mode-engine "{")))
-          (setq tagopen (substring-no-properties tagopen 0 l))
           (cond
-           ((string= tagopen (concat (web-mode-engine-delimiter-open web-mode-engine "{") "*"))
-            (setq closing-string (concat "*" (web-mode-engine-delimiter-close web-mode-engine "}"))))
-           ((string= tagopen (concat (web-mode-engine-delimiter-open web-mode-engine "{") "#"))
-            (setq closing-string (concat "#" (web-mode-engine-delimiter-close web-mode-engine "}"))))
+           ((string= tagopen "{*")
+            (setq closing-string "*}")
+            )
+           ((string= tagopen "{#")
+            (setq closing-string "#}"
+                  delim-open "{#"
+                  delim-close "#}")
+            )
            (t
-            (setq closing-string (web-mode-engine-delimiter-close web-mode-engine "}")))
-           )
+            (setq closing-string (cons "{" "}")
+                  delim-open "{/?"
+                  delim-close "}")
+            ) ;t
+           ) ;cond
           ) ;smarty
 
          ((string= web-mode-engine "web2py")
-          (setq closing-string "}}")
+          (setq closing-string "}}"
+                delim-open "{{[=]?"
+                delim-close "}}")
           ) ;web2py
 
          ((string= web-mode-engine "dust")
@@ -2501,7 +2683,9 @@
            ((string= sub2 "{!")
             (setq closing-string "!}"))
            (t
-            (setq closing-string "}")
+            (setq closing-string '("{". "}") ;;closing-string "}"
+                  delim-open "{[#/:?@><+^]?"
+                  delim-close "/?}")
             )
            )
           ) ;dust
@@ -2515,77 +2699,93 @@
             (setq closing-string "*/")
             )
            (t
-            (setq closing-string "}")
+            (setq closing-string "}"
+                  delim-open "{/?"
+                  delim-close "/?}")
             )
            )
           ) ;closure
 
-         ((string= web-mode-engine "ctemplate")
-          (cond
-           ((string= tagopen "{{{")
-            (setq closing-string "}}}"))
-           (t
-            (setq closing-string "}}"))
-           )
-          ) ;ctemplate
-
          ((string= web-mode-engine "go")
-          (setq closing-string "}}")
+          (setq closing-string "}}"
+                delim-open "{{"
+                delim-close "}}")
           ) ;go
 
          ((string= web-mode-engine "angular")
-          (setq closing-string "}}")
+          (setq closing-string "}}"
+                delim-open "{{"
+                delim-close "}}")
           ) ;angular
 
          ((string= web-mode-engine "mason")
           (cond
-           ((member tagopen '("<%def" "</%def" "<%method" "</%method"))
-            (setq closing-string ">")
-            )
-           ((> (length tagopen) 4)
-            (setq closing-string (concat "</" (substring tagopen 1 (length tagopen)) ">"))
+           ((and (member sub2 '("<%" "</"))
+                 (looking-at "[[:alpha:]]+"))
+            (if (member (match-string-no-properties 0) '("after" "around" "augment" "before" "def" "filter" "method" "override"))
+                (setq closing-string ">"
+                      delim-open "<[/]?%"
+                      delim-close ">")
+              (setq closing-string (concat "</%" (match-string-no-properties 0) ">")
+                    delim-open "<[^>]+>"
+                    delim-close "<[^>]+>")
+              ) ;if
             )
-           ((string= sub2 "<%")
-            (setq closing-string "%>"))
+           ((and (string= sub2 "<%")
+                 (eq (char-after) ?\s))
+            (setq closing-string "%>"
+                  delim-open "<%"
+                  delim-close "%>"))
+           ((string= tagopen "</&")
+            (setq closing-string ">"
+                  delim-open "</&"
+                  delim-close ">")
+            )
            ((string= sub2 "<&")
-            (setq closing-string "&>"))
+            (setq closing-string "&>"
+                  delim-open "<&[|]?"
+                  delim-close "&>"))
            (t
-            (setq closing-string "EOL"))
+            (setq closing-string "EOL"
+                  delim-open "%"))
            )
           ) ;mason
 
-         ((string= web-mode-engine "jsp")
-          (cond
-           ((string= sub2 "<%")
-            (setq closing-string "%>"))
-           ((string= sub2 "${")
-            (setq closing-string "}"))
-           (t
-            (setq closing-string ">"))
-           )
-          ) ;jsp
-
          ((string= web-mode-engine "underscore")
-          (setq closing-string "%>")
+          (setq closing-string "%>"
+                delim-open "<%"
+                delim-close "%>")
           ) ;underscore
 
          ((string= web-mode-engine "template-toolkit")
           (cond
-           ((string= sub2 "[#")
-            (setq closing-string "#]"))
+           ((string= tagopen "%%#")
+            (setq closing-string "EOL"))
+           ((string= tagopen "[%#")
+            (setq closing-string "%]"))
            (t
-            (setq closing-string "%]"))
+            (setq closing-string "%]"
+                  delim-open "\\[%[-+]?"
+                  delim-close "[-=+]?%\\]"))
            )
-          ) ;underscore
+          ) ;template-toolkit
 
          ((string= web-mode-engine "freemarker")
           (cond
+           ((and (string= sub2 "<#") (eq (char-after) ?\-))
+            (setq closing-string "-->"))
            ((string= sub1 "<")
-            (setq closing-string ">"))
+            (setq closing-string ">"
+                  delim-open "</?[#@]"
+                  delim-close "/?>"))
            ((string= sub1 "[")
-            (setq closing-string "]"))
+            (setq closing-string "]"
+                  delim-open "\\[/?[#@]"
+                  delim-close "/?\\]"))
            (t
-            (setq closing-string "}"))
+            (setq closing-string "}"
+                  delim-open "${"
+                  delim-close "}"))
            )
           ) ;freemarker
 
@@ -2596,7 +2796,8 @@
            ((string= sub2 "#*")
             (setq closing-string "*#"))
            (t
-            (setq closing-string "EOV"))
+            (setq closing-string "EOV"
+                  delim-open "#"))
            )
           ) ;velocity
 
@@ -2608,16 +2809,39 @@
            ((string= sub2 "@*")
             (setq closing-string "*@"))
            ((string= sub1 "@")
-            (setq closing-string "EOR"))
+            (setq closing-string "EOR"
+                  delim-open "@"))
+           ((and (string= sub1 "}")
+                 (looking-at-p "[ ]*\n"))
+            (setq closing-string "EOC")
+            )
            ((string= sub1 "}")
-            (setq closing-string "EOR"))
-           )
+            ;;(message "%s: %s" (point) sub1)
+            (save-excursion
+              (let (paren-pos)
+                (setq paren-pos (web-mode-part-opening-paren-position (1- (point))))
+                (if (and paren-pos (get-text-property paren-pos 'block-side))
+                    (setq closing-string "EOR")
+                  (setq closing-string nil)
+                  ) ;if
+                ) ;let
+              ) ;let
+            ) ;case }
+           ) ;cond
           ) ;razor
 
-         ((string= web-mode-engine "python")
-          (unless (looking-at-p "xml ")
-            (setq closing-string "?>"))
-          ) ;python
+         ((and (string= web-mode-engine "riot")
+               (not (get-text-property open 'part-side)))
+          (setq closing-string "}"
+                delim-open "{"
+                delim-close "}")
+          ) ;riot
+
+         ((string= web-mode-engine "marko")
+          (setq closing-string "}"
+                delim-open "${"
+                delim-close "}")
+          ) ;marko
 
          ) ;cond
 
@@ -2626,22 +2850,33 @@
           (cond
 
            ((listp closing-string)
-            (if (web-mode-rsf-balanced (car closing-string) (cdr closing-string) reg-end t)
-                (setq close (match-end 0)
-                      pos (point))
-              (when (and (string= web-mode-engine "php")
-                         (string= "<?" sub2))
+            (cond
+             ((web-mode-rsf-balanced (car closing-string) (cdr closing-string) reg-end t)
+              (setq close (match-end 0)
+                    pos (point))
+              )
+             ((and (string= web-mode-engine "php")
+                   (string= "<?" sub2))
+
+              (if (or (text-property-not-all (1+ open) (point-max) 'tag-beg nil)
+                      (text-property-not-all (1+ open) (point-max) 'block-beg nil)
+                      (looking-at-p "[ \t\n]*<"))
+                  (setq close nil
+                        delim-close nil
+                        pos (point))
                 (setq close (point-max)
                       delim-close nil
-                      pos (point-max)))
-              ) ;if
-            )
+                      pos (point-max))
+                ) ;if
+              ) ;case
+             ) ;cond
+            ) ;case listp
 
            ((and (string= web-mode-engine "smarty")
-                 (string= closing-string (web-mode-engine-delimiter-close web-mode-engine "}")))
+                 (string= closing-string "}"))
             (goto-char open)
             (setq tmp (web-mode-closing-delimiter-position
-                       (web-mode-engine-delimiter-close web-mode-engine "}")
+                       "}"
                        (point)
                        (line-end-position)))
             (if tmp
@@ -2652,7 +2887,7 @@
                   pos (point))
             )
 
-           ((and (member web-mode-engine '("closure" "dust"))
+           ((and (member web-mode-engine '("closure"))
                  (string= closing-string "}"))
             (goto-char open)
             (setq tmp (web-mode-closing-paren-position (point) (line-end-position)))
@@ -2669,40 +2904,81 @@
             (setq close (point)
                   pos (point)))
 
-           ((string= closing-string "EOR")
-            (web-mode-razor-skip-forward open)
+           ((string= closing-string "EOC")
             (setq close (point)
                   pos (point)))
 
+           ((string= closing-string "EOR")
+            (web-mode-razor-skip open)
+            (setq close (if (> (point) reg-end) reg-end (point))
+                  pos (if (> (point) reg-end) reg-end (point)))
+            (goto-char pos))
+
            ((string= closing-string "EOV")
-            (web-mode-velocity-skip-forward open)
+            (web-mode-velocity-skip open)
             (setq close (point)
                   pos (point)))
 
+           ((and (member web-mode-engine '("ctemplate"))
+                 (re-search-forward closing-string reg-end t))
+            (setq close (match-end 0)
+                  pos (point)))
+
            ((search-forward closing-string reg-end t)
             (setq close (match-end 0)
-                  pos (point))
-            (cond
-             ((and (string= web-mode-engine "erb")
-                   (eq (char-before (match-beginning 0)) ?\-))
-              (setq delim-close (concat "-" delim-close))
-              )
-             ) ;cond
-            )
-
+                  pos (point)))
            ) ;cond
 
           (when (and close (>= reg-end pos))
             ;;(message "pos(%S) : open(%S) close(%S)" pos open close)
+            (put-text-property open (1+ open) 'block-beg 0)
+            (put-text-property open (1+ open) 'block-controls 0)
             (put-text-property open close 'block-side t)
-            (put-text-property open (1+ open) 'block-beg 0)
             (put-text-property (1- close) close 'block-end t)
             (when delim-open
               (web-mode-block-delimiters-set open close delim-open delim-close))
-            (if (string= web-mode-engine "razor")
-                (web-mode-razor-tag-exclude open close)
-              (web-mode-scan-block open close))
-            )
+            (web-mode-block-scan open close)
+            (cond
+             ((and (string= web-mode-engine "erb")
+                   (looking-at-p "<%= javascript_tag do %>"))
+              (setq tagopen "<%= javascript_tag do %>"))
+             ((and (string= web-mode-engine "mako")
+                   (looking-at-p "<%block filter=\"collect_js\">"))
+              (setq tagopen "<%block filter=\"collect_js\">"))
+             ((and (string= web-mode-engine "mako")
+                   (looking-at-p "<%block filter=\"collect_css\">"))
+              (setq tagopen "<%block filter=\"collect_css\">"))
+             )
+            ;;(message "%S %s" (point) tagopen)
+            (when (and (member tagopen '("<r:script" "<r:style"
+                                         "<c:js" "<c:css"
+                                         "<%= javascript_tag do %>"
+                                         "<%block filter=\"collect_js\">"
+                                         "<%block filter=\"collect_css\">"))
+                       (setq part-beg close)
+                       (setq tagclose
+                             (cond
+                              ((string= tagopen "<r:script") "</r:script")
+                              ((string= tagopen "<r:style") "</r:style")
+                              ((string= tagopen "<c:js") "</c:js")
+                              ((string= tagopen "<c:css") "</c:css")
+                              ((string= tagopen "<%= javascript_tag do %>") "<% end %>")
+                              ((member tagopen '("<%block filter=\"collect_js\">"
+                                                 "<%block filter=\"collect_css\">")) "</%block")
+                              ))
+                       (web-mode-sf tagclose)
+                       (setq part-end (match-beginning 0))
+                       (> part-end part-beg))
+              (put-text-property part-beg part-end
+                                 'part-side
+                                 (cond
+                                  ((member tagopen '("<r:style" "<c:css" "<%block filter=\"collect_css\">")) 'css)
+                                  (t 'javascript)))
+              (setq pos part-beg
+                    part-beg nil
+                    part-end nil)
+              ) ;when
+            ) ;when close
 
           (if pos (goto-char pos))
 
@@ -2711,145 +2987,157 @@
         ) ;while
 
       (cond
-       ((>= i 1200)
-        (message "** strange loop (web-mode-scan-blocks) **"))
+       ((>= i 2000)
+        (message "scan-blocks ** warning (%S) **" i))
        ((string= web-mode-engine "razor")
-        (web-mode-process-blocks reg-beg reg-end "scan"))
+        (web-mode-process-blocks reg-beg reg-end 'web-mode-block-scan))
        ((string= web-mode-engine "django")
-        (web-mode-scan-django-block-comments reg-beg reg-end))
+        (web-mode-scan-engine-comments reg-beg reg-end
+                                       "{% comment %}" "{% endcomment %}"))
        ((string= web-mode-engine "mako")
-        (web-mode-scan-mako-block-comments reg-beg reg-end))
+        (web-mode-scan-engine-comments reg-beg reg-end
+                                       "<%doc>" "</%doc>"))
+       ((string= web-mode-engine "mason")
+        (web-mode-scan-engine-comments reg-beg reg-end
+                                       "<%doc>" "</%doc>"))
        ) ;cond
 
       )))
 
 (defun web-mode-block-delimiters-set (reg-beg reg-end delim-open delim-close)
-  "Set text-property 'block-token to 'delimiter on block delimiters (e.g. <?php ?>)"
-
-;;  (message "reg-beg(%S) reg-end(%S) delim-open(%S) delim-close(%S)" reg-beg reg-end delim-open delim-close)
-
-  (put-text-property reg-beg (+ reg-beg (length delim-open)) 'block-token 'delimiter)
+  "Set text-property 'block-token to 'delimiter-(beg|end) on block delimiters (e.g. <?php ?>)"
+  ;;(message "reg-beg(%S) reg-end(%S) delim-open(%S) delim-close(%S)" reg-beg reg-end delim-open delim-close)
+  (when (member web-mode-engine
+                '("asp" "aspx" "cl-emb" "clip" "closure" "ctemplate" "django" "dust"
+                  "elixir" "ejs" "erb" "freemarker" "jsp" "lsp" "mako" "mason" "mojolicious"
+                  "smarty" "template-toolkit" "web2py"))
+    (save-excursion
+      (when delim-open
+        (goto-char reg-beg)
+        (looking-at delim-open)
+        (setq delim-open (match-string-no-properties 0)))
+      (when delim-close
+        (goto-char reg-end)
+        (looking-back delim-close reg-beg t)
+        (setq delim-close (match-string-no-properties 0)))
+      ))
+  (when delim-open
+    (put-text-property reg-beg (+ reg-beg (length delim-open))
+                       'block-token 'delimiter-beg))
   (when delim-close
-;;    (message "%S > %S" (- reg-end (length delim-close)) reg-end)
-    (put-text-property (- reg-end (length delim-close)) reg-end 'block-token 'delimiter)
-    ))
-
-(defun web-mode-highlight-blocks (reg-beg reg-end)
-  "Highlight blocks."
-  (web-mode-process-blocks reg-beg reg-end "highlight"))
-
-;;todo : passer en funcall
-(defun web-mode-process-blocks (reg-beg reg-end type)
-  "Process blocks. The scan relies on the 'block-beg and 'block-end text-properties."
+    (put-text-property (- reg-end (length delim-close)) reg-end
+                       'block-token 'delimiter-end))
+  )
+
+(defun web-mode-process-blocks (reg-beg reg-end func)
   (let ((i 0) (continue t) (block-beg reg-beg) (block-end nil))
     (while continue
-      (setq block-end nil
-            i (1+ i))
+      (setq block-end nil)
       (unless (get-text-property block-beg 'block-beg)
         (setq block-beg (web-mode-block-next-position block-beg)))
       (when (and block-beg (< block-beg reg-end))
         (setq block-end (web-mode-block-end-position block-beg)))
       (cond
-       ((or (null block-end) (> block-end reg-end) (> i 1200))
-        (setq continue nil)
-        (if (> i 1200) (message "*** invalid loop (web-mode-process-blocks) ***")))
+       ((> (setq i (1+ i)) 2000)
+        (message "process-blocks ** warning (%S) **" (point))
+        (setq continue nil))
+       ((or (null block-end) (> block-end reg-end))
+        (setq continue nil))
        (t
         (setq block-end (1+ block-end))
-        (cond
-         ((string= type "scan")
-          (web-mode-scan-block block-beg block-end))
-         (t
-          (web-mode-highlight-block block-beg block-end))
-         )
+        (funcall func block-beg block-end)
         (setq block-beg block-end)
-        )
+        ) ;t
        ) ;cond
       ) ;while
     ))
 
-(defun web-mode-scan-parts (reg-beg reg-end)
-  "Scan parts."
-  (web-mode-process-parts reg-beg reg-end "scan"))
-
-(defun web-mode-highlight-parts (reg-beg reg-end)
-  "Highlight parts."
-  (web-mode-process-parts reg-beg reg-end "highlight"))
-
-;;todo : passer en funcall
-(defun web-mode-process-parts (reg-beg reg-end type)
-  "Process parts. The scan relies on the 'part-beg and 'part-end text-properties."
+(defun web-mode-process-parts (reg-beg reg-end func)
   (let ((i 0) (continue t) (part-beg reg-beg) (part-end nil))
     (while continue
-      (setq part-end nil
-            i (1+ i))
+      (setq part-end nil)
       (unless (get-text-property part-beg 'part-side)
         (setq part-beg (web-mode-part-next-position part-beg)))
       (when (and part-beg (< part-beg reg-end))
         (setq part-end (web-mode-part-end-position part-beg)))
       (cond
-       ((or (null part-end) (> part-end reg-end) (> i 1200))
-        (setq continue nil)
-        (if (> i 1200) (message "*** invalid loop (web-mode-process-parts) ***")))
+       ((> (setq i (1+ i)) 100)
+        (message "process-parts ** warning (%S) **" (point))
+        (setq continue nil))
+       ((or (null part-end) (> part-end reg-end))
+        (setq continue nil))
        (t
         (setq part-end (1+ part-end))
-        (cond
-         ((string= type "scan")
-          (web-mode-scan-part part-beg part-end))
-         (t
-          (web-mode-highlight-part part-beg part-end))
-         )
+        (funcall func part-beg part-end)
         (setq part-beg part-end)
         )
        ) ;cond
       ) ;while
     ))
 
-(defun web-mode-scan-block (reg-beg reg-end)
-  "Scan a block."
-  (let (sub1 sub2 sub3 regexp props continue beg match char (flags 0))
-
-    ;;(message "beg=%S end=%S" reg-beg reg-end)
-    ;;(remove-text-properties reg-beg reg-end web-mode-scan-properties)
-
-    (when (and (not web-mode-has-any-large-block)
-               (> (- reg-end reg-beg) web-mode-large-embed-threshold))
-      (setq web-mode-has-any-large-block t))
-
-    (goto-char reg-beg)
-
-    (setq sub1 (buffer-substring-no-properties reg-beg (+ reg-beg 1))
-          sub2 (buffer-substring-no-properties reg-beg (+ reg-beg 2))
-          sub3 (buffer-substring-no-properties reg-beg (+ reg-beg (if (>= (point-max) (+ reg-beg 3)) 3 2))))
+(defun web-mode-block-scan (block-beg block-end)
+  (let (sub1 sub2 sub3 regexp token-type)
+
+    ;;(message "block-beg=%S block-end=%S" block-beg block-end)
+    ;;(remove-text-properties block-beg block-end web-mode-scan-properties)
+
+    (goto-char block-beg)
 
     (cond
-
-     ((string= web-mode-engine "php")
-      (setq regexp "//\\|/\\*\\|\"\\|'\\|<<<['\"]?\\([[:alnum:]]+\\)['\"]?")
-      (setq flags (logior flags 1))
-      ) ;php
+     ((>= (point-max) (+ block-beg 3))
+      (setq sub3 (buffer-substring-no-properties block-beg (+ block-beg 3))
+            sub2 (buffer-substring-no-properties block-beg (+ block-beg 2))
+            sub1 (buffer-substring-no-properties block-beg (+ block-beg 1)))
+      )
+     ((>= (point-max) (+ block-beg 2))
+      (setq sub3 (buffer-substring-no-properties block-beg (+ block-beg 2))
+            sub2 (buffer-substring-no-properties block-beg (+ block-beg 2))
+            sub1 (buffer-substring-no-properties block-beg (+ block-beg 1)))
+      )
+     (t
+      (setq sub1 (buffer-substring-no-properties block-beg (+ block-beg 1)))
+      (setq sub2 sub1
+            sub3 sub1)
+      )
+     )
+
+    (cond
+
+     ((member web-mode-engine '("php" "lsp" "python" "web2py" "mason"))
+      (setq regexp web-mode-engine-token-regexp))
+
+     ((string= web-mode-engine "mako")
+      (cond
+       ((string= sub2 "##")
+        (setq token-type 'comment)
+        )
+       (t
+        (setq regexp web-mode-engine-token-regexp))
+       )
+      ) ;mako
 
      ((string= web-mode-engine "django")
       (cond
        ((member sub2 '("{{" "{%"))
         (setq regexp "\"\\|'"))
        ((string= sub2 "{#")
-        (setq props '(block-token comment)))
+        (setq token-type 'comment))
        )
       ) ;django
 
      ((string= web-mode-engine "ctemplate")
       (cond
-       ((string= sub3 "{{%")
-        (setq regexp "\"\\|'"))
        ((string= sub3 "{{!")
-        (setq props '(block-token comment)))
+        (setq token-type 'comment))
+       ((member sub2 '("{{"))
+        )
        )
       ) ;ctemplate
 
      ((string= web-mode-engine "go")
       (cond
        ((string= sub3 "{{/")
-        (setq props '(block-token comment)))
+        (setq token-type 'comment))
        ((string= sub2 "{{")
         (setq regexp "\"\\|'"))
        )
@@ -2858,40 +3146,53 @@
      ((string= web-mode-engine "razor")
       (cond
        ((string= sub2 "@*")
-        (setq props '(block-token comment)))
+        (setq token-type 'comment))
        (t
-        (setq regexp "//\\|\"\\|'"))
+        (setq regexp "//\\|@\\*\\|\"\\|'"))
        )
       ) ;razor
 
-     ((string= web-mode-engine "mako")
-      (setq regexp "\"\\|'\\|#")
-      ) ;mako
-
-     ((string= web-mode-engine "python")
-      (setq regexp "\"\\|'\\|#")
-      ) ;python
-
-     ((string= web-mode-engine "web2py")
-      (setq regexp "\"\\|'")
-      ) ;web2py
-
      ((string= web-mode-engine "blade")
       (cond
        ((string= sub3 "{{-")
-        (setq props '(block-token comment)))
+        (setq token-type 'comment))
        (t
         (setq regexp "\"\\|'"))
        )
       ) ;blade
 
+     ((string= web-mode-engine "cl-emb")
+      (cond
+       ((string= sub3 "<%#")
+        (setq token-type 'comment))
+       (t
+        (setq regexp "\"\\|'"))
+       )
+      ) ;cl-emb
+
+     ((string= web-mode-engine "elixir")
+      (cond
+       ((string= sub3 "<%#")
+        (setq token-type 'comment))
+       (t
+        (setq regexp "\"\\|'"))
+       )
+      ) ;elixir
+
+     ((string= web-mode-engine "mojolicious")
+      (cond
+       ((or (string= sub2 "%#") (string= sub3 "<%#"))
+        (setq token-type 'comment))
+       (t
+        (setq regexp "\"\\|'"))
+       )
+      ) ;mojolicious
+
      ((string= web-mode-engine "velocity")
       (cond
        ((member sub2 '("##" "#*"))
-        (setq props '(block-token comment)))
-       ((string= sub1 "$")
-        (setq regexp "\"\\|'"))
-       ((string= sub1 "#")
+        (setq token-type 'comment))
+       ((member sub1 '("$" "#"))
         (setq regexp "\"\\|'"))
        )
       ) ;velocity
@@ -2899,7 +3200,7 @@
      ((string= web-mode-engine "jsp")
       (cond
        ((string= sub3 "<%-")
-        (setq props '(block-token comment)))
+        (setq token-type 'comment))
        ((string= sub3 "<%@")
         (setq regexp "/\\*"))
        ((member sub2 '("${" "#{"))
@@ -2909,39 +3210,53 @@
        )
       ) ;jsp
 
+     ((string= web-mode-engine "clip")
+      (setq regexp nil)
+      ) ;clip
+
+     ((and (string= web-mode-engine "asp")
+           (string= sub2 "<%"))
+      (setq regexp "//\\|/\\*\\|\"\\|'")
+      ) ;asp
+
+     ((string= web-mode-engine "aspx")
+      (cond
+       ((string= sub3 "<%-")
+        (setq token-type 'comment))
+       ((string= sub3 "<%@")
+        (setq regexp "/\\*"))
+       ((string= sub3 "<%$")
+        (setq regexp "\"\\|'"))
+       (t
+        (setq regexp "//\\|/\\*\\|\"\\|'"))
+       )
+      ) ;aspx
+
      ((string= web-mode-engine "freemarker")
       (cond
        ((member sub3 '("<#-" "[#-"))
-        (setq props '(block-token comment)))
+        (setq token-type 'comment))
        ((member sub2 '("${" "#{"))
         (setq regexp "\"\\|'"))
        ((or (member sub2 '("<@" "[@" "<#" "[#"))
             (member sub3 '("</@" "[/@" "</#" "[/#")))
-        (setq regexp "\""))
+        (setq regexp "\"\\|'"))
        )
       ) ;freemarker
 
-     ((string= web-mode-engine "erb")
+     ((member web-mode-engine '("ejs" "erb"))
       (cond
        ((string= sub3 "<%#")
-        (setq props '(block-token comment)))
+        (setq token-type 'comment))
        (t
-        (setq regexp "\"\\|'\\|#\\|<<[-]?['\"]?\\([[:alnum:]_]+\\)['\"]?"))
+        (setq regexp web-mode-engine-token-regexp))
        )
       ) ;erb
 
-     ((string= web-mode-engine "mason")
-      (setq regexp "\"\\|'\\|#")
-      ) ;mason
-
-     ((string= web-mode-engine "asp")
-      (setq regexp "//\\|/\\*\\|\"\\|'")
-      ) ;asp
-
      ((string= web-mode-engine "template-toolkit")
       (cond
-       ((string= sub2 "[#")
-        (setq props '(block-token comment)))
+       ((member sub3 '("[%#" "%%#"))
+        (setq token-type 'comment))
        (t
         (setq regexp "#\\|\"\\|'"))
        )
@@ -2954,23 +3269,10 @@
      ((string= web-mode-engine "angular")
       ) ;angular
 
-     ((string= web-mode-engine "aspx")
-      (cond
-       ((string= sub3 "<%-")
-        (setq props '(block-token comment)))
-       ((string= sub3 "<%@")
-        (setq regexp "/\\*"))
-       ((string= sub3 "<%$")
-        (setq regexp "\"\\|'"))
-       (t
-        (setq regexp "//\\|/\\*\\|\"\\|'"))
-       )
-      ) ;aspx
-
      ((string= web-mode-engine "smarty")
       (cond
-       ((string= sub2 (concat (web-mode-engine-delimiter-open web-mode-engine "{") "*"))
-        (setq props '(block-token comment)))
+       ((string= sub2 "{*")
+        (setq token-type 'comment))
        (t
         (setq regexp "\"\\|'")))
       ) ;smarty
@@ -2978,7 +3280,7 @@
      ((string= web-mode-engine "dust")
       (cond
        ((string= sub2 "{!")
-        (setq props '(block-token comment)))
+        (setq token-type 'comment))
        (t
         (setq regexp "\"\\|'"))
        )
@@ -2987,7 +3289,7 @@
      ((string= web-mode-engine "closure")
       (cond
        ((member sub2 '("/*" "//"))
-        (setq props '(block-token comment)))
+        (setq token-type 'comment))
        (t
         (setq regexp "\"\\|'"))
        )
@@ -2995,100 +3297,2129 @@
 
      ) ;cond
 
-    (when props (add-text-properties reg-beg reg-end props))
-
-    (when regexp
+    (cond
+     (token-type
+      (put-text-property block-beg block-end 'block-token token-type))
+     ((and regexp
+           (> (- block-end block-beg) 6))
+      (web-mode-block-tokenize
+       (web-mode-block-code-beginning-position block-beg)
+       (web-mode-block-code-end-position block-beg)
+       regexp)
+      )
+     ) ;cond
+
+    ))
+
+(defun web-mode-block-tokenize (reg-beg reg-end &optional regexp)
+  (unless regexp (setq regexp web-mode-engine-token-regexp))
+  ;;(message "tokenize: reg-beg(%S) reg-end(%S) regexp(%S)" reg-beg reg-end regexp)
+  ;;(message "tokenize: reg-beg(%S) reg-end(%S) command(%S)" reg-beg reg-end this-command)
+  ;;(message "%S>%S : %S" reg-beg reg-end (buffer-substring-no-properties reg-beg reg-end))
+  (save-excursion
+    (let ((pos reg-beg) beg end char match continue (flags 0) token-type token-end)
+
+      (remove-list-of-text-properties reg-beg reg-end '(block-token))
+
+      ;; TODO : vérifier la cohérence
+      (put-text-property reg-beg reg-end 'block-side t)
 
       (goto-char reg-beg)
 
-      (while (re-search-forward regexp reg-end t)
-
+      (when (> reg-beg reg-end)
+        (message "block-tokenize ** reg-beg(%S) reg-end(%S) **" reg-beg reg-end))
+
+      (while (and (< reg-beg reg-end) (re-search-forward regexp reg-end t))
         (setq beg (match-beginning 0)
               match (match-string 0)
               continue t
-              flags (logior flags 1))
-
-        (setq char (aref match 0))
-
+              token-type 'comment
+              token-end (if (< reg-end (line-end-position)) reg-end (line-end-position))
+              char (aref match 0))
         (cond
 
          ((and (string= web-mode-engine "asp")
                (eq char ?\'))
-          (setq props '(block-token comment))
-          (goto-char (if (< reg-end (line-end-position)) reg-end (line-end-position)))
-          )
+          (goto-char token-end))
 
          ((eq char ?\')
-          (setq props '(block-token string))
+          (setq token-type 'string)
           (while (and continue (search-forward "'" reg-end t))
             (if (looking-back "\\\\+'" reg-beg t)
                 (setq continue (= (mod (- (point) (match-beginning 0)) 2) 0))
-              (setq continue nil))
-            )
-          )
+              (setq continue nil))))
 
          ((eq char ?\")
-          (setq props '(block-token string))
+          (setq token-type 'string)
           (while (and continue (search-forward "\"" reg-end t))
             (if (looking-back "\\\\+\"" reg-beg t)
                 (setq continue (= (mod (- (point) (match-beginning 0)) 2) 0))
-              (setq continue nil))
-            )
-          )
+              (setq continue nil))))
 
          ((string= match "//")
-          (setq props '(block-token comment))
-          (goto-char (if (< reg-end (line-end-position)) reg-end (line-end-position)))
-          )
+          (goto-char token-end))
+
+         ((eq char ?\;)
+          (goto-char token-end))
+
+         ((string= match "#|")
+          (unless (search-forward "|#" reg-end t)
+            (goto-char token-end)))
 
          ((eq char ?\#)
-          (setq props '(block-token comment))
-          (goto-char (if (< reg-end (line-end-position)) reg-end (line-end-position)))
-          )
+          (goto-char token-end))
 
          ((string= match "/*")
-          (setq props '(block-token comment))
-          (search-forward "*/" reg-end t)
-          )
+          (unless (search-forward "*/" reg-end t)
+            (goto-char token-end))
+          )
+
+         ((string= match "@*")
+          (unless (search-forward "*@" reg-end t)
+            (goto-char token-end)))
 
          ((eq char ?\<)
-          (when (and web-mode-enable-heredoc-fontification
-                     (string-match-p "JS\\|JAVASCRIPT\\|HTM\\|CSS" (match-string 1)))
-            (setq flags (logior flags 2))
-;;            (message "%S flags=%S" (point) flags)
+          (setq token-type 'string)
+          (re-search-forward (concat "^[ ]*" (match-string 1)) reg-end t))
+
+         (t
+          (message "block-tokenize ** token end (%S) **" beg)
+          (setq token-type nil))
+
+         ) ;cond
+
+        ;;(when (eq token-type 'comment) (message "comment: %S %S" beg (point)))
+
+        (put-text-property beg (point) 'block-token token-type)
+
+        (when (eq token-type 'comment)
+          (put-text-property beg (1+ beg) 'syntax-table (string-to-syntax "<"))
+          (if (or (< (point) (line-end-position)) (= (point) (point-max)))
+              (put-text-property (1- (point)) (point) 'syntax-table (string-to-syntax ">")) ;#445 #480
+            (put-text-property (point) (1+ (point)) 'syntax-table (string-to-syntax ">")) ;#377
+            )
+          )
+
+        ) ;while
+
+      (web-mode-block-controls-unset pos)
+
+      )))
+
+(defun web-mode-set-php-controls (reg-beg reg-end)
+  (goto-char reg-beg)
+  (let (match controls
+        (continue t)
+        (regexp "endif\\|endforeach\\|endfor\\|endwhile\\|elseif\\|else\\|if\\|foreach\\|for\\|while"))
+    (while continue
+      (if (not (web-mode-block-rsf regexp reg-end))
+          (setq continue nil)
+        (setq match (match-string-no-properties 0))
+;;        (message "%S %S" match (point))
+        (cond
+         ((and (member match '("else" "elseif"))
+               (looking-at-p "[ ]*[:(]"))
+          (setq controls (append controls (list (cons 'inside "if"))))
+          )
+         ((and (>= (length match) 3)
+               (string= (substring match 0 3) "end"))
+          (setq controls (append controls (list (cons 'close (substring match 3)))))
+          )
+         ((and (progn (skip-chars-forward "[ ]") t)
+               (eq (char-after) ?\()
+               (web-mode-closing-paren reg-end)
+               ;;(progn (message "ixi%S" (point)))
+               (looking-at-p ")[ ]*:"))
+          (setq controls (append controls (list (cons 'open match))))
+          )
+         ) ;cond
+        ) ;if
+      ) ;while
+    ;;(message "%S-%S %S" reg-beg reg-end controls)
+    (when (and controls (> (length controls) 1))
+      (setq controls (web-mode-block-controls-reduce controls)))
+    controls))
+
+(defun web-mode-block-controls-reduce (controls)
+  (when (and (eq (car (car controls)) 'open)
+             (member (cons 'close (cdr (car controls))) controls))
+    (setq controls nil))
+  controls)
+
+(defun web-mode-block-controls-unset (pos)
+  (cond
+   ((null (get-text-property pos 'block-side))
+    (message "block-controls-unset ** invalid value (%S) **" pos))
+   ((or (get-text-property pos 'block-beg)
+        (setq pos (web-mode-block-beginning-position pos)))
+    (put-text-property pos (1+ pos) 'block-controls 0))
+   (t
+    (message "block-controls-unset ** failure (%S) **" (point)))
+   ))
+
+(defun web-mode-block-controls-get (pos)
+  (web-mode-with-silent-modifications
+   (let ((controls nil))
+     (cond
+      ((null (get-text-property pos 'block-side))
+       (message "block-controls-get ** invalid value (%S) **" pos))
+      ((or (get-text-property pos 'block-beg)
+           (setq pos (web-mode-block-beginning-position pos)))
+       (setq controls (get-text-property pos 'block-controls))
+       (when (integerp controls)
+         (web-mode-block-controls-set pos (web-mode-block-end-position pos))
+         (setq controls (get-text-property pos 'block-controls))
+         )
+       )
+      (t
+       (message "block-controls-get ** failure (%S) **" (point)))
+      ) ;cond
+     controls)))
+
+(defun web-mode-block-controls-set (reg-beg reg-end)
+  (save-excursion
+    (goto-char reg-beg)
+    (let (controls pos type control)
+
+      (cond
+
+       ((null web-mode-engine)
+        (message "block-controls-set ** unknown engine (%S) **" web-mode-engine)
+        )
+
+       ((string= web-mode-engine "php")
+        (setq controls (web-mode-set-php-controls reg-beg reg-end))
+        (when (web-mode-block-starts-with "}" reg-beg)
+          (setq controls (append controls (list (cons 'close "{")))))
+        (when (web-mode-block-ends-with (cons "{" "}") reg-beg)
+          (setq controls (append controls (list (cons 'open "{")))))
+        ) ;php
+
+       ((string= web-mode-engine "ejs")
+        (cond
+         ((web-mode-block-ends-with "}[ ]*else[ ]*{" reg-beg)
+          (setq controls (append controls (list (cons 'inside "{")))))
+         ((web-mode-block-starts-with "}" reg-beg)
+          (setq controls (append controls (list (cons 'close "{")))))
+         ((web-mode-block-ends-with "{" reg-beg)
+          (setq controls (append controls (list (cons 'open "{")))))
+         )
+        ) ;ejs
+
+       ((string= web-mode-engine "erb")
+        (cond
+         ((web-mode-block-starts-with "else\\|elsif\\|when" reg-beg)
+          (setq controls (append controls (list (cons 'inside "ctrl")))))
+         ((web-mode-block-starts-with "end" reg-beg)
+          (setq controls (append controls (list (cons 'close "ctrl")))))
+         ((web-mode-block-ends-with " do\\( |.*|\\)?" reg-beg)
+          (setq controls (append controls (list (cons 'open "ctrl")))))
+         ((and (web-mode-block-starts-with "\\(for\\|if\\|unless\\|case\\)\\_>" reg-beg)
+               (not (web-mode-block-ends-with "end" reg-end)))
+          (setq controls (append controls (list (cons 'open "ctrl")))))
+         )
+        ) ;erb
+
+       ((string= web-mode-engine "django")
+        (when (eq (char-after (1+ reg-beg)) ?\%)
+          (cond
+           ((and (string= web-mode-minor-engine "jinja") ;#504
+                 (web-mode-block-starts-with "else\\_>" reg-beg))
+            (let ((continue t)
+                  (pos reg-beg)
+                  (ctrl nil))
+              (while continue
+                (cond
+                 ((null (setq pos (web-mode-block-control-previous-position 'open pos)))
+                  (setq continue nil))
+                 ((member (setq ctrl (cdr (car (get-text-property pos 'block-controls)))) '("if" "ifequal" "ifnotequal" "for"))
+                  (setq continue nil)
+                  )
+                 ) ;cond
+                )
+              (setq controls (append controls (list (cons 'inside (or ctrl "if")))))
+              )
+            )
+           ((web-mode-block-starts-with "\\(else\\|els?if\\)" reg-beg)
+            (let ((continue t)
+                  (pos reg-beg)
+                  (ctrl nil))
+              (while continue
+                (cond
+                 ((null (setq pos (web-mode-block-control-previous-position 'open pos)))
+                  (setq continue nil))
+                 ((member (setq ctrl (cdr (car (get-text-property pos 'block-controls)))) '("if" "ifequal" "ifnotequal"))
+                  (setq continue nil)
+                  )
+                 ) ;cond
+                ) ;while
+              (setq controls (append controls (list (cons 'inside (or ctrl "if")))))
+              ) ;let
+            ) ;case else
+           ((web-mode-block-starts-with "\\(empty\\)" reg-beg)
+            (setq controls (append controls (list (cons 'inside "for")))))
+           ((web-mode-block-starts-with "end\\([[:alpha:]]+\\)" reg-beg)
+            (setq controls (append controls (list (cons 'close (match-string-no-properties 1))))))
+           ((web-mode-block-starts-with (concat web-mode-django-control-blocks-regexp "[ %]") reg-beg)
+            (let (control)
+              (setq control (match-string-no-properties 1))
+              ;;(message "%S %S %S" control (concat "end" control) web-mode-django-control-blocks)
+              (when (member (concat "end" control) web-mode-django-control-blocks)
+                (setq controls (append controls (list (cons 'open control)))))
+              ) ;let
+            ) ;case
+           ) ;cond
+          ) ;when
+        ) ;django
+
+       ((string= web-mode-engine "smarty")
+        (cond
+         ((and (eq (char-after (1+ reg-beg)) ?\/)
+               (web-mode-block-starts-with "\\([[:alpha:]]+\\)" reg-beg))
+          (setq controls (append controls (list (cons 'close (match-string-no-properties 1))))))
+         ((web-mode-block-starts-with "\\(else\\|elseif\\)" reg-beg)
+          (setq controls (append controls (list (cons 'inside "if")))))
+         ((web-mode-block-starts-with "\\(block\\|foreach\\|for\\|if\\|section\\|while\\)")
+          (setq controls (append controls (list (cons 'open (match-string-no-properties 1))))))
+         )
+        ) ;smarty
+
+       ((string= web-mode-engine "web2py")
+        (cond
+         ((web-mode-block-starts-with "def" reg-beg)
+          (setq controls (append controls (list (cons 'open "def")))))
+         ((web-mode-block-starts-with "return" reg-beg)
+          (setq controls (append controls (list (cons 'close "def")))))
+         ((web-mode-block-starts-with "block" reg-beg)
+          (setq controls (append controls (list (cons 'open "block")))))
+         ((web-mode-block-starts-with "end" reg-beg)
+          (setq controls (append controls (list (cons 'close "block")))))
+         ((web-mode-block-starts-with "pass" reg-beg)
+          (setq controls (append controls (list (cons 'close "ctrl")))))
+         ((web-mode-block-starts-with "\\(except\\|finally\\|els\\)" reg-beg)
+          (setq controls (append controls (list (cons 'inside "ctrl")))))
+         ((web-mode-block-starts-with "\\(if\\|for\\|try\\|while\\)")
+          (setq controls (append controls (list (cons 'open "ctrl")))))
+         )
+        ) ;web2py
+
+       ((string= web-mode-engine "dust")
+        (cond
+         ((eq (char-after (1- reg-end)) ?\/)
+          )
+         ((eq (char-after (1+ reg-beg)) ?\:)
+          (setq pos (web-mode-block-control-previous-position 'open reg-beg))
+          (when pos
+            (setq controls (append controls
+                                   (list
+                                    (cons 'inside
+                                          (cdr (car (web-mode-block-controls-get pos))))))))
+          )
+         ((looking-at "{/\\([[:alpha:].]+\\)")
+          (setq controls (append controls (list (cons 'close (match-string-no-properties 1))))))
+         ((looking-at "{[#?@><+^]\\([[:alpha:].]+\\)")
+          (setq controls (append controls (list (cons 'open (match-string-no-properties 1))))))
+         )
+        ) ;dust
+
+       ((member web-mode-engine '("mojolicious"))
+        (cond
+         ((web-mode-block-ends-with "begin" reg-beg)
+          (setq controls (append controls (list (cons 'open "begin")))))
+         ((web-mode-block-starts-with "end" reg-beg)
+          (setq controls (append controls (list (cons 'close "begin")))))
+         ((web-mode-block-starts-with "}[ ]*else[ ]*{" reg-beg)
+          (setq controls (append controls (list (cons 'inside "{")))))
+         ((web-mode-block-starts-with "}" reg-beg)
+          (setq controls (append controls (list (cons 'close "{")))))
+         ((web-mode-block-ends-with "{" reg-beg)
+          (setq controls (append controls (list (cons 'open "{")))))
+         )
+        ) ;mojolicious
+
+       ((member web-mode-engine '("aspx" "underscore"))
+        (cond
+         ((web-mode-block-starts-with "}" reg-beg)
+          (setq controls (append controls (list (cons 'close "{")))))
+         ((web-mode-block-ends-with "{" reg-beg)
+          (setq controls (append controls (list (cons 'open "{")))))
+         )
+        ) ;aspx underscore
+
+       ((member web-mode-engine '("jsp" "asp" "clip"))
+        (cond
+         ((eq (char-after (1- reg-end)) ?\/)
+          )
+         ((looking-at "</?\\([[:alpha:]]+\\(?:[:.][[:alpha:]]+\\)\\|[[:alpha:]]+Template\\)")
+          (setq control (match-string-no-properties 1)
+                type (if (eq (aref (match-string-no-properties 0) 1) ?\/) 'close 'open))
+          (when (not (member control '("h:inputtext" "jsp:usebean" "jsp:forward" "struts:property")))
+            (setq controls (append controls (list (cons type control)))))
+          )
+         (t
+          (when (web-mode-block-starts-with "}" reg-beg)
+            (setq controls (append controls (list (cons 'close "{")))))
+          (when (web-mode-block-ends-with "{" reg-beg)
+            (setq controls (append controls (list (cons 'open "{")))))
+          )
+         )
+        ) ;jsp asp
+
+       ((string= web-mode-engine "mako")
+        (cond
+         ((looking-at "</?%\\([[:alpha:]]+\\(?:[:][[:alpha:]]+\\)?\\)")
+          (cond
+           ((eq (char-after (- (web-mode-block-end-position reg-beg) 1)) ?\/)
+            )
+           (t
+            (setq control (match-string-no-properties 1)
+                  type (if (eq (aref (match-string-no-properties 0) 1) ?\/) 'close 'open))
+            (setq controls (append controls (list (cons type control)))))
+           )
+          )
+         ((web-mode-block-starts-with "\\(else\\|elif\\)" reg-beg)
+          (setq controls (append controls (list (cons 'inside "if")))))
+         ((web-mode-block-starts-with "end\\(if\\|for\\)" reg-beg)
+          (setq controls (append controls (list (cons 'close (match-string-no-properties 1))))))
+         ((and (web-mode-block-starts-with "if\\|for" reg-beg)
+               (web-mode-block-ends-with ":" reg-beg))
+          (setq controls (append controls (list (cons 'open (match-string-no-properties 0))))))
+         )
+        ) ;mako
+
+       ((string= web-mode-engine "mason")
+        (cond
+         ((looking-at "</?%\\(after\\|around\\|augment\\|before\\|def\\|filter\\|method\\|override\\)")
+          (setq control (match-string-no-properties 1)
+                type (if (eq (aref (match-string-no-properties 0) 1) ?\/) 'close 'open))
+          (setq controls (append controls (list (cons type control))))
+          )
+         ) ;mason
+        )
+
+       ((string= web-mode-engine "ctemplate")
+        (cond
+         ((looking-at-p "{{else") ;#721
+          (let ((continue t)
+                (pos reg-beg)
+                (ctrl nil))
+            (while continue
+              (cond
+               ((null (setq pos (web-mode-block-control-previous-position 'open pos)))
+                (setq continue nil))
+               ((member (setq ctrl (cdr (car (get-text-property pos 'block-controls)))) '("if" "each"))
+                (setq continue nil)
+                )
+               ) ;cond
+              ) ;while
+            (setq controls (append controls (list (cons 'inside (or ctrl "if")))))
             )
-          (setq props '(block-token string))
-          (re-search-forward (concat "^[ ]*" (match-string 1)) reg-end t)
-          )
-
-         ((and (member web-mode-engine '("python" "erb"))
-               (eq char ?\#))
-          (setq props '(block-token comment))
-          (goto-char (if (< reg-end (line-end-position)) reg-end (line-end-position)))
+          ;;(setq controls (append controls (list (cons 'inside "if"))))
+          )
+
+         ((looking-at "{{[#^/][ ]*\\([[:alpha:]-]+\\)")
+          (setq control (match-string-no-properties 1)
+                type (if (eq (aref (match-string-no-properties 0) 2) ?\/) 'close 'open))
+          (setq controls (append controls (list (cons type control))))
+          )
+         )
+        ) ;ctemplate
+
+       ((string= web-mode-engine "blade")
+        (cond
+         ((not (eq (char-after) ?\@))
+          )
+         ((web-mode-block-starts-with
+           "section\(\s*\\(['\"]\\).*\\1\s*,\s*\\(['\"]\\).*\\2\s*\)" reg-beg)
+          )
+         ((web-mode-block-starts-with
+           "\\(?:end\\)?\\(foreach\\|forelse\\|for\\|if\\|section\\|unless\\|while\\)"
+           reg-beg)
+          (setq control (match-string-no-properties 1)
+                type (if (eq (aref (match-string-no-properties 0) 0) ?e) 'close 'open))
+          (setq controls (append controls (list (cons type control))))
+          )
+         ((web-mode-block-starts-with "stop\\|show\\|overwrite" reg-beg)
+          (setq controls (append controls (list (cons 'close "section")))))
+         ((web-mode-block-starts-with "else\\|elseif" reg-beg)
+          (setq controls (append controls (list (cons 'inside "if")))))
+         ((web-mode-block-starts-with "empty" reg-beg)
+          (setq controls (append controls (list (cons 'inside "forelse")))))
+         )
+        ) ;blade
+
+       ((string= web-mode-engine "closure")
+        (cond
+         ((eq (char-after (1- reg-end)) ?\/)
+          )
+         ((looking-at "alias\\|namespace")
+          )
+         ((web-mode-block-starts-with "ifempty" reg-beg)
+          (setq controls (append controls (list (cons 'inside "foreach")))))
+         ((web-mode-block-starts-with "else\\|elseif" reg-beg)
+          (setq controls (append controls (list (cons 'inside "if")))))
+         ((web-mode-block-starts-with "case\\|default" reg-beg)
+          (setq controls (append controls (list (cons 'inside "switch")))))
+         ((looking-at
+           "{/?\\(call\\|deltemplate\\|for\\|foreach\\|if\\|let\\|literal\\|msg\\|param\\|switch\\|template\\)")
+          (setq control (match-string-no-properties 1)
+                type (if (eq (aref (match-string-no-properties 0) 1) ?\/) 'close 'open))
+          (setq controls (append controls (list (cons type control))))
+          )
+         )
+        ) ;closure
+
+       ((string= web-mode-engine "go")
+        (cond
+         ((web-mode-block-starts-with "end\\_>" reg-beg)
+          (setq controls (append controls (list (cons 'close "ctrl")))))
+         ((web-mode-block-starts-with "else\\_>" reg-beg)
+          (setq controls (append controls (list (cons 'inside "ctrl")))))
+         ((web-mode-block-starts-with "\\(range\\|with\\|if\\)\\_>" reg-beg)
+          (setq controls (append controls (list (cons 'open "ctrl")))))
+         )
+        ) ;go
+
+       ((string= web-mode-engine "template-toolkit")
+        (cond
+         ((web-mode-block-starts-with "end" reg-beg)
+          (setq controls (append controls (list (cons 'close "ctrl")))))
+         ((web-mode-block-starts-with "els\\|catch\\|final" reg-beg)
+          (setq controls (append controls (list (cons 'inside "ctrl")))))
+         ((web-mode-block-starts-with "filter\\|foreach\\|if\\|last\\|next\\|perl\\|rawperl\\|try\\|unless\\|while" reg-beg)
+          (setq controls (append controls (list (cons 'open "ctrl")))))
+         )
+        ) ;template-toolkit
+
+       ((string= web-mode-engine "cl-emb")
+        (cond
+         ((web-mode-block-starts-with "@else" reg-beg)
+          (setq controls (append controls (list (cons 'inside "if")))))
+         ((web-mode-block-starts-with "@\\(?:end\\)?\\(if\\|unless\\|repeat\\|loop\\|with\\|genloop\\)" reg-beg)
+          (setq control (match-string-no-properties 1)
+                type (if (eq (aref (match-string-no-properties 0) 1) ?e) 'close 'open))
+          (setq controls (append controls (list (cons type control)))))
+         )
+        ) ;cl-emb
+
+       ((string= web-mode-engine "elixir")
+        (cond
+         ((web-mode-block-starts-with "end" reg-beg)
+          (setq controls (append controls (list (cons 'close "ctrl")))))
+         ((web-mode-block-starts-with "else" reg-beg)
+          (setq controls (append controls (list (cons 'inside "ctrl")))))
+         ((web-mode-block-ends-with " do" reg-beg)
+          (setq controls (append controls (list (cons 'open "ctrl")))))
+         )
+        ) ;elixir
+
+       ((string= web-mode-engine "velocity")
+        (cond
+         ((web-mode-block-starts-with "{?end" reg-beg)
+          (setq controls (append controls (list (cons 'close "ctrl")))))
+         ((web-mode-block-starts-with "{?els" reg-beg)
+          (setq controls (append controls (list (cons 'inside "ctrl")))))
+         ((web-mode-block-starts-with "{?\\(define\\|if\\|for\\|foreach\\|macro\\)" reg-beg)
+          (setq controls (append controls (list (cons 'open "ctrl")))))
+         )
+        ) ;velocity
+
+       ((string= web-mode-engine "freemarker")
+        (cond
+         ((looking-at "<#\\(import\\|assign\\|return\\|local\\)")
+          )
+         ((eq (char-after (1- reg-end)) ?\/)
+          )
+         ((looking-at "[<[]#\\(break\\|case\\|default\\)")
+          (setq controls (append controls (list (cons 'inside "switch"))))
+          )
+         ((looking-at "[<[]#els")
+          (setq controls (append controls (list (cons 'inside "if"))))
+          )
+         ((looking-at "</?\\([[:alpha:]]+\\(?:[:][[:alpha:]]+\\)?\\)")
+          (setq control (match-string-no-properties 1)
+                type (if (eq (aref (match-string-no-properties 0) 1) ?\/) 'close 'open))
+          (setq controls (append controls (list (cons type control))))
+          )
+         ((looking-at "</?\\(@\\)")
+          (setq control (match-string-no-properties 1)
+                type (if (eq (aref (match-string-no-properties 0) 1) ?\/) 'close 'open))
+          (setq controls (append controls (list (cons type control))))
+          )
+         ((looking-at "[<[]/?#\\([[:alpha:]]+\\(?:[:][[:alpha:]]+\\)?\\)")
+          (setq control (match-string-no-properties 1)
+                type (if (eq (aref (match-string-no-properties 0) 1) ?\/) 'close 'open))
+          (setq controls (append controls (list (cons type control))))
+          )
+         (t
+          (when (web-mode-block-starts-with "}" reg-beg)
+            (setq controls (append controls (list (cons 'close "{")))))
+          (when (web-mode-block-ends-with "{" reg-beg)
+            (setq controls (append controls (list (cons 'open "{")))))
+          )
+         )
+        ) ;freemarker
+
+       ((string= web-mode-engine "razor")
+        (when (web-mode-block-starts-with "}" reg-beg)
+          (setq controls (append controls (list (cons 'close "{")))))
+        (when (web-mode-block-ends-with "{" reg-beg)
+          (setq controls (append controls (list (cons 'open "{")))))
+        ) ;razor
+
+       ((string= web-mode-engine "lsp")
+        (when (web-mode-block-starts-with ")" reg-beg)
+          (setq controls (append controls (list (cons 'close "(")))))
+        (when (web-mode-block-is-opened-sexp reg-beg reg-end)
+          (setq controls (append controls (list (cons 'open "(")))))
+        ) ;lsp
+
+       ) ;cond engine
+
+      (put-text-property reg-beg (1+ reg-beg) 'block-controls controls)
+      ;;      (message "(%S) controls=%S" reg-beg controls)
+
+      )))
+
+(defun web-mode-block-is-opened-sexp (reg-beg reg-end)
+  (let ((n 0))
+    (save-excursion
+      (goto-char reg-beg)
+      (while (web-mode-block-rsf "[()]" reg-end)
+        (if (eq (char-before) ?\() (setq n (1+ n)) (setq n (1- n)))))
+    (> n 0)))
+
+(defvar web-mode-regexp1 "<\\(/?[[:alpha:]][[:alnum:]:-]*\\|!--\\|!\\[CDATA\\[\\|!doctype\\|!DOCTYPE\\|\?xml\\)")
+
+(defvar web-mode-regexp2 "<\\(/?[[:alpha:]][[:alnum:]:-]*\\|!--\\|!\\[CDATA\\[\\)")
+
+(defun web-mode-scan-elements (reg-beg reg-end)
+  (save-excursion
+    (let (part-beg part-end flags limit close-expr props tname tbeg tend element-content-type (regexp web-mode-regexp1) part-close-tag char)
+
+      (goto-char reg-beg)
+
+      (while (web-mode-dom-rsf regexp reg-end)
+
+        (setq flags 0
+              tname (downcase (match-string-no-properties 1))
+              char (aref tname 0)
+              tbeg (match-beginning 0)
+              tend nil
+              element-content-type nil
+              limit reg-end
+              part-beg nil
+              part-end nil
+              props nil
+              close-expr nil
+              part-close-tag nil)
+
+        (cond
+         ((not (member char '(?\! ?\?)))
+          (cond
+           ((string-match-p "-" tname)
+            (setq flags (logior flags 2)))
+           ((string-match-p ":" tname)
+            (setq flags (logior flags 32)))
+           )
+          (cond
+           ((eq char ?\/)
+            (setq props (list 'tag-name (substring tname 1) 'tag-type 'end)
+                  flags (logior flags 4)
+                  limit (if (> reg-end (line-end-position)) (line-end-position) reg-end))
+            )
+           ((web-mode-element-is-void tname)
+            (setq props (list 'tag-name tname 'tag-type 'void)))
+           (t
+            (setq props (list 'tag-name tname 'tag-type 'start)))
+           ) ;cond
+          )
+         ((and (eq char ?\!) (eq (aref tname 1) ?\-))
+          (setq close-expr "-->"
+                props '(tag-type comment)))
+         ((string= tname "?xml")
+          (setq regexp web-mode-regexp2
+                close-expr "?>"
+                props '(tag-type declaration)))
+         ((string= tname "![cdata[")
+          (setq close-expr "]]>"
+                props '(tag-type cdata)))
+         ((string= tname "!doctype")
+          (setq regexp web-mode-regexp2
+                props '(tag-type doctype)))
+         ) ;cond
+
+        (cond
+         ((and (null close-expr) (eq (char-after) ?\>))
+          (setq flags (logior flags 16)
+                tend (1+ (point))))
+         ((null close-expr)
+          (setq flags (logior flags (web-mode-attr-skip reg-end)))
+          (when (> (logand flags 8) 0)
+            (setq props (plist-put props 'tag-type 'void)))
+          (setq tend (point)))
+         ((web-mode-dom-sf close-expr limit t)
+          (setq tend (point)))
+         (t
+          (setq tend (line-end-position)))
+         )
+
+        (cond
+         ((string= tname "style")
+          (setq element-content-type "css"
+                part-close-tag "</style>"))
+         ((string= tname "script")
+          (let (script)
+            (setq script (buffer-substring-no-properties tbeg tend)
+                  part-close-tag "</script>")
+            (cond
+             ((string-match-p " type[ ]*=[ ]*[\"']text/\\(jsx\\|babel\\)" script)
+              (setq element-content-type "jsx"))
+             ((string-match-p " type[ ]*=[ ]*[\"']text/\\(x-handlebars\\|x-jquery-tmpl\\|html\\|ng-template\\|template\\|mustache\\)" script)
+              (setq element-content-type "html"
+                    part-close-tag nil))
+             ((string-match-p " type[ ]*=[ ]*[\"']application/\\(ld\\+json\\|json\\)" script)
+              (setq element-content-type "json"))
+             (t
+              (setq element-content-type "javascript"))
+             ) ;cond
+            ) ;let
+          ) ;script
+         )
+
+        (add-text-properties tbeg tend props)
+        (put-text-property tbeg (1+ tbeg) 'tag-beg flags)
+        (put-text-property (1- tend) tend 'tag-end t)
+
+        (when (and part-close-tag
+                   (web-mode-dom-sf part-close-tag reg-end t)
+                   (setq part-beg tend)
+                   (setq part-end (match-beginning 0))
+                   (> part-end part-beg))
+          (put-text-property part-beg part-end 'part-side
+                             (intern element-content-type web-mode-obarray))
+          (setq tend part-end)
+          ) ;when
+
+        (goto-char tend)
+
+        ) ;while
+
+      )))
+
+;; tag flags
+;; (1)attrs (2)custom (4)slash-beg (8)slash-end (16)bracket-end
+;; (32)namespaced
+
+;; attr flags
+;; (1)custom-attr (2)engine-attr (4)spread-attr[jsx] (8)code-value
+
+;; attr states
+;; (0)nil (1)space (2)name (3)space-before (4)equal (5)space-after
+;; (6)value-uq (7)value-sq (8)value-dq (9)value-bq : jsx attr={}
+
+(defun web-mode-attr-skip (limit)
+
+  (let ((tag-flags 0) (attr-flags 0) (continue t) (attrs 0) (counter 0) (brace-depth 0)
+        (pos-ori (point)) (state 0) (equal-offset 0) (go-back nil)
+        (is-jsx (or (string= web-mode-content-type "jsx") (eq (get-text-property (point) 'part-type) 'jsx)))
+        attr name-beg name-end val-beg char pos escaped spaced quoted)
+
+    (while continue
+
+      (setq pos (point)
+            char (char-after)
+            spaced (eq char ?\s))
+
+      (when quoted (setq quoted (1+ quoted)))
+
+      (cond
+
+       ((>= pos limit)
+        (setq continue nil)
+        (setq go-back t)
+        (setq attrs (+ attrs (web-mode-attr-scan state char name-beg name-end val-beg attr-flags equal-offset)))
+        )
+
+       ((or (and (= state 8) (not (member char '(?\" ?\\))))
+            (and (= state 7) (not (member char '(?\' ?\\))))
+            (and (= state 9) (not (member char '(?} ?\\))))
+            )
+        (when (and (= state 9) (eq char ?\{))
+          (setq brace-depth (1+ brace-depth)))
+        )
+
+       ((and (= state 9) (eq char ?\}) (> brace-depth 1))
+        (setq brace-depth (1- brace-depth)))
+
+       ((get-text-property pos 'block-side)
+        (when (= state 2)
+          (setq name-end pos))
+        )
+
+       ((and (= state 2) is-jsx (eq char ?\}) (eq attr-flags 4))
+        (setq name-end pos)
+        (setq attrs (+ attrs (web-mode-attr-scan state char name-beg name-end val-beg attr-flags equal-offset)))
+        (setq state 0
+              attr-flags 0
+              equal-offset 0
+              name-beg nil
+              name-end nil
+              val-beg nil)
+        )
+
+       ((or (and (= state 8) (eq ?\" char) (not escaped))
+            (and (= state 7) (eq ?\' char) (not escaped))
+            (and (= state 9) (eq ?\} char) (= brace-depth 1))
+            )
+
+        ;;(message "%S %S" (point) attr-flags)
+        (setq attrs (+ attrs (web-mode-attr-scan state char name-beg name-end val-beg attr-flags equal-offset)))
+        (setq state 0
+              attr-flags 0
+              equal-offset 0
+              name-beg nil
+              name-end nil
+              val-beg nil)
+        )
+
+       ((and (member state '(4 5)) (member char '(?\' ?\" ?\{)))
+        (setq val-beg pos)
+        (setq quoted 1)
+        (setq state (cond ((eq ?\' char) 7)
+                          ((eq ?\" char) 8)
+                          (t             9)))
+        (when (= state 9)
+          (setq brace-depth 1))
+        )
+
+       ((and (eq ?\= char) (member state '(2 3)))
+        (setq equal-offset (- pos name-beg))
+        (setq state 4)
+        (setq attr (buffer-substring-no-properties name-beg (1+ name-end)))
+        (when (and web-mode-indentless-attributes (member (downcase attr) web-mode-indentless-attributes))
+          ;;(message "onclick")
+          (setq attr-flags (logior attr-flags 8)))
+        )
+
+       ((and spaced (= state 0))
+        (setq state 1)
+        )
+
+       ((and (eq char ?\<) (not (member state '(7 8 9))))
+        (setq continue nil)
+        (setq go-back t)
+        (setq attrs (+ attrs (web-mode-attr-scan state char name-beg name-end val-beg attr-flags equal-offset)))
+        )
+
+       ((and (eq char ?\>) (not (member state '(7 8 9))))
+        (setq tag-flags (logior tag-flags 16))
+        (when (eq (char-before) ?\/)
+          (setq tag-flags (logior tag-flags 8))
+          )
+        (setq continue nil)
+        (when name-beg
+          (setq attrs (+ attrs (web-mode-attr-scan state char name-beg name-end val-beg attr-flags equal-offset))))
+        )
+
+       ((and spaced (member state '(1 3 5)))
+        )
+
+       ((and spaced (= state 2))
+        (setq state 3)
+        )
+
+       ((and (eq char ?\/) (member state '(4 5)))
+        (setq attrs (+ attrs (web-mode-attr-scan state char name-beg name-end val-beg attr-flags equal-offset)))
+        (setq state 1
+              attr-flags 0
+              equal-offset 0
+              name-beg nil
+              name-end nil
+              val-beg nil)
+        )
+
+       ((and (eq char ?\/) (member state '(0 1)))
+        )
+
+       ((and spaced (= state 4))
+        (setq state 5)
+        )
+
+       ((and (= state 3)
+             (or (and (>= char 97) (<= char 122)) ;a - z
+                 (and (>= char 65) (<= char 90)) ;A - Z
+                 (eq char ?\-)))
+        (setq attrs (+ attrs (web-mode-attr-scan state char name-beg name-end val-beg attr-flags equal-offset)))
+        (setq state 2
+              attr-flags 0
+              equal-offset 0
+              name-beg pos
+              name-end pos
+              val-beg nil)
+        )
+
+       ((and (eq char ?\n) (not (member state '(7 8 9))))
+        (setq attrs (+ attrs (web-mode-attr-scan state char name-beg name-end val-beg attr-flags equal-offset)))
+        (setq state 1
+              attr-flags 0
+              equal-offset 0
+              name-beg nil
+              name-end nil
+              val-beg nil)
+        )
+
+       ((and (= state 6) (member char '(?\s ?\n ?\/)))
+        (setq attrs (+ attrs (web-mode-attr-scan state char name-beg name-end val-beg attr-flags equal-offset)))
+        (setq state 1
+              attr-flags 0
+              equal-offset 0
+              name-beg nil
+              name-end nil
+              val-beg nil)
+        )
+
+       ((and quoted (= quoted 2) (member char '(?\s ?\n ?\>)))
+        (when (eq char ?\>)
+          (setq tag-flags (logior tag-flags 16))
+          (setq continue nil))
+        (setq state 6)
+        (setq attrs (+ attrs (web-mode-attr-scan state char name-beg name-end val-beg attr-flags equal-offset)))
+        (setq state 1
+              attr-flags 0
+              equal-offset 0
+              name-beg nil
+              name-end nil
+              val-beg nil)
+        )
+
+       ((and (not spaced) (= state 1))
+        (when (and is-jsx (eq char ?\{))
+          (setq attr-flags 4))
+        (setq state 2)
+        (setq name-beg pos
+              name-end pos)
+        )
+
+       ((member state '(4 5))
+        (setq val-beg pos)
+        (setq state 6)
+        )
+
+       ((= state 1)
+        (setq state 2)
+        )
+
+       ((= state 2)
+        (setq name-end pos)
+        (when (and (= attr-flags 0) (member char '(?\- ?\:)))
+          (let (attr)
+            (setq attr (buffer-substring-no-properties name-beg (1+ name-end)))
+            (cond
+             ((member attr '("http-equiv"))
+              (setq attr-flags (1- attr-flags))
+              )
+             ((and web-mode-engine-attr-regexp
+                   (string-match-p web-mode-engine-attr-regexp attr))
+              ;;(message "%S: %S" pos web-mode-engine-attr-regexp)
+              (setq attr-flags (logior attr-flags 2))
+              ;;(setq attr-flags (1- attr-flags))
+              )
+             ((and (eq char ?\-) (not (string= attr "http-")))
+              (setq attr-flags (logior attr-flags 1)))
+             ) ;cond
+            ) ;let
+          ) ;when attr-flags = 1
+        ) ;state=2
+
+       ) ;cond
+
+      ;;(message "point(%S) end(%S) state(%S) c(%S) name-beg(%S) name-end(%S) val-beg(%S) attr-flags(%S) equal-offset(%S)" pos end state char name-beg name-end val-beg attr-flags equal-offset)
+
+      (when (and quoted (>= quoted 2))
+        (setq quoted nil))
+
+      (setq escaped (eq ?\\ char))
+      (when (null go-back)
+        (forward-char))
+
+      ) ;while
+
+    (when (> attrs 0) (setq tag-flags (logior tag-flags 1)))
+
+    tag-flags))
+
+(defun web-mode-attr-scan (state char name-beg name-end val-beg flags equal-offset)
+;;  (message "point(%S) state(%S) c(%c) name-beg(%S) name-end(%S) val-beg(%S) flags(%S) equal-offset(%S)"
+;;           (point) state char name-beg name-end val-beg flags equal-offset)
+  (if (null flags) (setq flags 0))
+  (cond
+   ((null name-beg)
+;;    (message "name-beg is null (%S)" (point))
+    0)
+   ((or (and (= state 8) (not (eq ?\" char)))
+        (and (= state 7) (not (eq ?\' char))))
+    (put-text-property name-beg (1+ name-beg) 'tag-attr-beg flags)
+    (put-text-property name-beg val-beg 'tag-attr t)
+    (put-text-property (1- val-beg) val-beg 'tag-attr-end equal-offset)
+    1)
+   ((and (member state '(4 5)) (null val-beg))
+    (put-text-property name-beg (1+ name-beg) 'tag-attr-beg flags)
+    (put-text-property name-beg (+ name-beg equal-offset 1) 'tag-attr t)
+    (put-text-property (+ name-beg equal-offset) (+ name-beg equal-offset 1) 'tag-attr-end equal-offset)
+    1)
+   (t
+    (let (val-end)
+      (if (null val-beg)
+          (setq val-end name-end)
+        (setq val-end (point))
+        (when (or (null char) (member char '(?\s ?\n ?\> ?\/)))
+          (setq val-end (1- val-end))
+          )
+        ) ;if
+      (put-text-property name-beg (1+ name-beg) 'tag-attr-beg flags)
+      (put-text-property name-beg (1+ val-end) 'tag-attr t)
+      (put-text-property val-end (1+ val-end) 'tag-attr-end equal-offset)
+      ) ;let
+    1) ;t
+   ) ;cond
+  )
+
+(defun web-mode-part-scan (reg-beg reg-end &optional content-type depth)
+  (save-excursion
+    (let (token-re ch-before ch-at ch-next token-type beg continue)
+      ;;(message "%S %S" reg-beg reg-end)
+      (cond
+       (content-type
+        )
+       ((member web-mode-content-type web-mode-part-content-types)
+        (setq content-type web-mode-content-type))
+       (t
+        (setq content-type (symbol-name (get-text-property reg-beg 'part-side))))
+       ) ;cond
+
+      (goto-char reg-beg)
+
+      (cond
+       ((member content-type '("javascript" "json"))
+        (setq token-re "/\\|\"\\|'\\|`"))
+       ((member content-type '("jsx"))
+        (setq token-re "/\\|\"\\|'\\|`\\|</?[[:alpha:]]"))
+       ((string= web-mode-content-type "css")
+        (setq token-re "\"\\|'\\|/\\*\\|//"))
+       ((string= content-type "css")
+        (setq token-re "\"\\|'\\|/\\*"))
+       (t
+        (setq token-re "/\\*\\|\"\\|'"))
+       )
+
+      (while (and token-re (< (point) reg-end) (web-mode-dom-rsf token-re reg-end t))
+
+        (setq beg (match-beginning 0)
+              token-type nil
+              continue t
+              ch-at (char-after beg)
+              ch-next (or (char-after (1+ beg)) ?\d)
+              ch-before (or (char-before beg) ?\d))
+
+        ;;(message "[%S>%S|%S] %S %c %c %c" reg-beg reg-end depth beg ch-before ch-at ch-next)
+
+        (cond
+
+         ((eq ?\' ch-at)
+          (while (and continue (search-forward "'" reg-end t))
+            (cond
+             ((get-text-property (1- (point)) 'block-side)
+              (setq continue t))
+             ((looking-back "\\\\+'" reg-beg t)
+              (setq continue (= (mod (- (point) (match-beginning 0)) 2) 0))
+              )
+             (t
+              (setq continue nil))
+             )
+            ) ;while
+          (setq token-type 'string))
+
+         ((eq ?\` ch-at)
+          (while (and continue (search-forward "`" reg-end t))
+            (cond
+             ((get-text-property (1- (point)) 'block-side)
+              (setq continue t))
+             ((looking-back "\\\\+`" reg-beg t)
+              (setq continue (= (mod (- (point) (match-beginning 0)) 2) 0)))
+             (t
+              (setq continue nil))
+             )
+            ) ;while
+          (setq token-type 'string))
+
+         ((eq ?\" ch-at)
+          (while (and continue (search-forward "\"" reg-end t))
+            (cond
+             ((get-text-property (1- (point)) 'block-side)
+              (setq continue t))
+             ((looking-back "\\\\+\"" reg-beg t)
+              (setq continue (= (mod (- (point) (match-beginning 0)) 2) 0)))
+             (t
+              (setq continue nil))
+             ) ;cond
+            ) ;while
+          (cond
+           ((string= content-type "json")
+            (if (looking-at-p "[ ]*:")
+                (cond
+                 ((eq ?\@ (char-after (1+ beg)))
+                  (setq token-type 'context))
+                 (t
+                  (setq token-type 'key))
+                 )
+              (setq token-type 'string))
+            ) ;json
+           (t
+            (setq token-type 'string))
+           ) ;cond
+          )
+
+         ((eq ?\< ch-at)
+          ;;(message "before [%S>%S|%S] pt=%S" reg-beg reg-end depth (point))
+          (if (web-mode-jsx-skip reg-end)
+              (web-mode-jsx-scan-element beg (point) depth)
+            (forward-char))
+          ;;(message "after [%S>%S|%S] pt=%S" reg-beg reg-end depth (point))
+          )
+
+         ((and (eq ?\/ ch-at) (member content-type '("javascript" "jsx")))
+          (cond
+           ((eq ?\\ ch-before)
+            )
+           ((eq ?\* ch-next)
+            ;;(message "--> %S %S" (point) reg-end)
+            (when (search-forward "*/" reg-end t)
+              (setq token-type 'comment))
+            )
+           ((eq ?\/ ch-next)
+            (setq token-type 'comment)
+            (goto-char (if (< reg-end (line-end-position)) reg-end (line-end-position)))
+            )
+           ((and (looking-at-p ".*/")
+                 (looking-back "[[(,=:!&|?{};][ ]*/"))
+                 ;;(re-search-forward "/[gimyu]*" reg-end t))
+            (let ((eol (line-end-position)))
+              (while (and continue (search-forward "/" eol t))
+                (cond
+                 ((get-text-property (1- (point)) 'block-side)
+                  (setq continue t))
+                 ((looking-back "\\\\+/" reg-beg t)
+                  (setq continue (= (mod (- (point) (match-beginning 0)) 2) 0)))
+                 (t
+                  (re-search-forward "[gimyu]*" eol t)
+                  (setq token-type 'string)
+                  (setq continue nil))
+                 )
+                ) ;while
+              ) ;let
+            )
+           ) ;cond
+          )
+
+         ((eq ?\/ ch-next)
+          (unless (eq ?\\ ch-before)
+            (setq token-type 'comment)
+            (goto-char (if (< reg-end (line-end-position)) reg-end (line-end-position)))
+            )
+          )
+
+         ((eq ?\* ch-next)
+          (cond
+           ((search-forward "*/" reg-end t)
+            (setq token-type 'comment))
+           ((not (eobp))
+            (forward-char))
+           ) ;cond
           )
 
          ) ;cond
 
-        (add-text-properties beg (point) props)
+        (when (and beg (>= reg-end (point)) token-type)
+          (put-text-property beg (point) 'part-token token-type)
+          (cond
+           ((eq token-type 'comment)
+            (put-text-property beg (1+ beg) 'syntax-table (string-to-syntax "<"))
+            (when (< (point) (point-max))
+              (if (< (point) (line-end-position))
+                  (put-text-property (1- (point)) (point) 'syntax-table (string-to-syntax ">")) ;#445
+                (put-text-property (point) (1+ (point)) 'syntax-table (string-to-syntax ">")) ;#377
+                )
+              ) ;when
+            ) ;comment
+           ((eq token-type 'string)
+            (put-text-property beg (1+ beg) 'syntax-table (string-to-syntax "|"))
+            (when (< (point) (point-max))
+              (if (< (point) (line-end-position))
+                  (put-text-property (1- (point)) (point) 'syntax-table (string-to-syntax "|"))
+                (put-text-property (point) (1+ (point)) 'syntax-table (string-to-syntax "|"))
+                )
+              ) ;when
+            ) ;string
+           ) ;cond
+          ) ;when
+
+        (when (> (point) reg-end)
+          (message "reg-beg(%S) reg-end(%S) token-type(%S) point(%S)" reg-beg reg-end token-type (point)))
+
+        ;;(message "#[%S>%S|%S] %S %c %c %c | (%S)" reg-beg reg-end depth beg ch-before ch-at ch-next (point))
 
         ) ;while
 
-      (put-text-property reg-beg (1+ reg-beg) 'block-beg flags)
-
-      ) ;when regexp
-
+      )))
+
+(defun web-mode-jsx-skip (reg-end)
+  (let ((continue t) (pos nil) (i 0))
+    (save-excursion
+      (while continue
+        (cond
+         ((> (setq i (1+ i)) 100)
+          (message "jsx-skip ** warning **")
+          (setq continue nil))
+         ((not (web-mode-dom-rsf ">\\([ \t\n]*[\];,)':}]\\)\\|{" reg-end))
+          (setq continue nil)
+          )
+         ((eq (char-before) ?\{)
+          (backward-char)
+          (web-mode-closing-paren reg-end)
+          (forward-char)
+          )
+         (t
+          (setq continue nil)
+          (setq pos (match-beginning 1))
+          ) ;t
+         ) ;cond
+        ) ;while
+      ) ;save-excursion
+    (when pos (goto-char pos))
+    ;;(message "jsx-skip: %S" pos)
+    pos))
+
+;; http://facebook.github.io/jsx/
+;; https://github.com/facebook/jsx/blob/master/AST.md
+(defun web-mode-jsx-scan-element (reg-beg reg-end depth)
+  (unless depth (setq depth 1))
+  (save-excursion
+    (let (token-beg token-end regexp)
+      (goto-char reg-beg)
+      (put-text-property reg-beg (1+ reg-beg) 'jsx-beg depth)
+      (put-text-property (1- reg-end) reg-end 'jsx-end depth)
+      (put-text-property reg-beg reg-end 'jsx-depth depth)
+      (goto-char reg-beg)
+      (while (web-mode-part-sf "/*" reg-end t)
+        (goto-char (match-beginning 0))
+        (if (looking-back "{")
+            (progn
+              (backward-char)
+              (setq regexp "*/}"))
+          (setq regexp "*/"))
+        (setq token-beg (point))
+        (if (not (web-mode-part-sf regexp reg-end t))
+            (goto-char reg-end)
+          (setq token-end (point))
+          (put-text-property token-beg token-end 'part-token 'comment)
+          ) ;if
+        ) ;while
+      (web-mode-scan-elements reg-beg reg-end)
+      (web-mode-jsx-scan-expression reg-beg reg-end (1+ depth))
+      )))
+
+(defun web-mode-jsx-scan-expression (reg-beg reg-end depth)
+  (let ((continue t) beg end)
+    (save-excursion
+      (goto-char reg-beg)
+      ;;(message "reg-beg=%S reg-end=%S" reg-beg reg-end)
+      (while (and continue (search-forward "{" reg-end t))
+        (backward-char)
+        (setq beg (point)
+              end (web-mode-closing-paren reg-end))
+        (cond
+         ((eq (get-text-property beg 'part-token) 'comment)
+          (forward-char))
+         ((not end)
+          (setq continue nil))
+         (t
+          (setq end (1+ end))
+          (put-text-property beg end 'jsx-depth depth)
+          (put-text-property beg (1+ beg) 'jsx-beg depth)
+          (put-text-property (1- end) end 'jsx-end depth)
+          (web-mode-part-scan beg end "jsx" (1+ depth))
+          ) ;t
+         ) ;cond
+        )
+      )
+    ))
+
+(defun web-mode-jsx-is-html (&optional pos)
+  (interactive)
+  (unless pos (setq pos (point)))
+  (let ((depth (get-text-property pos 'jsx-depth)))
+    (cond
+     ((or (null depth) (<= pos 2))
+      (setq pos nil))
+     ((and (= depth 1) (get-text-property pos 'jsx-beg))
+      (setq pos nil))
+     ((get-text-property pos 'jsx-beg)
+      (setq pos (null (get-text-property pos 'tag-beg))))
+     ((setq pos (web-mode-jsx-depth-beginning-position pos))
+      (setq pos (not (null (get-text-property pos 'tag-beg)))))
+     (t
+      (setq pos nil))
+     ) ;cond
+    ;;(message "is-html: %S (depth=%S)" pos depth)
+    pos))
+
+(defun web-mode-jsx-depth-beginning-position (&optional pos target-depth)
+  (interactive)
+  (unless pos (setq pos (point)))
+  (unless target-depth (setq target-depth (get-text-property pos 'jsx-depth)))
+  (cond
+   ((or (null target-depth) (bobp))
+    (setq pos nil))
+   ((and (get-text-property pos 'jsx-beg) (= target-depth (get-text-property pos 'jsx-depth)))
+    )
+   (t
+    (let ((continue t) depth)
+      (while continue
+        (setq pos (previous-single-property-change pos 'jsx-depth))
+        (cond
+         ((or (null pos)
+              (null (setq depth (get-text-property pos 'jsx-depth))))
+          (setq continue nil
+                pos nil))
+         ((and (get-text-property pos 'jsx-beg) (= target-depth depth))
+          (setq continue nil))
+         ) ;cond
+        ) ;while
+      ) ;let
+    ) ;t
+   ) ;cond
+  ;;(message "beg: %S" pos)
+  pos)
+
+(defun web-mode-jsx-element-next (reg-end)
+  (let (continue beg end)
+    (setq beg (point))
+    (unless (get-text-property beg 'jsx-depth)
+      (setq beg (next-single-property-change beg 'jsx-beg)))
+    (setq continue (and beg (< beg reg-end))
+          end beg)
+    (while continue
+      (setq end (next-single-property-change end 'jsx-end))
+      (cond
+       ((or (null end) (> end reg-end))
+        (setq continue nil
+              end nil))
+       ((eq (get-text-property end 'jsx-depth) 1)
+        (setq continue nil))
+       (t
+        (setq end (1+ end)))
+       ) ;cond
+      ) ;while
+    ;;(message "beg=%S end=%S" beg end)
+    (if (and beg end (< beg end)) (cons beg end) nil)))
+
+(defun web-mode-jsx-expression-next (reg-end)
+  (let (beg end depth continue pos)
+    (setq beg (point))
+    ;;(message "pt=%S" beg)
+    (unless (and (get-text-property beg 'jsx-beg) (null (get-text-property beg 'tag-beg)))
+      ;;(setq beg (next-single-property-change beg 'jsx-beg))
+      (setq continue t
+            pos (1+ beg))
+      (while continue
+        (setq pos (next-single-property-change pos 'jsx-beg))
+        (cond
+         ((null pos)
+          (setq continue nil
+                beg nil))
+         ((> pos reg-end)
+          (setq continue nil
+                beg nil))
+         ((null (get-text-property pos 'jsx-beg))
+          )
+         ((null (get-text-property pos 'tag-beg))
+          (setq continue nil
+                beg pos))
+         ;;(t
+         ;; (setq pos (1+ pos)))
+         ) ;cond
+        ) ;while
+      ) ;unless
+    ;;(message "beg=%S" beg)
+    (when (and beg (< beg reg-end))
+      (setq depth (get-text-property beg 'jsx-beg)
+            continue (not (null depth))
+            pos beg)
+      ;;(message "beg=%S" beg)
+      (while continue
+        (setq pos (next-single-property-change pos 'jsx-end))
+        ;;(message "pos=%S" pos)
+        (cond
+         ((null pos)
+          (setq continue nil))
+         ((> pos reg-end)
+          (setq continue nil))
+         ((eq depth (get-text-property pos 'jsx-end))
+          (setq continue nil
+                end pos))
+         (t
+          ;;(setq pos (1+ pos))
+          )
+         ) ;cond
+        ) ;while
+      ) ;when
+    ;;(message "%S > %S" beg end)
+    (if (and beg end) (cons beg end) nil)))
+
+(defun web-mode-jsx-depth-next (reg-end)
+  (let (beg end depth continue pos)
+    (setq beg (point))
+    ;;(message "pt=%S" beg)
+    (unless (get-text-property beg 'jsx-beg)
+      ;;(setq beg (next-single-property-change beg 'jsx-beg))
+      ;;(setq pos (1+ beg))
+      (setq pos (next-single-property-change (1+ beg) 'jsx-beg))
+      (cond
+       ((null pos)
+        (setq beg nil))
+       ((>= pos reg-end)
+        (setq beg nil))
+       (t
+        (setq beg pos))
+       ) ;cond
+      ) ;unless
+    ;;(message "beg=%S" beg)
+    (when beg
+      (setq depth (get-text-property beg 'jsx-beg)
+            continue (not (null depth))
+            pos beg)
+      ;;(message "beg=%S" beg)
+      (while continue
+        (setq pos (next-single-property-change pos 'jsx-end))
+        ;;(message "pos=%S" pos)
+        (cond
+         ((null pos)
+          (setq continue nil))
+         ((> pos reg-end)
+          (setq continue nil))
+         ((eq depth (get-text-property pos 'jsx-end))
+          (setq continue nil
+                end pos))
+         (t
+          ;;(setq pos (1+ pos))
+          )
+         ) ;cond
+        ) ;while
+      ) ;when
+    ;;(message "%S > %S" beg end)
+    (if (and beg end) (cons beg end) nil)))
+
+(defun web-mode-jsx-beginning ()
+  (interactive)
+  (let (depth (continue t) (reg-beg (point-min)) (pos (point)))
+    (setq depth (get-text-property pos 'jsx-depth))
+    (cond
+     ((not depth)
+      )
+     ((get-text-property (1- pos) 'jsx-beg)
+      (goto-char (1- pos)))
+     (t
+      (while continue
+        (setq pos (previous-single-property-change pos 'jsx-beg))
+        ;;(message "pos=%S" pos)
+        (cond
+         ((null pos)
+          (setq continue nil))
+         ((<= pos reg-beg)
+          (setq continue nil))
+         ((eq depth (get-text-property pos 'jsx-beg))
+          (setq continue nil))
+         ) ;cond
+        ) ;while
+      (web-mode-go pos)
+      ) ;t
+     ) ;cond
+    ))
+
+(defun web-mode-jsx-end ()
+  (interactive)
+  (let (depth (continue t) (reg-end (point-max)) (pos (point)))
+    (setq depth (get-text-property pos 'jsx-depth))
+    (cond
+     ((not depth)
+      )
+     ((get-text-property pos 'jsx-end)
+      (goto-char (+ pos 1)))
+     (t
+      (while continue
+        (setq pos (next-single-property-change pos 'jsx-end))
+        ;;(message "pos=%S" pos)
+        (cond
+         ((null pos)
+          (setq continue nil))
+         ((> pos reg-end)
+          (setq continue nil))
+         ((eq depth (get-text-property pos 'jsx-end))
+          (setq continue nil))
+         ) ;cond
+        ) ;while
+      (web-mode-go pos 1)
+      ) ;t
+     ) ;cond
+    ))
+
+(defun web-mode-velocity-skip (pos)
+  (goto-char pos)
+  (let ((continue t) (i 0))
+    (when (eq ?\# (char-after))
+      (forward-char))
+    (when (member (char-after) '(?\$ ?\@))
+      (forward-char))
+    (when (member (char-after) '(?\!))
+      (forward-char))
+    (if (member (char-after) '(?\{))
+        (search-forward "}")
+      (setq continue t)
+      (while continue
+        (skip-chars-forward "a-zA-Z0-9_-")
+        (when (> (setq i (1+ i)) 500)
+          (message "velocity-skip ** warning (%S) **" pos)
+          (setq continue nil))
+        (when (member (char-after) '(?\())
+          (search-forward ")" nil t))
+        (if (member (char-after) '(?\.))
+            (forward-char)
+          (setq continue nil))
+        ) ;while
+      ) ;if
+    ))
+
+(defun web-mode-razor-skip (pos)
+  (goto-char pos)
+  (let ((continue t) (i 0))
+    (while continue
+      (skip-chars-forward " =@a-zA-Z0-9_-")
+      (cond
+       ((> (setq i (1+ i)) 500)
+        (message "razor-skip ** warning **")
+        (setq continue nil))
+       ((and (eq (char-after) ?\*)
+             (eq (char-before) ?@))
+        (when (not (search-forward "*@" nil t))
+          (setq continue nil))
+        )
+       ((looking-at-p "@[({]")
+        (forward-char)
+        (when (setq pos (web-mode-closing-paren-position (point)))
+          (goto-char pos))
+        (forward-char)
+        )
+       ((and (not (eobp)) (eq ?\( (char-after)))
+        (if (looking-at-p "[ \n]*[<@]")
+            (setq continue nil)
+          (when (setq pos (web-mode-closing-paren-position))
+            (goto-char pos))
+          (forward-char)
+          ) ;if
+        )
+       ((and (not (eobp)) (eq ?\. (char-after)))
+        (forward-char))
+       ((looking-at-p "[ \n]*{")
+        (search-forward "{")
+        (if (looking-at-p "[ \n]*[<@]")
+            (setq continue nil)
+          (backward-char)
+          (when (setq pos (web-mode-closing-paren-position))
+            (goto-char pos))
+          (forward-char)
+          ) ;if
+        )
+       ((looking-at-p "}")
+        (forward-char))
+       (t
+        (setq continue nil))
+       ) ;cond
+      ) ;while
+    ))
+
+;; css rule = selector(s) + declaration (properties)
+(defun web-mode-css-rule-next (limit)
+  (let (at-rule sel-beg sel-end dec-beg dec-end chunk)
+    (skip-chars-forward "\n\t ")
+    (setq sel-beg (point))
+    (when (and (< (point) limit)
+               (web-mode-part-rsf "[{;]" limit))
+      (setq sel-end (1- (point)))
+      (cond
+       ((eq (char-before) ?\{)
+        (setq dec-beg (point))
+        (setq dec-end (web-mode-closing-paren-position (1- dec-beg) limit))
+        (if dec-end
+            (progn
+              (goto-char dec-end)
+              (forward-char))
+          (setq dec-end limit)
+          (goto-char limit))
+        )
+       (t
+        )
+       ) ;cond
+      (setq chunk (buffer-substring-no-properties sel-beg sel-end))
+      (when (string-match "@\\([[:alpha:]-]+\\)" chunk)
+        (setq at-rule (match-string-no-properties 1 chunk)))
+      ) ;when
+    (if (not sel-end)
+        (progn (goto-char limit) nil)
+      (list :at-rule at-rule
+            :sel-beg sel-beg
+            :sel-end sel-end
+            :dec-beg dec-beg
+            :dec-end dec-end)
+      ) ;if
     ))
 
-(defun web-mode-highlight-block (reg-beg reg-end)
-  "Highlight block."
-  (let (sub2 sub3 continue char keywords token-type face beg end flags)
+(defun web-mode-css-rule-current (&optional pos part-beg part-end)
+  "Current CSS rule boundaries."
+  (unless pos (setq pos (point)))
+  (unless part-beg (setq part-beg (web-mode-part-beginning-position pos)))
+  (unless part-end (setq part-end (web-mode-part-end-position pos)))
+  (save-excursion
+    (let (beg end)
+      (goto-char pos)
+      (if (not (web-mode-part-sb "{" part-beg))
+          (progn
+            (setq beg part-beg)
+            (if (web-mode-part-sf ";" part-end)
+                (setq end (1+ (point)))
+              (setq end part-end))
+            ) ;progn
+        (setq beg (point))
+        (setq end (web-mode-closing-paren-position beg part-end))
+        (if end
+            (setq end (1+ end))
+          (setq end (line-end-position)))
+;;        (message "%S >>beg%S >>end%S" pos beg end)
+        (if (> pos end)
+
+            ;;selectors
+            (progn
+              (goto-char pos)
+              (if (web-mode-part-rsb "[};]" part-beg)
+                  (setq beg (1+ (point)))
+                (setq beg part-beg)
+                ) ;if
+              (goto-char pos)
+              (if (web-mode-part-rsf "[{;]" part-end)
+                  (cond
+                   ((eq (char-before) ?\;)
+                    (setq end (point))
+                    )
+                   (t
+                    (setq end (web-mode-closing-paren-position (1- (point)) part-end))
+                    (if end
+                        (setq end (1+ end))
+                      (setq end part-end))
+                    )
+                   ) ;cond
+                (setq end part-end)
+                )
+              ) ;progn selectors
+
+          ;; declaration
+          (goto-char beg)
+          (if (web-mode-part-rsb "[}{;]" part-beg)
+              (setq beg (1+ (point)))
+            (setq beg part-beg)
+            ) ;if
+          ) ;if > pos end
+        )
+;;      (message "beg(%S) end(%S)" beg end)
+      (when (eq (char-after beg) ?\n)
+        (setq beg (1+ beg)))
+      (cons beg end)
+      )))
+
+(defun web-mode-scan-engine-comments (reg-beg reg-end tag-start tag-end)
+  "Scan engine comments (mako, django)."
+  (save-excursion
+    (let (beg end (continue t))
+      (goto-char reg-beg)
+      (while (and continue
+                  (< (point) reg-end)
+                  (re-search-forward tag-start reg-end t))
+        (goto-char (match-beginning 0))
+        (setq beg (point))
+        (if (not (re-search-forward tag-end reg-end t))
+            (setq continue nil)
+          (setq end (point))
+          (remove-list-of-text-properties beg end web-mode-scan-properties)
+          (add-text-properties beg end '(block-side t block-token comment))
+          (put-text-property beg (1+ beg) 'block-beg 0)
+          (put-text-property (1- end) end 'block-end t)
+          ) ;if
+        ) ;while
+      )))
+
+(defun web-mode-propertize (&optional beg end)
+
+  (unless beg (setq beg web-mode-change-beg))
+  (unless end (setq end web-mode-change-end))
+
+  ;;(message "%S %S" web-mode-content-type (get-text-property beg 'part-side))
+  ;;(message "propertize: beg(%S) end(%S)" web-mode-change-beg web-mode-change-end)
+  ;;(message "%S %S" (get-text-property beg 'part-side) (get-text-property end 'part-side))
+
+  (when (and end (> end (point-max)))
+    (setq end (point-max)))
+
+  (setq web-mode-change-beg nil
+        web-mode-change-end nil)
+  (cond
+
+   ((or (null beg) (null end))
+    nil)
+
+   ((and (member web-mode-engine '("php" "asp"))
+         (get-text-property beg 'block-side)
+         (get-text-property end 'block-side)
+         (> beg (point-min))
+         (not (eq (get-text-property (1- beg) 'block-token) 'delimiter-beg))
+         (not (eq (get-text-property end 'block-token) 'delimiter-end)))
+    ;;(message "invalidate block")
+    (web-mode-invalidate-block-region beg end))
+
+   ((and (or (member web-mode-content-type '("css" "jsx" "javascript"))
+             (and (get-text-property beg 'part-side)
+                  (get-text-property end 'part-side)
+                  (> beg (point-min))
+                  (get-text-property (1- beg) 'part-side))
+             ))
+    ;;(message "invalidate part (%S > %S)" beg end)
+    (web-mode-invalidate-part-region beg end))
+
+   (t
+    ;;(message "invalidate default (%S > %S)" beg end)
+    (web-mode-invalidate-region beg end))
+
+   ) ;cond
+
+  )
+
+;; NOTE: il est important d'identifier des caractères en fin de ligne
+;; web-mode-block-tokenize travaille en effet sur les fins de lignes pour
+;; les commentaires de type //
+(defun web-mode-invalidate-block-region (pos-beg pos-end)
+  ;;  (message "pos-beg(%S) pos-end(%S)" pos-beg pos-end)
+  (save-excursion
+    (let (beg end code-beg code-end)
+      ;;(message "invalidate-block-region: pos-beg(%S)=%S" pos-beg (get-text-property pos 'block-side))
+      ;;(message "code-beg(%S) code-end(%S) pos-beg(%S) pos-end(%S)" code-beg code-end pos-beg pos-end)
+      (cond
+       ((not (and (setq code-beg (web-mode-block-code-beginning-position pos-beg))
+                  (setq code-end (web-mode-block-code-end-position pos-beg))
+                  (>= pos-beg code-beg)
+                  (<= pos-end code-end)
+                  (> code-end code-beg)))
+        (web-mode-invalidate-region pos-beg pos-end))
+       ((member web-mode-engine '("asp"))
+        (goto-char pos-beg)
+        (forward-line -1)
+        (setq beg (line-beginning-position))
+        (when (> code-beg beg)
+          (setq beg code-beg))
+        (goto-char pos-beg)
+        (forward-line)
+        (setq end (line-end-position))
+        (when (< code-end end)
+          (setq end code-end))
+        ;; ?? pas de (web-mode-block-tokenize beg end) ?
+        (cons beg end)
+        ) ;asp
+       (t
+        (goto-char pos-beg)
+        (when (string= web-mode-engine "php")
+          (cond
+           ((and (looking-back "\*")
+                 (looking-at-p "/"))
+            (search-backward "/*" code-beg))
+           ) ;cond
+          )
+        (if (web-mode-block-rsb "[;{}(][ ]*\n" code-beg)
+            (setq beg (match-end 0))
+          (setq beg code-beg))
+        (goto-char pos-end)
+        (if (web-mode-block-rsf "[;{})][ ]*\n" code-end)
+            (setq end (1- (match-end 0)))
+          (setq end code-end))
+        (web-mode-block-tokenize beg end)
+        ;;(message "beg(%S) end(%S)" beg end)
+        (cons beg end)
+        )
+       ) ;cond
+      )))
+
+(defun web-mode-invalidate-part-region (pos-beg pos-end)
+  (save-excursion
+    (let (beg end part-beg part-end language)
+      (if (member web-mode-content-type '("css" "javascript" "json" "jsx"))
+          (setq language web-mode-content-type)
+        (setq language (symbol-name (get-text-property pos-beg 'part-side))))
+      (setq part-beg (web-mode-part-beginning-position pos-beg)
+            part-end (web-mode-part-end-position pos-beg))
+      ;;(message "language(%S) pos-beg(%S) pos-end(%S) part-beg(%S) part-end(%S)"
+      ;;         language pos-beg pos-end part-beg part-end)
+      (goto-char pos-beg)
+      (cond
+       ((not (and part-beg part-end
+                  (>= pos-beg part-beg)
+                  (<= pos-end part-end)
+                  (> part-end part-beg)))
+        (web-mode-invalidate-region pos-beg pos-end))
+       ((member language '("javascript" "json" "jsx"))
+        (if (web-mode-javascript-rsb "[;{}(][ ]*\n" part-beg)
+            (setq beg (match-end 0))
+          (setq beg part-beg))
+        (goto-char pos-end)
+        (if (web-mode-javascript-rsf "[;{})][ ]*\n" part-end)
+            (setq end (match-end 0))
+          (setq end part-end))
+        (web-mode-scan-region beg end language))
+       ((string= language "css")
+        (let (rule1 rule2)
+          (setq rule1 (web-mode-css-rule-current pos-beg))
+          (setq rule2 rule1)
+          (when (> pos-end (cdr rule1))
+            (setq rule2 (web-mode-css-rule-current pos-end)))
+          (setq beg (car rule1)
+                end (cdr rule2))
+          )
+        (web-mode-scan-region beg end language))
+       (t
+        (setq beg part-beg
+              end part-end)
+        (web-mode-scan-region beg end language))
+       ) ;cond
+      )))
+
+(defun web-mode-invalidate-region (reg-beg reg-end)
+  ;;(message "%S | reg-beg(%S) reg-end(%S)" (point) reg-beg reg-end)
+  (setq reg-beg (web-mode-invalidate-region-beginning-position reg-beg)
+        reg-end (web-mode-invalidate-region-end-position reg-end))
+  ;;(message "invalidate-region: reg-beg(%S) reg-end(%S)" reg-beg reg-end)
+  (web-mode-scan-region reg-beg reg-end))
+
+(defun web-mode-invalidate-region-beginning-position (pos)
+  (save-excursion
+    (goto-char pos)
+    (when (and (bolp) (not (bobp)))
+      (backward-char))
+    (beginning-of-line)
+    ;;(message "pos=%S %S" (point) (text-properties-at (point)))
+    (setq pos (point-min))
+    (let ((continue (not (bobp))))
+      (while continue
+        (cond
+         ((bobp)
+          (setq continue nil))
+         ;; NOTE: Going back to the previous start tag is necessary
+         ;; when inserting a part endtag (e.g. </script>).
+         ;; Indeed, parts must be identified asap.
+         ((and (progn (back-to-indentation) t)
+               (get-text-property (point) 'tag-beg)
+               (eq (get-text-property (point) 'tag-type) 'start))
+          (setq pos (point)
+                continue nil))
+         (t
+          (forward-line -1))
+         ) ;cond
+        ) ;while
+      ;;(message "pos=%S" pos)
+      pos)))
+
+(defun web-mode-invalidate-region-end-position (pos)
+  (save-excursion
+    (goto-char pos)
+    ;;(message "pos=%S %S" pos (get-text-property pos 'block-token))
+    (when (string= web-mode-engine "jsp")
+      (cond
+       ((and (looking-back "<%")
+             (looking-at-p "--"))
+        (search-forward "--%>"))
+       ((and (looking-back "-- %")
+             (looking-at-p ">"))
+        (search-forward "--%>"))
+       ) ;cond
+      ) ;when
+
+    (setq pos (point-max))
+    (let ((continue (not (eobp))))
+      (while continue
+        (end-of-line)
+        ;;(message "%S %S" (point) (get-text-property (point) 'block-token))
+        (cond
+         ((eobp)
+          (setq continue nil))
+         ;;()
+         ((and (not (get-text-property (point) 'tag-type))
+               (not (get-text-property (point) 'part-side))
+               (not (get-text-property (point) 'block-side)))
+          (setq pos (point)
+                continue nil))
+         (t
+          (forward-line))
+         ) ;cond
+        ) ;while
+      pos)))
+
+(defun web-mode-buffer-scan ()
+  "Scan entine buffer."
+  (interactive)
+  (web-mode-scan-region (point-min) (point-max)))
+
+;;---- FONTIFICATION -----------------------------------------------------------
+
+(defun web-mode-font-lock-highlight (limit)
+  ;;(message "font-lock-highlight: point(%S) limit(%S) change-beg(%S) change-end(%S)" (point) limit web-mode-change-beg web-mode-change-end)
+  (cond
+   (web-mode-inhibit-fontification
+    nil)
+   (t
+    (web-mode-highlight-region (point) limit)
+    nil)
+   ))
+
+(defun web-mode-buffer-highlight ()
+  (interactive)
+  (if (fboundp 'font-lock-flush)
+      (font-lock-flush)
+    (font-lock-fontify-buffer)))
+
+(defun web-mode-extend-region ()
+  ;;(message "extend-region: flb(%S) fle(%S) wmcb(%S) wmce(%S)" font-lock-beg font-lock-end web-mode-change-beg web-mode-change-end)
+  ;;  (setq font-lock-beg web-mode-change-beg
+  ;;        font-lock-end web-mode-change-end)
+  (cond
+   (web-mode-inhibit-fontification
+    nil)
+   (t ;;(and web-mode-change-beg web-mode-change-end)
+    (when (or (null web-mode-change-beg) (< font-lock-beg web-mode-change-beg))
+      ;;(message "font-lock-beg(%S) < web-mode-change-beg(%S)" font-lock-beg web-mode-change-beg)
+      (setq web-mode-change-beg font-lock-beg))
+    (when (or (null web-mode-change-end) (> font-lock-end web-mode-change-end))
+      ;;(message "font-lock-end(%S) > web-mode-change-end(%S)" font-lock-end web-mode-change-end)
+      (setq web-mode-change-end font-lock-end))
+    (let ((region (web-mode-propertize web-mode-change-beg web-mode-change-end)))
+      (when region
+        ;;(message "region: %S" region)
+        (setq font-lock-beg (car region)
+              font-lock-end (cdr region)
+              ;;web-mode-change-beg (car region)
+              ;;web-mode-change-end (cdr region)
+              )
+        ) ;when
+      ) ;let
+    nil) ;t
+   ))
+
+(defun web-mode-unfontify-region (beg end)
+  ;;(message "unfontify: %S %S" beg end)
+  )
+
+(defun web-mode-highlight-region (&optional beg end) ;; content-type)
+  ;;(message "highlight-region: beg(%S) end(%S)" beg end)
+  (web-mode-with-silent-modifications
+   (save-excursion
+     (save-restriction
+       (save-match-data
+         (let ((buffer-undo-list t)
+               ;;(inhibit-modification-hooks t)
+               (inhibit-point-motion-hooks t)
+               (inhibit-quit t))
+           (remove-list-of-text-properties beg end '(font-lock-face face))
+           (cond
+            ((and (get-text-property beg 'block-side)
+                  (not (get-text-property beg 'block-beg)))
+             (web-mode-block-highlight beg end))
+            ((or (member web-mode-content-type web-mode-part-content-types)
+                 ;;(member content-type web-mode-part-content-types)
+                 (get-text-property beg 'part-side))
+             (web-mode-part-highlight beg end)
+             (web-mode-process-blocks beg end 'web-mode-block-highlight))
+            ((string= web-mode-engine "none")
+             (web-mode-highlight-tags beg end)
+             (web-mode-process-parts beg end 'web-mode-part-highlight))
+            (t
+             (web-mode-highlight-tags beg end)
+             (web-mode-process-parts beg end 'web-mode-part-highlight)
+             (web-mode-process-blocks beg end 'web-mode-block-highlight))
+            ) ;cond
+           (when web-mode-enable-element-content-fontification
+             (web-mode-highlight-elements beg end))
+           (when web-mode-enable-whitespace-fontification
+             (web-mode-highlight-whitespaces beg end))
+           ;;(message "%S %S" font-lock-keywords font-lock-keywords-alist)
+           ))))))
+
+(defun web-mode-highlight-tags (reg-beg reg-end &optional depth)
+  (let ((continue t))
+    (goto-char reg-beg)
+    (when (and (not (get-text-property (point) 'tag-beg))
+               (not (web-mode-tag-next)))
+      (setq continue nil))
+    (when (and continue (>= (point) reg-end))
+      (setq continue nil))
+    (while continue
+      (cond
+       (depth
+        (when (eq depth (get-text-property (point) 'jsx-depth))
+          (web-mode-tag-highlight))
+        )
+       (t
+        (web-mode-tag-highlight))
+       ) ;cond
+      (when (or (not (web-mode-tag-next))
+                (>= (point) reg-end))
+        (setq continue nil))
+      ) ;while
+    (when web-mode-enable-inlays
+      (when (null web-mode-inlay-regexp)
+        (setq web-mode-inlay-regexp (regexp-opt '("\\[" "\\(" "\\begin{align}"))))
+      (let (beg end expr)
+        (goto-char reg-beg)
+        (while (web-mode-dom-rsf web-mode-inlay-regexp reg-end)
+          (setq beg (match-beginning 0)
+                end nil
+                expr (substring (match-string-no-properties 0) 0 2))
+          (setq expr (cond
+                      ((string= expr "\\[") "\\]")
+                      ((string= expr "\\(") "\\)")
+                      (t "\\end{align}")))
+          (when (and (web-mode-dom-sf expr reg-end)
+                     (setq end (match-end 0))
+                     (not (text-property-any beg end 'tag-end t)))
+            (font-lock-append-text-property beg end 'font-lock-face 'web-mode-inlay-face)
+            ) ;when
+          ) ;while
+        ) ;let
+      ) ;when
+    (when web-mode-enable-html-entities-fontification
+      (let (beg end)
+        (goto-char reg-beg)
+        (while (web-mode-dom-rsf "&\\([#]?[[:alnum:]]\\{2,8\\}\\);" reg-end)
+          (setq beg (match-beginning 0)
+                end (match-end 0))
+          (when (not (text-property-any beg end 'tag-end t))
+            (font-lock-append-text-property beg end 'font-lock-face 'web-mode-html-entity-face)
+            ) ;when
+          ) ;while
+        ) ;let
+      ) ;when
+    ))
+
+(defun web-mode-tag-highlight (&optional beg end)
+  (unless beg (setq beg (point)))
+  (unless end (setq end (1+ (web-mode-tag-end-position beg))))
+  (let (name type face flags slash-beg slash-end bracket-end)
+    (setq flags (get-text-property beg 'tag-beg)
+          type (get-text-property beg 'tag-type)
+          name (get-text-property beg 'tag-name))
+    (cond
+     ((eq type 'comment)
+      (put-text-property beg end 'font-lock-face 'web-mode-comment-face)
+      (when (and web-mode-enable-comment-interpolation (> (- end beg) 5))
+        (web-mode-interpolate-comment beg end nil)))
+     ((eq type 'cdata)
+      (put-text-property beg end 'font-lock-face 'web-mode-doctype-face))
+     ((eq type 'doctype)
+      (put-text-property beg end 'font-lock-face 'web-mode-doctype-face))
+     ((eq type 'declaration)
+      (put-text-property beg end 'font-lock-face 'web-mode-doctype-face))
+     (name
+      (setq face (cond
+                  ((and web-mode-enable-element-tag-fontification
+                        (setq face (cdr (assoc name web-mode-element-tag-faces))))
+                   face)
+                  ((> (logand flags 32) 0) 'web-mode-html-tag-namespaced-face)
+                  ((> (logand flags 2) 0)  'web-mode-html-tag-custom-face)
+                  (t                       'web-mode-html-tag-face))
+            slash-beg (> (logand flags 4) 0)
+            slash-end (> (logand flags 8) 0)
+            bracket-end (> (logand flags 16) 0))
+      (put-text-property beg (+ beg (if slash-beg 2 1))
+                         'font-lock-face 'web-mode-html-tag-bracket-face)
+      (put-text-property (+ beg (if slash-beg 2 1)) (+ beg (if slash-beg 2 1) (length name))
+                         'font-lock-face face)
+      (when (or slash-end bracket-end)
+        (put-text-property (- end (if slash-end 2 1)) end 'font-lock-face 'web-mode-html-tag-bracket-face)
+        ) ;when
+      (when (> (logand flags 1) 0)
+        ;;(message "%S>%S" beg end)
+        (web-mode-highlight-attrs beg end))
+      ) ;case name
+     ) ;cond
+    ))
+
+(defun web-mode-highlight-attrs (reg-beg reg-end)
+  (let ((continue t) (pos reg-beg) beg end flags offset face)
+    ;;(message "highlight-attrs %S>%S" reg-beg reg-end)
+    (while continue
+      (setq beg (web-mode-attribute-next-position pos reg-end))
+      (cond
+       ((or (null beg) (>= beg reg-end))
+        (setq continue nil))
+       (t
+        (setq flags (or (get-text-property beg 'tag-attr-beg) 0))
+        (setq face (cond
+                    ((= (logand flags 1) 1) 'web-mode-html-attr-custom-face)
+                    ((= (logand flags 2) 2) 'web-mode-html-attr-engine-face)
+                    ((= (logand flags 4) 4) nil)
+                    (t                      'web-mode-html-attr-name-face)))
+        ;;(setq end (if (get-text-property beg 'tag-attr-end) beg (web-mode-attribute-end-position beg)))
+        (setq end (web-mode-attribute-end-position beg))
+        (cond
+         ((or (null end) (>= end reg-end))
+          (setq continue nil))
+         (t
+          (setq offset (get-text-property end 'tag-attr-end))
+          (if (= offset 0)
+              (put-text-property beg (1+ end) 'font-lock-face face)
+            (put-text-property beg (+ beg offset) 'font-lock-face face)
+            (put-text-property (+ beg offset) (+ beg offset 1)
+                               'font-lock-face
+                               'web-mode-html-attr-equal-face)
+            (when (not (get-text-property (+ beg offset 1) 'jsx-beg))
+              (put-text-property (+ beg offset 1) (1+ end)
+                                 'font-lock-face
+                                 'web-mode-html-attr-value-face)
+              )
+            ) ;if offset
+          (setq pos (1+ end))
+          ) ;t
+         ) ;cond
+        ) ;t
+       );cond
+      ) ;while
+    ))
+
+(defun web-mode-block-highlight (reg-beg reg-end)
+  (let (sub1 sub2 sub3 continue char keywords token-type face beg end (buffer (current-buffer)))
     ;;(message "reg-beg=%S reg-end=%S" reg-beg reg-end)
-    (remove-text-properties reg-beg reg-end '(font-lock-face nil))
+
+    ;; NOTE: required for block inside tag attr
+    (remove-list-of-text-properties reg-beg reg-end '(font-lock-face))
 
     (goto-char reg-beg)
 
     (when (null web-mode-engine-font-lock-keywords)
-      (setq sub2 (buffer-substring-no-properties
+      (setq sub1 (buffer-substring-no-properties
+                  reg-beg (+ reg-beg 1))
+            sub2 (buffer-substring-no-properties
                   reg-beg (+ reg-beg 2))
             sub3 (buffer-substring-no-properties
                   reg-beg (+ reg-beg (if (>= (point-max) (+ reg-beg 3)) 3 2))))
@@ -3096,7 +5427,8 @@
 
     (cond
 
-     ((eq (get-text-property reg-beg 'block-token) 'comment)
+     ((and (get-text-property reg-beg 'block-beg)
+           (eq (get-text-property reg-beg 'block-token) 'comment))
       (put-text-property reg-beg reg-end 'font-lock-face 'web-mode-comment-face)
       ) ;comment block
 
@@ -3116,7 +5448,7 @@
       (cond
        ((member sub3 '("<% " "<%\n" "<%!"))
         (setq keywords web-mode-mako-block-font-lock-keywords))
-       ((string= sub2 "% ")
+       ((eq (aref sub2 0) ?\%)
         (setq keywords web-mode-mako-block-font-lock-keywords))
        ((member sub2 '("<%" "</"))
         (setq keywords web-mode-mako-tag-font-lock-keywords))
@@ -3124,6 +5456,20 @@
         (setq keywords web-mode-uel-font-lock-keywords))
        )) ;mako
 
+     ((string= web-mode-engine "mason")
+      ;;(message "%S %S" sub2 sub3)
+      (cond
+       ((member sub3 '("<% " "<%\n" "<&|"))
+        (setq keywords web-mode-mason-code-font-lock-keywords))
+       ((eq (aref sub2 0) ?\%)
+        (setq keywords web-mode-mason-code-font-lock-keywords))
+       ((and (or (string= sub2 "<%") (string= sub3 "</%"))
+             (not (member sub3 '("<%c" "<%i" "<%p"))))
+        (setq keywords web-mode-mason-block-font-lock-keywords))
+       (t
+        (setq keywords web-mode-mason-code-font-lock-keywords))
+       )) ;mason
+
      ((string= web-mode-engine "jsp")
       (cond
        ((string= sub3 "<%@")
@@ -3132,9 +5478,22 @@
         (setq keywords web-mode-uel-font-lock-keywords))
        ((string= sub2 "<%")
         (setq keywords web-mode-jsp-font-lock-keywords))
+       ;;(t
+       ;; (setq keywords web-mode-engine-tag-font-lock-keywords))
+       )) ;jsp
+
+     ((string= web-mode-engine "asp")
+      (cond
+       ((or (string= sub2 "<%")
+            (not (string= sub1 "<")))
+        (setq keywords web-mode-asp-font-lock-keywords))
        (t
-        (setq keywords web-mode-jsp-tag-font-lock-keywords))
-       )) ;jsp
+        (setq keywords web-mode-engine-tag-font-lock-keywords))
+       )) ;asp
+
+     ((string= web-mode-engine "clip")
+      (setq keywords web-mode-engine-tag-font-lock-keywords)
+      ) ;clip
 
      ((string= web-mode-engine "aspx")
       (cond
@@ -3156,24 +5515,19 @@
                            web-mode-freemarker-square-font-lock-keywords
                          web-mode-freemarker-font-lock-keywords)))
        (t
-        (setq keywords web-mode-jsp-tag-font-lock-keywords))
+        (setq keywords web-mode-engine-tag-font-lock-keywords))
        )) ;freemarker
 
      ) ;cond
 
     (when keywords
       (web-mode-fontify-region reg-beg reg-end keywords)
-;;      (message "reg-beg(%S) reg-end(%S)" reg-beg reg-end)
-      (setq flags (get-text-property reg-beg 'block-beg))
-      (setq continue (not (null flags))) ;;(> (logand flags 1) 0))
+      (setq continue t)
       (setq end reg-beg)
       (while continue
-;;        (message "end=%S %S" end (get-text-property end 'block-token))
-        (if (and (= reg-beg end)
-                 (eq (get-text-property end 'block-token) 'delimiter))
+        (if (get-text-property end 'block-token)
             (setq beg end)
-          (setq beg (next-single-property-change end 'block-token))
-          )
+          (setq beg (next-single-property-change end 'block-token buffer reg-end)))
         (setq end nil)
         (when beg (setq char (char-after beg)))
         (if (and beg (< beg reg-end))
@@ -3183,12 +5537,14 @@
                           ((eq token-type 'string)  'web-mode-block-string-face)
                           ((eq token-type 'comment) 'web-mode-block-comment-face)
                           (t                        'web-mode-block-delimiter-face)))
-              (setq end (next-single-property-change beg 'block-token))
+              (setq end (next-single-property-change beg 'block-token buffer reg-end))
+;;              (message "end=%S" end)
               (if (and end (<= end reg-end))
                   (progn
-;;                    (message "%S > %S face(%S)" beg end face)
-                    (remove-text-properties beg end '(face nil))
-                    (put-text-property beg end 'font-lock-face face))
+                    ;;(message "%S > %S face(%S)" beg end face)
+                    (remove-list-of-text-properties beg end '(face))
+                    (put-text-property beg end 'font-lock-face face)
+                    )
                 (setq continue nil
                       end nil)
                 ) ;if end
@@ -3201,14 +5557,15 @@
             (when (and web-mode-enable-heredoc-fontification
                        (eq char ?\<)
                        (> (- end beg) 8)
-                       ;;                     (progn (message "ici%S" (buffer-substring-no-properties beg end)) t)
-                     ;;                   (member web-mode-engine '("php"))
-                       (> (logand flags 2) 0)
+                       ;;(progn (message "%S" (buffer-substring-no-properties beg end)) t)
                        (string-match-p "JS\\|JAVASCRIPT\\|HTM\\|CSS" (buffer-substring-no-properties beg end)))
-              (setq keywords (if (eq ?H (char-after (+ beg 3)))
-                                 web-mode-html-font-lock-keywords
-                               web-mode-javascript-font-lock-keywords))
-;;              (remove-text-properties beg end '(font-lock-face nil))
+              (setq keywords
+                    (cond
+                     ((string-match-p "H" (buffer-substring-no-properties beg (+ beg 8)))
+                      web-mode-html-font-lock-keywords)
+                     (t
+                      web-mode-javascript-font-lock-keywords)
+                     ))
               (web-mode-fontify-region beg end keywords)
             ))
 ;;          (message "%S %c %S beg=%S end=%S" web-mode-enable-string-interpolation char web-mode-engine beg end)
@@ -3216,720 +5573,50 @@
                      (member char '(?\" ?\<))
                      (member web-mode-engine '("php" "erb"))
                      (> (- end beg) 4))
-            (web-mode-interpolate-string beg end))
-          (when (and web-mode-enable-comment-keywords
+            (web-mode-interpolate-block-string beg end)
+            ) ;when
+          (when (and web-mode-enable-comment-interpolation
                      (eq token-type 'comment)
                      (> (- end beg) 3))
             (web-mode-interpolate-comment beg end t)
             ) ;when
+          (when (and web-mode-enable-sql-detection
+                     (eq token-type 'string)
+                     (> (- end beg) 6)
+                     ;;(eq char ?\<)
+                     ;;(web-mode-looking-at-p (concat "[ \n]*" web-mode-sql-queries) (1+ beg))
+                     (web-mode-looking-at-p (concat "\\(.\\|<<<[[:alnum:]]+\\)[ \n]*" web-mode-sql-queries) beg)
+                     )
+            (web-mode-interpolate-sql-string beg end)
+            ) ;when
           ) ;when beg end
-        ) ;while
+        ) ;while continue
       ) ;when keywords
 
-    (when (and (member web-mode-engine '("jsp" "mako"))
+    ;;(when (and (member web-mode-engine '("jsp" "mako"))
+    (when (and (member web-mode-engine '("mako"))
                (> (- reg-end reg-beg) 12)
                (eq ?\< (char-after reg-beg)))
       (web-mode-interpolate-block-tag reg-beg reg-end))
 
     (when web-mode-enable-block-face
+;;      (message "block-face %S %S" reg-beg reg-end)
       (font-lock-append-text-property reg-beg reg-end 'face 'web-mode-block-face))
 
     ))
 
-(defun web-mode-scan-mako-block-comments (reg-beg reg-end)
-  "Scan extra"
-  (save-excursion
-    (let (beg end)
-      (goto-char reg-beg)
-      (while (and (< (point) reg-end)
-                  (re-search-forward "<%doc>" reg-end t))
-        (goto-char (match-beginning 0))
-        (setq beg (point))
-        (when (re-search-forward "</%doc>" reg-end t)
-          (setq end (point))
-          (remove-text-properties beg end web-mode-scan-properties)
-          (add-text-properties beg end '(block-side t block-token comment))
-          (put-text-property beg (1+ beg) 'block-beg t)
-          (put-text-property (1- end) end 'block-end t)
-          ) ;when
-        ) ;while
-      )))
-
-(defun web-mode-scan-django-block-comments (reg-beg reg-end)
-  "Scan extra"
-  (save-excursion
-    (let (beg end)
-      (goto-char reg-beg)
-      (while (and (< (point) reg-end)
-                  (re-search-forward "{% comment %}" reg-end t))
-        (setq beg (point))
-        (goto-char (1+ (match-beginning 0)))
-        (when (web-mode-match-django-block)
-          (setq end (1- (point)))
-          (remove-text-properties beg end web-mode-scan-properties)
-          (add-text-properties beg end '(block-token comment font-lock-face web-mode-comment-face))
-          )
-        )
-      )))
-
-(defun web-mode-interpolate-block-tag (beg end)
-  "Scan a block tag (jsp / mako) to fontify ${ } blocks"
-  (save-excursion
-    (goto-char (+ 4 beg))
-    (setq end (1- end))
-    (while (re-search-forward "${.*}" end t)
-      (remove-text-properties (match-beginning 0) (match-end 0) '(font-lock-face nil))
-      (web-mode-fontify-region (match-beginning 0) (match-end 0)
-                               web-mode-uel-font-lock-keywords)
-      )
-    ))
-
-;; todo : parsing plus compliqué: {$obj->values[3]->name}
-(defun web-mode-interpolate-string (beg end)
-  "Interpolate php/erb strings."
-  (save-excursion
-    (goto-char (1+ beg))
-    (setq end (1- end))
-    (cond
-     ((string= web-mode-engine "php")
-      (while (re-search-forward "$[[:alnum:]_]+\\(->[[:alnum:]_]+\\)*\\|{[ ]*$.+}" end t)
-        ;;web-mode-php-var-interpolation-font-lock-keywords
-;;        (message "la%S" (point))
-        (remove-text-properties (match-beginning 0) (match-end 0) '(font-lock-face nil))
-        (web-mode-fontify-region (match-beginning 0) (match-end 0)
-                                 web-mode-php-var-interpolation-font-lock-keywords)
-
-        ;; (put-text-property (match-beginning 0) (match-end 0)
-        ;;                    'font-lock-face nil)
-        ;; (put-text-property (1+ (match-beginning 0)) (match-end 0)
-        ;;                    'font-lock-face 'web-mode-variable-name-face)
-
-        ))
-     ((string= web-mode-engine "erb")
-      (while (re-search-forward "#{.*}" end t)
-        (remove-text-properties (match-beginning 0) (match-end 0) '(font-lock-face nil))
-        (put-text-property (match-beginning 0) (match-end 0)
-                           'font-lock-face 'web-mode-variable-name-face)
-        ))
-     ) ;cond
-    ))
-
-(defun web-mode-interpolate-comment (beg end block-side)
-  "Interpolate comment"
-  (save-excursion
-    (let (regexp)
-      (goto-char beg)
-      (setq regexp (concat "\\<\\(" web-mode-comment-keywords "\\)\\>"))
-      (while (re-search-forward regexp end t)
-        (font-lock-prepend-text-property (match-beginning 1) (match-end 1)
-                                         'font-lock-face
-                                         'web-mode-comment-keyword-face)
-        )
-      )))
-
-;;todo :
-;; tag-type : start / end / void / comment / cdata / doctype / declaration
-;; tag-name uniquement sur les html tag
-
-;; piste d'optim : associer tagname à 'tag-end
-
-;; flags
-;; (1)attrs (2)custom (4)slash-beg (8)slash-end (16)brackend-end
-
-;; http://dev.w3.org/html5/html-author/#tags
-;; http://www.w3schools.com/jsref/prop_node_nodetype.asp
-;; start-tag, end-tag, tag-name, element (<a>xsx</a>, an element is delimited by tags), void-element
-;; http://www.w3.org/TR/html-markup/syntax.html#syntax-elements
-(defun web-mode-scan-tags (reg-beg reg-end)
-  "Scan html nodes (tags/attrs/comments/doctype)."
+(defun web-mode-part-highlight (reg-beg reg-end &optional depth)
   (save-excursion
-    (let (part-beg part-end flags limit close-expr props tname tbeg tend tstop element-content-type attrs-end close-found is-tag slash-beg slash-end regexp regexp1 regexp2)
-
-      (setq regexp1 "<\\(/?[[:alpha:]][[:alnum:]-]*\\|!--\\|!\\[CDATA\\[\\|!doctype\\|\?xml\\)"
-            regexp2 "<\\(/?[[:alpha:]][[:alnum:]-]*\\|!--\\|!\\[CDATA\\[\\)")
-
-      (setq regexp regexp1)
-
-      (goto-char reg-beg)
-
-      (while (web-mode-rsf-client regexp reg-end t)
-
-        (setq flags 0
-              tname (downcase (match-string-no-properties 1))
-              tbeg (match-beginning 0)
-              tend nil
-              tstop (point)
-              element-content-type nil
-              limit reg-end
-              part-beg nil
-              part-end nil
-              props nil
-              close-expr ">"
-              close-found nil
-              is-tag nil
-              slash-beg nil
-              slash-end nil)
-
-        (cond
-         ((string= tname "!--")
-          (setq close-expr "-->"
-                props '(tag-type comment)))
-         ((string= tname "?xml")
-          (setq regexp regexp2
-                close-expr "?>"
-                props '(tag-type declaration)))
-         ((string= tname "![cdata[")
-          (setq close-expr "]]>"
-                props '(tag-type cdata)))
-         ((string= tname "!doctype")
-          (setq regexp regexp2
-                props '(tag-type doctype)))
-         (t
-          (setq is-tag t)
-          (when (string-match-p "-" tname)
-            (setq flags (logior flags 2)))
-          (cond
-           ((eq ?\/ (aref tname 0))
-            (setq props (list 'tag-name (substring tname 1) 'tag-type 'end)
-                  slash-beg t
-                  flags (logior flags 4))
-            (setq limit (if (> reg-end (line-end-position)) (line-end-position) reg-end))
-            )
-           ((web-mode-is-void-element tname)
-            (setq props (list 'tag-name tname 'tag-type 'void))
-            )
-           (t
-            (setq props (list 'tag-name tname 'tag-type 'start))
-            )
-           ) ;cond
-          ) ;t
-         ) ;cond
-
-        (if (web-mode-sf-client close-expr limit t)
-            (progn
-              (setq attrs-end (- (point) (length close-expr))
-                    tend (point)
-                    flags (logior flags 16)
-                    close-found t)
-              (when (eq ?\/ (char-after (- (point) 2)))
-                (setq attrs-end (1- attrs-end)
-                      props (plist-put props 'tag-type 'void)
-                      slash-end t
-                      flags (logior flags 8)))
-              ) ;progn
-          (setq attrs-end (line-end-position)
-                tend (line-end-position))
-          ) ;if
-
-        (cond
-         ((string= tname "script")
-          (let (script)
-            (setq script (buffer-substring-no-properties tbeg tend))
-            (cond
-             ((string-match-p " type[ ]*=[ ]*[\"']text/\\(x-handlebars\\|html\\|ng-template\\)" script)
-              (setq element-content-type "html"))
-             ((string-match-p " type[ ]*=[ ]*[\"']application/\\(ld\\+json\\|json\\)" script)
-              (setq element-content-type "json"))
-             (t
-              (setq element-content-type "javascript"))
-             )
-            ;;          (message "tag=%S : %S" tname element-content-type)
-            )
-          ) ;case script
-         ((string= tname "style")
-          (setq element-content-type "css")
-          )
-         )
-
-        ;;        (message "tag=%S (%S > %S)\n%S" tname tbeg tend props)
-        (add-text-properties tbeg tend props)
-
-;;        (when is-tag
-
-        (when (and is-tag
-                   (not slash-beg)
-                   (> (- attrs-end tstop) 2)
-                   (> (web-mode-scan-attrs tstop attrs-end) 0))
-          (setq flags (logior flags 1)))
-
-        ;;            (progn
-        (put-text-property tbeg (1+ tbeg) 'tag-beg flags)
-        ;;              (when close-found
-        (put-text-property (1- tend) tend 'tag-end t)
-          ;;                )
-          ;;              ) ;progn
-          ;;          (add-text-properties tbeg (1+ tbeg) '(tag-beg t))
-          ;;          (add-text-properties (1- tend) tend '(tag-end t))
-;;          )
-
-        (when (and is-tag close-found)
-
-          (cond
-           ((and (string= tname "script")
-                 (member element-content-type '("javascript" "json")))
-            (setq close-expr "</script>"))
-           ((string= tname "style")
-            (setq close-expr "</style>"))
-           (t
-            (setq close-expr nil))
-           )
-
-          ;; si <script type="document/html"> on ne fait pas la suite
-
-          (when (and close-expr (web-mode-sf-client close-expr reg-end t))
-            (setq part-beg (if (eq (char-after tend) ?\n) (1+ tend) tend)
-                  part-end (match-beginning 0))
-;;            (message "part-beg(%S) part-end(%S)" part-beg part-end)
-            (if (>= (- part-end part-beg) 3)
-                (progn
-                  (put-text-property part-beg part-end
-                                     'part-side
-                                     (cond
-                                      ((string= element-content-type "javascript") 'javascript)
-                                      ((string= element-content-type "json") 'json)
-                                      ((string= element-content-type "css") 'css)
-                                      ))
-;;                  (web-mode-scan-part part-beg part-end)
-                  ) ;progn
-;;              (remove-text-properties part-beg part-end web-mode-scan-properties2)
-              ) ;if
-            (goto-char part-end)
-            ) ;when
-
-          ) ;when
-
-        ) ;while
-
-      )))
-
-(defun web-mode-highlight-tags (reg-beg reg-end)
-  "web-mode-highlight-nodes"
-  (let ((continue t))
-    (goto-char reg-beg)
-    (when (and (not (get-text-property (point) 'tag-beg))
-               (not (web-mode-tag-next)))
-      (setq continue nil))
-    (when (and continue (>= (point) reg-end))
-      (setq continue nil))
-    (while continue
-      (web-mode-highlight-tag)
-      (when (or (not (web-mode-tag-next))
-                (>= (point) reg-end))
-        (setq continue nil))
-      ) ;while
-    ))
-
-;; flags
-;; (1)attrs (2)custom (4)slash-beg (8)slash-end (16)brackend-end
-(defun web-mode-highlight-tag ()
-  "web-mode-highlight-nodes"
-  (let ((beg (point)) end name type face flags slash-beg slash-end bracket-end)
-
-    (setq end (1+ (web-mode-tag-end-position beg))
-          flags (get-text-property beg 'tag-beg)
-          type (get-text-property beg 'tag-type)
-          name (get-text-property beg 'tag-name))
-
-    (cond
-
-     ((eq type 'comment)
-      (put-text-property beg end 'font-lock-face 'web-mode-comment-face)
-;;      (message "web-mode-enable-comment-keywords=%S beg(%S) end(%S)" web-mode-enable-comment-keywords beg end)
-      (when (and web-mode-enable-comment-keywords (> (- end beg) 5))
-        (web-mode-interpolate-comment beg end nil))
-      )
-
-     ((eq type 'cdata)
-      (put-text-property beg end 'font-lock-face 'web-mode-doctype-face))
-
-     ((eq type 'doctype)
-      (put-text-property beg end 'font-lock-face 'web-mode-doctype-face))
-
-     ((eq type 'declaration)
-      (put-text-property beg end 'font-lock-face 'web-mode-doctype-face))
-
-     (name
-
-      ;; todo : se passer des vars intermédiaires
-      (setq face (if (> (logand flags 2) 0) 'web-mode-html-tag-custom-face 'web-mode-html-tag-face)
-            slash-beg (> (logand flags 4) 0)
-            slash-end (> (logand flags 8) 0)
-            bracket-end (> (logand flags 16) 0))
-
-      (put-text-property beg (+ beg (if slash-beg 2 1)) 'font-lock-face 'web-mode-html-tag-bracket-face)
-      (put-text-property (+ beg (if slash-beg 2 1))
-                         (+ beg (if slash-beg 2 1) (length name))
-                         'font-lock-face face)
-      (when (or slash-end bracket-end)
-        (put-text-property (- end (if slash-end 2 1)) end 'font-lock-face 'web-mode-html-tag-bracket-face)
-        ) ;when
-
-      (when (> (logand flags 1) 0)
-        (web-mode-highlight-attrs beg end)
-        )
-
-      ) ;name
-
-     ) ;cond
-
-    ))
-
-;; todo : optimisation des zones reg-beg et reg-end
-(defun web-mode-highlight-attrs (reg-beg reg-end)
-  "Highlight attributes."
-  (let ((continue t) (pos reg-beg) beg end flags offset face)
-    (while continue
-      (setq beg (next-single-property-change pos 'tag-attr))
-      (if (and beg (< beg reg-end))
-          (progn
-            (setq flags (get-text-property beg 'tag-attr))
-;;            (message "beg=%S flags=%S" beg flags)
-            (setq face (if (> (logand flags 1) 0) 'web-mode-html-attr-custom-face 'web-mode-html-attr-name-face))
-            (if (get-text-property beg 'tag-attr-end)
-                (setq end beg)
-              (setq end (next-single-property-change beg 'tag-attr-end)))
-;;            (message "beg=%S end=%S" beg end)
-            (if (and end (< end reg-end))
-                (progn
-                  (setq offset (get-text-property end 'tag-attr-end))
-;;                  (message "offset=%S" offset)
-                  (if (= offset 0)
-                      (put-text-property beg (1+ end) 'font-lock-face face)
-                    (put-text-property beg (+ beg offset) 'font-lock-face face)
-                    (put-text-property (+ beg offset) (+ beg offset 1) 'font-lock-face 'web-mode-html-attr-equal-face)
-                    (put-text-property (+ beg offset 1) (1+ end) 'font-lock-face 'web-mode-html-attr-value-face)
-                    ) ;if offset
-                  (setq pos (1+ end))
-                  ) ;progn
-              (setq continue nil)
-              ) ;if end
-            ) ;progn beg
-        (setq continue nil)
-        ) ;if beg
-      ) ;while
-    ))
-
-;; http://www.w3.org/TR/html-markup/syntax.html#syntax-attributes
-;; states:
-;; nil(0) space(1) name(2) space-before(3) equal(4) space-after(5) value-uq(6) value-sq(7) value-dq(8)
-(defun web-mode-scan-attrs (beg end)
-  "Scan html attributes."
-  (save-excursion
-    ;;(message "beg(%S) end(%S)" beg end)
-    (let (name-beg name-end val-beg (count 0) (state 0) (flags 0) (equal-offset 0) char pos escaped spaced)
-      (goto-char (1- beg))
-
-      (while (< (point) end)
-        (forward-char)
-        (setq pos (point)
-              char (char-after))
-        (setq spaced (eq char ?\s))
-
-        (cond
-
-         ((= pos end)
-          (when name-beg
-            (unless name-end (setq name-end (1- pos)))
-;;            (message "name-end=%S" name-end)
-            (setq count (+ count (web-mode-scan-attr state char name-beg name-end val-beg flags equal-offset)))
-            )
-          (setq state 0
-                flags 0
-                equal-offset 0
-                name-beg nil
-                name-end nil
-                val-beg nil)
-          )
-
-         ((get-text-property pos 'block-side)
-          )
-
-         ((and spaced (= state 0))
-          (setq state 1)
-          )
-
-         ((and spaced (member state '(1 3 5)))
-          )
-
-         ((and spaced (= state 2))
-          (setq name-end (1- pos)
-                state 3)
-          )
-
-         ((and spaced (= state 4))
-          (setq state 5)
-          )
-
-         ((and (= state 3)
-;;               (progn (message "pt=%S state=%S char=%c" (point) state char) t)
-               (or (and (>= char 65) (<= char 90)) ;A - Z
-                   (and (>= char 97) (<= char 122)) ;a - z
-                   ;; (= char 34) (= char 39) ; " '
-                   ;; (and (>= char 48) (<= char 57)) ;0 - 9
-                   ))
-;;          (message "pos=%S name-beg=%S name-end=%S char=%c" pos name-beg name-end char)
-;;          (message "%S %S - %S %S" ?a ?z ?A ?Z)
-;;          (message "pos=%S %S" pos name-end)
-          (setq count (+ count (web-mode-scan-attr state char name-beg name-end val-beg flags equal-offset)))
-          (setq state 2
-                flags 0
-                equal-offset 0
-                name-beg pos
-                name-end nil
-                val-beg nil)
-          )
-
-         ((and (eq char ?\n) (not (member state '(7 8))))
-          (if (= state 2) (setq name-end (1- pos)))
-          (setq count (+ count (web-mode-scan-attr state char name-beg name-end val-beg flags equal-offset)))
-          (setq state 1
-                flags 0
-                equal-offset 0
-                name-beg nil
-                name-end nil
-                val-beg nil)
-          )
-
-         ((or (and (eq ?\" char) (= state 8) (not escaped))
-              (and (eq ?\' char) (= state 7) (not escaped))
-              (and (member char '(?\s ?\n ?\>)) (= state 6)))
-          (setq count (+ count (web-mode-scan-attr state char name-beg name-end val-beg flags equal-offset)))
-          (setq state (if (= state 6) 1 0)
-                flags 0
-                equal-offset 0
-                name-beg nil
-                name-end nil
-                val-beg nil)
-          )
-
-         ((and (not spaced) (= state 1))
-          (setq state 2)
-          (setq name-beg pos)
-          )
-
-         ((and (eq ?\= char) (member state '(2 3)))
-          (setq name-end pos)
-;;          (message "name-beg=%S %S" name-beg pos)
-          (setq equal-offset (- pos name-beg))
-          (setq state 4)
-          )
-
-         ((and (eq ?\" char) (member state '(4 5)))
-          (setq val-beg pos)
-          (setq state 8)
-;;          (setq count (+ count (web-mode-scan-attr state char name-beg name-end val-beg flags equal-offset)))
-          )
-
-         ((and (eq ?\' char) (member state '(4 5)))
-          (setq val-beg pos)
-          (setq state 7)
-;;          (setq count (+ count (web-mode-scan-attr state char name-beg name-end val-beg flags equal-offset)))
-          )
-
-         ((member state '(4 5))
-          (setq val-beg pos)
-          (setq state 6)
-          )
-
-         ((= state 1)
-          (setq state 2)
-          )
-
-         ((and (= state 2) (eq ?\- char))
-          (setq flags (logior flags 1))
-          )
-
-         ) ;cond
-
-        ;;        (message "point(%S) end(%S) state(%S) c(%S) name-beg(%S) name-end(%S) val-beg(%S) flags(%S) equal-offset(%S)" pos end state char name-beg name-end val-beg flags equal-offset)
-
-        (setq escaped (eq ?\\ char))
-
-        ) ;while
-
-      count)))
-
-;; flags:
-;; (1)custom
-
-;; states:
-;; (0)nil (1)space (2)name (3)space-before (4)equal (5)space-after (6)value-uq (7)value-sq (8)value-dq
-(defun web-mode-scan-attr (state char name-beg name-end val-beg flags equal-offset)
-  "propertize attr."
-;;  (message "point(%S) state(%S) c(%c) name-beg(%S) name-end(%S) val-beg(%S) flags(%S) equal-offset(%S)"
-;;           (point) state char name-beg name-end val-beg flags equal-offset)
-;;  (message "flags=%S" flags)
-  (cond
-   ((and (= state 8) (not (eq ?\" char)))
-    0)
-   ((and (= state 7) (not (eq ?\' char)))
-    0)
-   ((= state 4)
-    0)
-   ((null name-beg)
-    0)
-   (t
-    (let (val-end)
-      (if (null val-beg)
-          (setq val-end name-end)
-        (setq val-end (point))
-        (when (or (null char) (member char '(?\s ?\n ?\>)))
-          (setq val-end (1- val-end)))
-        ) ;if
-        ;;      (put-text-property name-beg (1+ name-beg) 'tag-attr flags)
-      (put-text-property name-beg (1+ val-end) 'tag-attr flags)
-      (put-text-property val-end (1+ val-end) 'tag-attr-end equal-offset)
-      ) ;let
-    1) ;t
-   ) ;cond
-  )
-
-(defun web-mode-scan-part (reg-beg reg-end)
-  "Scan client part (e.g. javascript, json, css)."
-  (save-excursion
-    (let (token-re ch-before ch-at ch-next token-type start continue content-type)
-;;      (message "reg-beg(%S) reg-end(%S) content-type(%S)" reg-beg reg-end content-type)
-
-      (if (member web-mode-content-type '("javascript" "json" "css"))
+    (let (start continue token-type face pos beg end string-face comment-face content-type)
+      ;;(message "part-highlight: reg-beg(%S) reg-end(%S)" reg-beg reg-end)
+      (if (member web-mode-content-type web-mode-part-content-types)
           (setq content-type web-mode-content-type)
-        (setq content-type (symbol-name (get-text-property reg-beg 'part-side)))
-;;        (message "content-type=%S" content-type)
-        )
-
-;;      (remove-text-properties reg-beg reg-end web-mode-scan-properties2)
-
-      (goto-char reg-beg)
-
-      (when (and (not web-mode-has-any-large-part)
-                 (> (- reg-end reg-beg) web-mode-large-embed-threshold))
-;;        (message "** large part detected [ %S - %S ] **" reg-beg reg-end)
-        (setq web-mode-has-any-large-part t))
-
-      (cond
-       ((string= content-type "javascript")
-        (setq token-re "/.\\|\"\\|'"))
-       ((string= content-type "json")
-        (setq token-re "//\\|/\\*\\|\"\\|'"))
-       ((string= content-type "css")
-        (setq token-re "/\\*\\|\"\\|'"))
-       (t
-        (setq token-re "/\\*\\|\"\\|'"))
-       )
-
-      (while (and token-re (web-mode-rsf-client token-re reg-end t))
-        (setq start (match-beginning 0)
-              token-type nil
-              continue t)
-        (setq ch-at (char-after start))
-        (setq ch-next (or (char-after (1+ start)) ?\d))
-        (setq ch-before (or (char-before start) ?\d))
-        ;;        (message "beg=%S :%c%c%c" start ch-before ch-at ch-next)
-        (cond
-
-         ((eq ?\' ch-at)
-          (unless (eq ?\\ ch-before)
-            (while (and continue (search-forward "'" reg-end t))
-              (setq continue (or (get-text-property (1- (point)) 'block-side)
-                                 (eq ?\\ (char-before (1- (point))))))
-              )
-            (cond
-             ((string= content-type "javascript")
-              (setq token-type 'string))
-             ((string= content-type "css")
-              (setq token-type 'string))
-             ((string= content-type "json")
-              (setq token-type 'string))
-             (t
-              (setq token-type 'string))
-             ) ;cond
-            ) ;unless
-          )
-
-         ((eq ?\" ch-at)
-          (unless (eq ?\\ ch-before)
-            (while (and continue (search-forward "\"" reg-end t))
-              (setq continue (or (get-text-property (1- (point)) 'block-side)
-                                 (eq ?\\ (char-before (1- (point))))))
-              ) ;while
-            (cond
-             ((string= content-type "json")
-              (if (looking-at-p "[ ]*:")
-                  (cond
-                   ((eq ?\@ (char-after (1+ start)))
-                    (setq token-type 'context))
-                   (t
-                    (setq token-type 'key))
-                   )
-                (setq token-type 'string))
-              )
-             (t
-              (cond
-               ((string= content-type "javascript")
-                (setq token-type 'string))
-               ((string= content-type "css")
-                (setq token-type 'string))
-               (t
-                (setq token-type 'string))
-               ) ;cond
-              ) ;t
-             ) ;cond
-            ) ;unless
-          )
-
-         ((eq ?\/ ch-next)
-          (unless (eq ?\\ ch-before)
-            (setq token-type 'comment)
-            (goto-char (if (< reg-end (line-end-position)) reg-end (line-end-position)))
-            )
-          )
-
-         ((eq ?\* ch-next)
-          (unless (eq ?\\ ch-before)
-            (setq token-type 'comment)
-            (search-forward "*/" reg-end t)
-            )
-          )
-
-         ((and (string= content-type "javascript")
-               (eq ?\/ ch-at)
-               (progn (backward-char) t)
-               (looking-back "[(=][ ]*/")
-               (looking-at-p ".+/")
-;;               (not (eq ?\s ch-next))
-               )
-;;          (message "regexp literal at (%S)" (1- (point)))
-          (while (and continue (search-forward "/" reg-end t))
-            (setq continue (or (get-text-property (1- (point)) 'block-side)
-                               (eq ?\\ (char-before (1- (point))))))
-            )
-          (setq token-type 'string)
-          (skip-chars-forward "gimy")
-          )
-
-         ) ;cond
-
-        (when (and (>= reg-end (point)) token-type)
-          (put-text-property start (point) 'part-token token-type)
-          )
-
-        ) ;while
-
-      )))
-
-(defun web-mode-highlight-part (reg-beg reg-end)
-  "Highlight part (e.g. javascript, json, css)."
-  (save-excursion
-    (let (char start continue token-type face beg end string-face comment-face content-type)
-
-      (if (member web-mode-content-type '("javascript" "json" "css"))
-          (setq content-type web-mode-content-type)
-        (setq content-type (symbol-name (get-text-property reg-beg 'part-side)))
-        )
-
-      (goto-char reg-beg)
-
-      (cond
-       ((string= content-type "javascript")
+        (setq content-type (symbol-name (get-text-property reg-beg 'part-side))))
+      (unless depth
+        (when (string= content-type "jsx") (setq depth 0))
+        )
+      (cond
+       ((member content-type '("javascript" "jsx"))
         (setq string-face 'web-mode-javascript-string-face
               comment-face 'web-mode-javascript-comment-face)
         (web-mode-fontify-region reg-beg reg-end web-mode-javascript-font-lock-keywords))
@@ -3940,95 +5627,155 @@
        ((string= content-type "css")
         (setq string-face 'web-mode-css-string-face
               comment-face 'web-mode-css-comment-face)
-        (web-mode-highlight-css-rules reg-beg reg-end)
-        )
+        (web-mode-css-rules-highlight reg-beg reg-end))
        (t
         (setq string-face 'web-mode-part-string-face
-              comment-face 'web-mode-part-comment-face)
-        )
+              comment-face 'web-mode-part-comment-face))
        )
 
+      (goto-char reg-beg)
+
+      ;;(when (string= content-type "jsx") (web-mode-highlight-tags reg-beg reg-end))
+      ;;(setq continue (and pos (< pos reg-end)))
       (setq continue t
-            end reg-beg)
+            pos reg-beg)
       (while continue
-        (setq char (char-after beg))
-        (setq beg (next-single-property-change end 'part-token)
-              end nil)
-        (if (and beg (< beg reg-end))
-            (progn
-              (setq token-type (get-text-property beg 'part-token))
-              (setq face (cond
-                          ((eq token-type 'context) 'web-mode-json-context-face)
-                          ((eq token-type 'key) 'web-mode-json-key-face)
-                          ((eq token-type 'string) string-face)
-                          (t comment-face)))
-              (setq end (next-single-property-change beg 'part-token))
-              (if (and end (< end reg-end))
-                  (progn
-                    (remove-text-properties beg end '(face nil))
-                    (put-text-property beg end 'font-lock-face face)
-                    )
-                (setq continue nil
-                      end nil)
-                ) ;if end
-              ) ;progn beg
+        (if (get-text-property pos 'part-token)
+            (setq beg pos)
+          (setq beg (next-single-property-change pos 'part-token)))
+        (cond
+         ((or (null beg) (>= beg reg-end))
           (setq continue nil
-                end nil)
-          ) ;if beg
-
-        (when (and beg end
-                   web-mode-enable-comment-keywords
-                   (eq token-type 'comment)
-                   (> (- end beg) 3))
-          (web-mode-interpolate-comment beg end t))
-
+                end nil))
+         ((and (eq depth 0) (get-text-property beg 'jsx-depth))
+          (setq pos (or (next-single-property-change beg 'jsx-depth) (point-max))))
+         (t
+          (setq token-type (get-text-property beg 'part-token))
+          (setq face (cond
+                      ((eq token-type 'string)  string-face)
+                      ((eq token-type 'comment) comment-face)
+                      ((eq token-type 'context) 'web-mode-json-context-face)
+                      ((eq token-type 'key)     'web-mode-json-key-face)
+                      (t                        nil)))
+          (setq end (or (next-single-property-change beg 'part-token) (point-max))
+                pos end)
+          (cond
+           ((or (null end) (> end reg-end))
+            (setq continue nil
+                  end nil))
+           (t
+            (when face
+              (remove-list-of-text-properties beg end '(face))
+              (put-text-property beg end 'font-lock-face face))
+            (cond
+             ((< (- end beg) 6)
+              )
+             ((eq token-type 'string)
+              (when (and web-mode-enable-string-interpolation
+                         (member content-type '("javascript" "jsx")))
+                (web-mode-interpolate-javascript-string beg end)))
+             ((eq token-type 'comment)
+              (when web-mode-enable-comment-interpolation
+                (web-mode-interpolate-comment beg end t)))
+             ) ;cond
+            ) ;t
+           ) ;cond
+          ) ;t
+         ) ;cond
         ) ;while
 
-      (when web-mode-enable-part-face
-        (font-lock-append-text-property reg-beg reg-end 'face 'web-mode-part-face)
-        ;;        (font-lock-prepend-text-property reg-beg reg-end 'font-lock-face 'web-mode-block-face)
-        ;;        (font-lock-append-text-property reg-beg reg-end 'web-mode-part-face face)
-        )
-
-      )))
-
-(defun web-mode-highlight-css-rules (part-beg part-end)
-  "Scan CSS rules."
+      (when (and (string= web-mode-content-type "html") web-mode-enable-part-face)
+        (font-lock-append-text-property reg-beg reg-end 'face
+                                        (if (string= content-type "javascript")
+                                            'web-mode-script-face
+                                          'web-mode-style-face))
+        )
+
+      (when (and (eq depth 0) (string= content-type "jsx"))
+        (let (pair elt-beg elt-end exp-beg exp-end exp-depth)
+          (goto-char reg-beg)
+          (while (setq pair (web-mode-jsx-element-next reg-end))
+            ;;(message "elt-pair=%S" pair)
+            (setq elt-beg (car pair)
+                  elt-end (cdr pair))
+            (remove-list-of-text-properties elt-beg (1+ elt-end) '(face))
+            (web-mode-highlight-tags elt-beg elt-end 1)
+            (goto-char elt-beg)
+            (while (setq pair (web-mode-jsx-expression-next elt-end))
+              ;;(message "exp-pair=%S elt-end=%S" pair elt-end)
+              (setq exp-beg (car pair)
+                    exp-end (cdr pair))
+              (when (eq (char-after exp-beg) ?\{)
+                (setq exp-depth (get-text-property exp-beg 'jsx-depth))
+                (remove-list-of-text-properties exp-beg exp-end '(font-lock-face))
+                (put-text-property exp-beg (1+ exp-beg) 'font-lock-face 'web-mode-block-delimiter-face)
+                (when (and (eq (get-text-property exp-beg 'tag-attr-beg) 4) (web-mode-looking-at-p "\.\.\." (1+ exp-beg)))
+                  (put-text-property exp-beg (+ exp-beg 4) 'font-lock-face 'web-mode-block-delimiter-face))
+                (put-text-property exp-end (1+ exp-end) 'font-lock-face 'web-mode-block-delimiter-face)
+                (web-mode-highlight-tags (1+ exp-beg) exp-end (1+ exp-depth))
+                (web-mode-part-highlight (1+ exp-beg) exp-end exp-depth)
+                (web-mode-fontify-region (1+ exp-beg) exp-end web-mode-javascript-font-lock-keywords)
+                )
+              (goto-char (1+ exp-beg))
+              ) ;while exp
+
+            (when (and elt-beg web-mode-jsx-depth-faces)
+              (let (depth-beg depth-end jsx-face)
+                (goto-char elt-beg)
+                (while (setq pair (web-mode-jsx-depth-next reg-end))
+                  ;;(message "depth-pair=%S" pair)
+                  (setq depth-beg (car pair)
+                        depth-end (cdr pair)
+                        depth (get-text-property depth-beg 'jsx-depth)
+                        jsx-face (elt web-mode-jsx-depth-faces (1- depth)))
+                  ;;(message "%S" jsx-face)
+                  (font-lock-prepend-text-property depth-beg (1+ depth-end) 'face jsx-face)
+                  (goto-char (+ depth-beg 2))
+                  )
+                ) ;let
+              )
+
+            (goto-char (1+ elt-end))
+            ) ;while elt
+          ) ;let
+        ) ;when
+
+      ) ;let
+    ) ;save-excursion
+  )
+
+(defun web-mode-css-rules-highlight (part-beg part-end)
   (save-excursion
     (goto-char part-beg)
-    (let (rule (continue t) (i 0) at-rule)
+    (let (rule (continue t) (i 0) (at-rule nil))
       (while continue
-        (setq i (1+ i))
-        (setq rule (web-mode-css-next-rule part-end))
+        (setq rule (web-mode-css-rule-next part-end))
+        ;;(message "rule=%S" rule)
         (cond
-         ((> i 1000)
-          (message "*** too much css rules ***")
+         ((> (setq i (1+ i)) 1000)
+          (message "css-rules-highlight ** too much rules **")
           (setq continue nil))
          ((null rule)
-          (setq continue nil)
-          )
+          (setq continue nil))
          ((and (setq at-rule (plist-get rule :at-rule))
                (not (member at-rule '("charset" "font-face" "import")))
                (plist-get rule :dec-end))
-          (web-mode-highlight-css-rule (plist-get rule :sel-beg)
+          (web-mode-css-rule-highlight (plist-get rule :sel-beg)
                                        (plist-get rule :sel-end)
                                        nil nil)
-          (web-mode-highlight-css-rules (plist-get rule :dec-beg)
-                                        (plist-get rule :dec-end))
-          )
+          (web-mode-css-rules-highlight (plist-get rule :dec-beg)
+                                        (plist-get rule :dec-end)))
          (t
-          (web-mode-highlight-css-rule (plist-get rule :sel-beg)
+          (web-mode-css-rule-highlight (plist-get rule :sel-beg)
                                        (plist-get rule :sel-end)
                                        (plist-get rule :dec-beg)
-                                       (plist-get rule :dec-end))
-          )
+                                       (plist-get rule :dec-end)))
          ) ;cond
-        )
-      )
+        ) ;while
+      ) ;let
     ))
 
-(defun web-mode-highlight-css-rule (sel-beg sel-end dec-beg dec-end)
-  "Fontify css rule."
+(defun web-mode-css-rule-highlight (sel-beg sel-end dec-beg dec-end)
   (save-excursion
 ;;    (message "sel-beg=%S sel-end=%S dec-beg=%S dec-end=%S" sel-beg sel-end dec-beg dec-end)
     (web-mode-fontify-region sel-beg sel-end
@@ -4037,203 +5784,30 @@
       (web-mode-fontify-region dec-beg dec-end
                                web-mode-declaration-font-lock-keywords)
       (goto-char dec-beg)
-      (while (and (not web-mode-disable-css-colorization)
-                  (re-search-forward "#[0-9a-fA-F]\\{6\\}\\|#[0-9a-fA-F]\\{3\\}\\|rgb([ ]*\\([[:digit:]]\\{1,3\\}\\)[ ]*,[ ]*\\([[:digit:]]\\{1,3\\}\\)[ ]*,[ ]*\\([[:digit:]]\\{1,3\\}\\)\\(.*?\\))" dec-end t)
+      (while (and web-mode-enable-css-colorization
+                  (re-search-forward "#[0-9a-fA-F]\\{6\\}\\|#[0-9a-fA-F]\\{3\\}\\|rgba?([ ]*\\([[:digit:]]\\{1,3\\}\\)[ ]*,[ ]*\\([[:digit:]]\\{1,3\\}\\)[ ]*,[ ]*\\([[:digit:]]\\{1,3\\}\\)\\(.*?\\))" dec-end t)
                   (< (point) dec-end))
         (web-mode-colorize (match-beginning 0) (match-end 0))
         )
       )))
 
-;; css rule = selector(s) + declaration (properties)
-(defun web-mode-css-next-rule (limit)
-  "next rule"
-  (let (at-rule sel-beg sel-end dec-beg dec-end chunk)
-    (skip-chars-forward "\n\t ")
-    (setq sel-beg (point))
-    (when (and (< (point) limit)
-               (web-mode-rsf-client "[{;]" limit t))
-      (setq sel-end (1- (point)))
-      (cond
-       ((eq (char-before) ?\{)
-        (setq dec-beg (point))
-        (setq dec-end (web-mode-closing-paren-position (1- dec-beg) limit))
-        (if dec-end
-            (progn
-              (goto-char dec-end)
-              (forward-char))
-          (setq dec-end limit)
-          (goto-char limit))
-        )
-       (t
-        )
-       ) ;cond
-      (setq chunk (buffer-substring-no-properties sel-beg sel-end))
-      (when (string-match "@\\([[:alpha:]-]+\\)" chunk)
-        (setq at-rule (match-string-no-properties 1 chunk))
-;;        (message "%S at-rule=%S" chunk at-rule)
-        )
-      ) ;when
-    (if (not sel-end)
-        (progn (goto-char limit) nil)
-      (list :at-rule at-rule
-            :sel-beg sel-beg
-            :sel-end sel-end
-            :dec-beg dec-beg
-            :dec-end dec-end)
-      ) ;if
-    ))
-
-(defun web-mode-css-current-rule (pos min max)
-  "current css rule"
+(defun web-mode-fontify-region (beg end keywords)
+;;  (message "beg=%S end=%S" beg end);; (symbol-name keywords))
   (save-excursion
-    (let (beg end)
-      (goto-char pos)
-      (if (not (web-mode-sb-client "{" min t))
-          (progn
-            (setq beg min)
-            (if (web-mode-sf-client ";" max t)
-                (setq end (1+ (point)))
-              (setq end max))
-            )
-        (setq beg (point))
-        (setq end (web-mode-closing-paren-position beg max))
-        (if end
-            (setq end (1+ end))
-          (setq end max)
-          )
-;;        (message "%S >>beg%S >>end%S" pos beg end)
-        (if (> pos end)
-
-            ;;selectors
-            (progn
-              (goto-char pos)
-              (if (web-mode-rsb-client "[};]" min t)
-                  (setq beg (1+ (point)))
-                (setq beg min)
-                )
-              (goto-char pos)
-              (if (web-mode-rsf-client "[{;]" max t)
-                  (cond
-                   ((eq (char-before) ?\;)
-                    (setq end (point))
-                    )
-                   (t
-                    (setq end (web-mode-closing-paren-position (1- (point)) max))
-                    (if end
-                        (setq end (1+ end))
-                      (setq end max))
-                    )
-                   ) ;cond
-                (setq end max)
-                )
-              ) ;progn selectors
-
-          ;; declaration
-          (goto-char beg)
-          (if (web-mode-rsb-client "[}{;]" min t)
-              (setq beg (1+ (point)))
-            (setq beg min)
-            )
-          )
-        )
-;;      (message "beg(%S) end(%S)" beg end)
-      (cons beg end)
-      )))
-
-(defun web-mode-velocity-skip-forward (pos)
-  "find the end of a velocity block."
-  (goto-char pos)
-  (let (continue)
-    (when (eq ?\# (char-after))
-      (forward-char))
-    ;;(message "pt=%S %c" (point) (char-after))
-    (when (member (char-after) '(?\$ ?\@))
-      ;;              (message "pt=%S" (point))
-      (forward-char))
-    (when (member (char-after) '(?\!))
-      ;;              (message "pt=%S" (point))
-      (forward-char))
-    (if (member (char-after) '(?\{))
-        (search-forward "}")
-      (setq continue t)
-      (while continue
-        (skip-chars-forward "a-zA-Z0-9_-")
-        (when (member (char-after) '(?\())
-          (search-forward ")")
-          )
-        (if (member (char-after) '(?\.))
-            (forward-char)
-          (setq continue nil))
-        ) ;while
-      ) ;if
-    ))
-
-(defun web-mode-razor-tag-exclude (block-beg block-end)
-  "Exclude HTML"
-  (save-excursion
-    (goto-char block-beg)
-;;    (message "block-beg(%S) block-end(%S)" block-beg block-end)
-    (let ((continue t) beg end tag-beg tag-end)
-      (while (re-search-forward "[ \t]*\\(</?[[:alpha:]].*?>\\)[ \n]*" block-end t)
-        (setq beg (match-beginning 0)
-              end (match-end 0)
-              tag-beg (match-beginning 1)
-              tag-end (match-end 1))
-;;        (message "beg(%S) end(%S)" beg end)
-        (remove-text-properties beg end '(block-side))
-        (put-text-property (1- tag-beg) tag-beg 'block-end t)
-        (put-text-property tag-end (1+ tag-end) 'block-beg t)
-        (when (and (looking-at "\\(.+\\)<")
-                   (not (string-match-p "@" (match-string-no-properties 1))))
-          (remove-text-properties (match-beginning 1) (match-end 1) '(block-side))
-          )
-        )
-      )))
-
-(defun web-mode-razor-skip-forward (pos)
-  "Find the end of a razor block."
-  (goto-char pos)
-  ;;            (message "pt=%S %c" (point) (char-after))
-  (let ((continue t) tmp (i 0))
-    (while continue
-      (setq i (1+ i))
-;;      (message "i=%S (%S)" i (point))
-      (skip-chars-forward " =@a-zA-Z0-9_-")
-      (cond
-       ((> i 500)
-        (message "*** invalid razor loop at (%S) ***" pos)
-        (setq continue nil))
-       ((looking-at-p "@[({]")
-        (forward-char)
-        (setq tmp (web-mode-closing-paren-position (point)))
-        (when tmp
-          (goto-char tmp))
-        (forward-char)
-        )
-       ((and (not (eobp)) (eq ?\( (char-after)))
-        (setq tmp (web-mode-closing-paren-position))
-        (when tmp
-          (goto-char tmp))
-        (forward-char)
-        )
-       ((and (not (eobp)) (eq ?\. (char-after)))
-        (forward-char))
-       ((looking-at-p "[ \n]*{")
-        (search-forward "{")
-        (backward-char)
-        (setq tmp (web-mode-closing-paren-position))
-        (when tmp
-          (goto-char tmp))
-        (forward-char)
-        )
-       (t
-        (setq continue nil))
-       ) ;cond
-      ) ;while
+    (let ((font-lock-keywords keywords)
+          (font-lock-multiline nil)
+          (font-lock-keywords-case-fold-search
+           (member web-mode-engine '("asp" "template-toolkit")))
+          (font-lock-keywords-only t)
+          (font-lock-extend-region-functions nil))
+      ;;      (message "%S" keywords)
+      (when (listp font-lock-keywords)
+        (font-lock-fontify-region beg end)
+        )
+      )
     ))
 
 (defun web-mode-colorize-foreground (color)
-  "Colorize foreground based on background luminance."
   (let* ((values (x-color-values color))
 	 (r (car values))
 	 (g (cadr values))
@@ -4242,7 +5816,6 @@
 	"white" "black")))
 
 (defun web-mode-colorize (beg end)
-  "Colorize CSS colors."
   (let (str plist len)
     (setq str (buffer-substring-no-properties beg end))
     (setq len (length str))
@@ -4262,34 +5835,73 @@
      ) ;cond
     ))
 
-
-(defun web-mode-fontify-region (beg end keywords)
-  "Font-lock region according to the keywords."
+(defun web-mode-interpolate-block-tag (beg end)
+  (save-excursion
+    (goto-char (+ 4 beg))
+    (setq end (1- end))
+    (while (re-search-forward "${.*?}" end t)
+      (remove-list-of-text-properties (match-beginning 0) (match-end 0) '(face))
+      (web-mode-fontify-region (match-beginning 0) (match-end 0)
+                               web-mode-uel-font-lock-keywords))
+    ))
+
+(defun web-mode-interpolate-javascript-string (beg end)
+  (save-excursion
+    (goto-char (1+ beg))
+    (setq end (1- end))
+    (while (re-search-forward "${.*?}" end t)
+      (put-text-property (match-beginning 0) (match-end 0)
+                         'font-lock-face
+                         'web-mode-variable-name-face)
+      )
+    ))
+
+;; todo : parsing plus compliqué: {$obj->values[3]->name}
+(defun web-mode-interpolate-block-string (beg end)
   (save-excursion
-;;    (message "beg=%S end=%S" beg end)
-    (let ((font-lock-keywords keywords)
-          (font-lock-multiline nil)
-          (font-lock-keywords-case-fold-search (member web-mode-engine
-                                                       '("asp"
-                                                         "template-toolkit")))
-          (font-lock-keywords-only t)
-          (font-lock-extend-region-functions nil))
-;;      (remove-text-properties beg end '(font-lock-face nil))
-;;      (message "%S" web-mode-mako-tag-font-lock-keywords)
-      (when (listp font-lock-keywords)
-        (font-lock-fontify-region beg end))
-      ))
-
-  ;; UGLY HACK / workaround (help needed)
-
-  ;; (unless web-mode-buffer-highlighted
-  ;;   (setq web-mode-buffer-highlighted t)
-  ;;   (web-mode-fontify-region beg end keywords))
-
-  )
+    (goto-char (1+ beg))
+    (setq end (1- end))
+    (cond
+     ((string= web-mode-engine "php")
+      (while (re-search-forward "$[[:alnum:]_]+\\(->[[:alnum:]_]+\\)*\\|{[ ]*$.+?}" end t)
+;;        (message "%S > %S" (match-beginning 0) (match-end 0))
+        (remove-list-of-text-properties (match-beginning 0) (match-end 0) '(font-lock-face))
+        (web-mode-fontify-region (match-beginning 0) (match-end 0)
+                                 web-mode-php-var-interpolation-font-lock-keywords)
+        ))
+     ((string= web-mode-engine "erb")
+      (while (re-search-forward "#{.*?}" end t)
+        (remove-list-of-text-properties (match-beginning 0) (match-end 0) '(font-lock-face))
+        (put-text-property (match-beginning 0) (match-end 0)
+                           'font-lock-face 'web-mode-variable-name-face)
+        ))
+     ) ;cond
+    ))
+
+(defun web-mode-interpolate-comment (beg end block-side)
+  (save-excursion
+    (let ((regexp (concat "\\_<\\(" web-mode-comment-keywords "\\)\\_>")))
+      (goto-char beg)
+      (while (re-search-forward regexp end t)
+        (font-lock-prepend-text-property (match-beginning 1) (match-end 1)
+                                         'font-lock-face
+                                         'web-mode-comment-keyword-face)
+        ) ;while
+      )))
+
+(defun web-mode-interpolate-sql-string (beg end)
+  (save-excursion
+    (let ((case-fold-search t)
+          (regexp (concat "\\_<\\(" web-mode-sql-keywords "\\)\\_>")))
+      (goto-char beg)
+      (while (re-search-forward regexp end t)
+        (font-lock-prepend-text-property (match-beginning 1) (match-end 1)
+                                         'font-lock-face
+                                         'web-mode-sql-keyword-face)
+        ) ;while
+      )))
 
 (defun web-mode-fill-paragraph (&optional justify)
-  "fill paragraph"
   (save-excursion
     (let ((pos (point)) fill-coll
           prop pair beg end delim-beg delim-end chunk fill-col)
@@ -4319,14 +5931,10 @@
             (setq delim-beg "<!--"
                   delim-end "-->"))
            )
-          ;;          (subst-char-in-region beg end ?\n ?\s)
-          ;;          (message "fill-column=%S pt=%S pair=%S chunk=%S"
-          ;;                   fill-column (point) pair chunk)
           )
         ) ;comment - case
 
        ((web-mode-is-content)
-;;        (message "prop(%S)" prop)
         (setq pair (web-mode-content-boundaries pos))
         (setq beg (car pair)
               end (cdr pair))
@@ -4335,8 +5943,7 @@
        ) ;cond
 ;;      (message "beg(%S) end(%S)" beg end)
       (when (and beg end)
-        (fill-region beg end)
-        (indent-for-tab-command))
+        (fill-region beg end))
       t)))
 
 (defun web-mode-property-boundaries (prop &optional pos)
@@ -4358,32 +5965,7 @@
         (when (null end) (setq end (point-min))))
       (cons beg end))))
 
-;; verifier avec text-property-any si 'block-side
-(defun web-mode-content-apply (&optional fun)
-  "web-mode-content-apply"
-  (interactive)
-  (save-excursion
-    (let (beg (i 0) (continue t))
-      (goto-char (point-min))
-      (when (get-text-property (point) 'tag-type)
-        (web-mode-tag-end)
-        (setq beg (point)))
-      (while (and continue
-                  (or (get-text-property (point) 'tag-beg)
-                      (web-mode-tag-next)))
-        (setq i (1+ i))
-        (when (> i 2000)
-          (setq continue nil))
-        (when (and beg (> (point) beg))
-          (message "content=%S > %S" beg (point)))
-        (if (web-mode-tag-end)
-            (setq beg (point))
-          (setq continue nil))
-        ) ;while
-      )))
-
 (defun web-mode-content-boundaries (&optional pos)
-  "Text boundaries"
   (unless pos (setq pos (point)))
   (let (beg end)
     (setq beg (or (previous-property-change pos (current-buffer))
@@ -4394,23 +5976,42 @@
       (setq beg (1+ beg)))
     (while (and (> end beg) (member (char-after (1- end)) '(?\s ?\n)))
       (setq end (1- end)))
-    (message "beg(%S) end(%S)" beg end)
+;;    (message "beg(%S) end(%S)" beg end)
     (cons beg end)
     ))
 
-(defun web-mode-coord-pos (line column)
-  "Return pos at Line / Column pos"
-  (save-excursion
-    (when (stringp line) (setq line (string-to-number line)))
-    (when (stringp column) (setq column (string-to-number column)))
-;;    (message "%d %d" line column)
-;;    (beginning-of-buffer)
-;;    (forward-line line)
-    (goto-char (point-min))
-    (forward-line (1- line))
-    (move-to-column (1- column))
-    (point))
-  )
+(defun web-mode-engine-syntax-check ()
+  (interactive)
+  (let ((proc nil)
+        (errors nil)
+        (file (concat temporary-file-directory "emacs-web-mode-tmp")))
+    (write-region (point-min) (point-max) file)
+    (cond
+     ;;       ((null (buffer-file-name))
+     ;;        )
+     ((string= web-mode-engine "php")
+      (setq proc (start-process "php-proc" nil "php" "-l" file))
+      (set-process-filter proc
+                          (lambda (proc output)
+                            (cond
+                             ((string-match-p "No syntax errors" output)
+                              (message "No syntax errors")
+                              )
+                             (t
+;;                              (setq output (replace-regexp-in-string temporary-file-directory "" output))
+;;                              (message output)
+                              (message "Syntax error")
+                              (setq errors t))
+                             ) ;cond
+;;                            (delete-file file)
+                            ) ;lambda
+                          )
+      ) ;php
+     (t
+      (message "no syntax checker found")
+      ) ;t
+     ) ;cond
+    errors))
 
 (defun web-mode-jshint ()
   "Run JSHint on all the JavaScript parts."
@@ -4420,7 +6021,9 @@
       (setq proc (start-process
                   "jshint-proc"
                   nil
-                  "jshint" "--extract=auto" (buffer-file-name)))
+                  (or (executable-find "jshint") "/usr/local/bin/jshint")
+                  "--extract=auto"
+                  (buffer-file-name)))
       (setq web-mode-jshint-errors 0)
       (set-process-filter proc
                           (lambda (proc output)
@@ -4431,8 +6034,9 @@
                                       output offset)
                                 (setq web-mode-jshint-errors (1+ web-mode-jshint-errors))
                                 (setq offset (match-end 0))
-                                (setq pos (web-mode-coord-pos (match-string-no-properties 1 output)
-                                                              (match-string-no-properties 2 output)))
+                                (setq pos (web-mode-coord-position
+                                           (match-string-no-properties 1 output)
+                                           (match-string-no-properties 2 output)))
                                 (when (get-text-property pos 'tag-beg)
                                   (setq pos (1- pos)))
                                 (when (not (= pos old))
@@ -4441,9 +6045,9 @@
                                   (overlay-put overlay 'font-lock-face 'web-mode-error-face)
                                   )
                                 (setq msg (or (overlay-get overlay 'help-echo)
-                                               (concat "l="
+                                               (concat "line="
                                                        (match-string-no-properties 1 output)
-                                                       " c="
+                                                       " column="
                                                        (match-string-no-properties 2 output)
                                                        )))
                                 (overlay-put overlay 'help-echo
@@ -4508,10 +6112,7 @@
           ) ;while
 
         (dotimes (i i)
-          (setq tags (cdr tags))
-;;          (setq cell (nth i tags))
-;;          (message "removing=%S" cell)
-          )
+          (setq tags (cdr tags)))
 
         )
        ) ;cond
@@ -4519,37 +6120,257 @@
         (setq continue nil))
       ) ;while
     (message "%S error(s) detected" errors)
-    (if (> errors 0)
-        (progn (goto-char first)
-               (recenter))
-      (goto-char ori)
-      ) ;if
+    (if (< errors 1)
+        (goto-char ori)
+      (goto-char first)
+      (recenter))
     ;;    (message "%S" tags)
     ))
 
+(defun web-mode-highlight-elements (beg end)
+  (save-excursion
+    (goto-char beg)
+    (let ((continue (or (get-text-property (point) 'tag-beg) (web-mode-tag-next)))
+          (i 0) (ctx nil) (face nil))
+      (while continue
+        (cond
+         ((> (setq i (1+ i)) 1000)
+          (message "highlight-elements ** too much tags **")
+          (setq continue nil))
+         ((> (point) end)
+          (setq continue nil))
+         ((not (get-text-property (point) 'tag-beg))
+          (setq continue nil))
+         ((eq (get-text-property (point) 'tag-type) 'start)
+          (when (and (setq ctx (web-mode-element-boundaries (point)))
+                     (<= (car (cdr ctx)) end)
+                     (setq face (cdr (assoc (get-text-property (point) 'tag-name) web-mode-element-content-faces))))
+            (font-lock-prepend-text-property (1+ (cdr (car ctx))) (car (cdr ctx))
+                                             'font-lock-face face))
+          )
+         ) ;cond
+        (when (not (web-mode-tag-next))
+          (setq continue nil))
+        ) ;while
+      )))
+
+(defun web-mode-enable (feature)
+  "Enable one feature."
+  (interactive
+   (list (completing-read
+          "Feature: "
+          (let (features)
+            (dolist (elt web-mode-features)
+              (setq features (append features (list (car elt)))))
+            features))))
+  (when (and (or (not feature) (< (length feature) 1)) web-mode-last-enabled-feature)
+    (setq feature web-mode-last-enabled-feature))
+  (when feature
+    (setq web-mode-last-enabled-feature feature)
+    (setq feature (cdr (assoc feature web-mode-features)))
+    (cond
+     ((eq feature 'web-mode-enable-current-column-highlight)
+      (web-mode-column-show))
+     ((eq feature 'web-mode-enable-current-element-highlight)
+      (when (not web-mode-enable-current-element-highlight)
+        (web-mode-toggle-current-element-highlight))
+      )
+     ((eq feature 'web-mode-enable-whitespace-fontification)
+      (web-mode-whitespaces-on))
+     (t
+      (set feature t)
+      (web-mode-buffer-highlight))
+     )
+    ) ;when
+  )
+
+(defun web-mode-disable (feature)
+  "Disable one feature."
+  (interactive
+   (list (completing-read
+          "Feature: "
+          (let (features)
+            (dolist (elt web-mode-features)
+              (setq features (append features (list (car elt)))))
+            features))))
+  (when (and (or (not feature) (< (length feature) 1)) web-mode-last-enabled-feature)
+    (setq feature web-mode-last-enabled-feature))
+  (when feature
+    (setq feature (cdr (assoc feature web-mode-features)))
+    (cond
+     ((eq feature 'web-mode-enable-current-column-highlight)
+      (web-mode-column-hide))
+     ((eq feature 'web-mode-enable-current-element-highlight)
+      (when web-mode-enable-current-element-highlight
+        (web-mode-toggle-current-element-highlight))
+      )
+     ((eq feature 'web-mode-enable-whitespace-fontification)
+      (web-mode-whitespaces-off))
+     (t
+      (set feature nil)
+      (web-mode-buffer-highlight))
+     )
+    ) ;when
+  )
+
+(defun web-mode-make-tag-overlays ()
+  (unless web-mode-overlay-tag-start
+    (setq web-mode-overlay-tag-start (make-overlay 1 1)
+          web-mode-overlay-tag-end (make-overlay 1 1))
+    (overlay-put web-mode-overlay-tag-start
+                 'font-lock-face
+                 'web-mode-current-element-highlight-face)
+    (overlay-put web-mode-overlay-tag-end
+                 'font-lock-face
+                 'web-mode-current-element-highlight-face)))
+
+(defun web-mode-delete-tag-overlays ()
+  (when web-mode-overlay-tag-start
+    (delete-overlay web-mode-overlay-tag-start)
+    (delete-overlay web-mode-overlay-tag-end)))
+
+(defun web-mode-column-overlay-factory (index)
+  (let (overlay)
+    (when (null web-mode-column-overlays)
+      (dotimes (i 100)
+        (setq overlay (make-overlay 1 1))
+        (overlay-put overlay 'font-lock-face 'web-mode-current-column-highlight-face)
+        (setq web-mode-column-overlays (append web-mode-column-overlays (list overlay)))
+        )
+      ) ;when
+    (setq overlay (nth index web-mode-column-overlays))
+    (when (null overlay)
+      (setq overlay (make-overlay 1 1))
+      (overlay-put overlay 'font-lock-face 'web-mode-current-column-highlight-face)
+      (setq web-mode-column-overlays (append web-mode-column-overlays (list overlay)))
+      ) ;when
+    overlay))
+
+(defun web-mode-column-hide ()
+  (setq web-mode-enable-current-column-highlight nil)
+  (remove-overlays (point-min) (point-max)
+                   'font-lock-face
+                   'web-mode-current-column-highlight-face))
+
+(defun web-mode-column-show ()
+  (let ((index 0) overlay diff column line-to line-from)
+    (web-mode-column-hide)
+    (setq web-mode-enable-current-column-highlight t)
+    (save-excursion
+      (back-to-indentation)
+      (setq column (current-column)
+            line-to (web-mode-line-number))
+      (when (and (get-text-property (point) 'tag-beg)
+                 (member (get-text-property (point) 'tag-type) '(start end))
+                 (web-mode-tag-match)
+                 (setq line-from (web-mode-line-number))
+                 (not (= line-from line-to)))
+        (when (> line-from line-to)
+          (let (tmp)
+            (setq tmp line-from)
+            (setq line-from line-to)
+            (setq line-to tmp))
+          ) ;when
+        ;;(message "column(%S) line-from(%S) line-to(%S)" column line-from line-to)
+        (goto-char (point-min))
+        (when (> line-from 1)
+          (forward-line (1- line-from)))
+        (while (<= line-from line-to)
+          (setq overlay (web-mode-column-overlay-factory index))
+          (setq diff (- (line-end-position) (point)))
+          (cond
+           ((or (and (= column 0) (= diff 0))
+                (> column diff))
+            (end-of-line)
+            (move-overlay overlay (point) (point))
+            (overlay-put overlay
+                         'after-string
+                         (concat
+                          (if (> column diff) (make-string (- column diff) ?\s) "")
+                          (propertize " "
+                                      'font-lock-face
+                                      'web-mode-current-column-highlight-face)
+                          ) ;concat
+                         )
+            )
+           (t
+            (move-to-column column)
+            (overlay-put overlay 'after-string nil)
+            (move-overlay overlay (point) (1+ (point)))
+            )
+           ) ;cond
+          (setq line-from (1+ line-from))
+          (forward-line)
+          (setq index (1+ index))
+          ) ;while
+        ) ;when
+      ) ;save-excursion
+    ) ;let
+  )
+
+(defun web-mode-highlight-current-element ()
+  (let ((ctx (web-mode-element-boundaries)) len)
+    (cond
+     ((null ctx)
+      (web-mode-delete-tag-overlays))
+     (t
+      (web-mode-make-tag-overlays)
+      (setq len (length (get-text-property (caar ctx) 'tag-name)))
+      (move-overlay web-mode-overlay-tag-start (+ (caar ctx) 1) (+ (caar ctx) 1 len))
+      (move-overlay web-mode-overlay-tag-end (+ (cadr ctx) 2) (+ (cadr ctx) 2 len))
+      ) ;t
+     ) ;cond
+    ))
+
+(defun web-mode-highlight-whitespaces (beg end)
+  (save-excursion
+    (goto-char beg)
+    (while (re-search-forward web-mode-whitespaces-regexp end t)
+      (add-text-properties (match-beginning 0) (match-end 0)
+                           '(face web-mode-whitespace-face))
+      ) ;while
+    ))
+
 (defun web-mode-whitespaces-show ()
   "Toggle whitespaces."
   (interactive)
-  (if web-mode-enable-whitespaces
+  (if web-mode-enable-whitespace-fontification
       (web-mode-whitespaces-off)
-    (web-mode-whitespaces-on))
-  (web-mode-scan-buffer))
+    (web-mode-whitespaces-on)))
 
 (defun web-mode-whitespaces-on ()
   "Show whitespaces."
   (interactive)
-  (when web-mode-hl-line-mode-flag
-    (global-hl-line-mode -1))
   (when web-mode-display-table
     (setq buffer-display-table web-mode-display-table))
-  (setq web-mode-enable-whitespaces t))
+  (setq web-mode-enable-whitespace-fontification t))
 
 (defun web-mode-whitespaces-off ()
-  "Hide whitespaces."
-  (when web-mode-hl-line-mode-flag
-    (global-hl-line-mode 1))
   (setq buffer-display-table nil)
-  (setq web-mode-enable-whitespaces nil))
+  (setq web-mode-enable-whitespace-fontification nil))
+
+(defun web-mode-use-tabs ()
+  "Tweaks vars to be compatible with TAB indentation."
+  (let (offset)
+    (setq web-mode-block-padding 0)
+    (setq web-mode-script-padding 0)
+    (setq web-mode-style-padding 0)
+    (setq offset
+          (cond
+           ((and (boundp 'tab-width) tab-width) tab-width)
+           ((and (boundp 'standard-indent) standard-indent) standard-indent)
+           (t 4)))
+    ;;    (message "offset(%S)" offset)
+    (setq web-mode-attr-indent-offset offset)
+    (setq web-mode-code-indent-offset offset)
+    (setq web-mode-css-indent-offset offset)
+    (setq web-mode-markup-indent-offset offset)
+    (setq web-mode-sql-indent-offset offset)
+    (add-to-list 'web-mode-indentation-params '("lineup-args" . nil))
+    (add-to-list 'web-mode-indentation-params '("lineup-calls" . nil))
+    (add-to-list 'web-mode-indentation-params '("lineup-concats" . nil))
+    (add-to-list 'web-mode-indentation-params '("lineup-ternary" . nil))
+    ))
 
 (defun web-mode-buffer-indent ()
   "Indent all buffer."
@@ -4557,16 +6378,8 @@
   (indent-region (point-min) (point-max))
   (delete-trailing-whitespace))
 
-(defun web-mode-buffer-refresh ()
-  "Indent and fontify buffer."
-  (interactive)
-;;  (put-text-property (point-min) (point-max) 'invisible nil)
-;;  (remove-overlays)
-  (web-mode-scan-buffer)
-  (web-mode-buffer-indent))
-
 (defun web-mode-buffer-change-tag-case (&optional type)
-  "Change HTML tag case."
+  "Change html tag case."
   (interactive)
   (save-excursion
     (goto-char (point-min))
@@ -4577,7 +6390,7 @@
         (setq continue nil))
       (while continue
         (skip-chars-forward "<!/")
-        (if (looking-at "\\([[:alnum:]-]+\\)")
+        (if (looking-at "\\([[:alnum:]:-]+\\)")
             (replace-match (funcall f (match-string 0)) t))
 ;;        (message "tag: %S (%S)"
 ;;                 (get-text-property (point) 'tag-name)
@@ -4588,49 +6401,50 @@
       )))
 
 (defun web-mode-buffer-change-attr-case (&optional type)
-  "alter tag case"
+  "Change case of html attribute names."
   (interactive)
+  (unless type (setq type "downcase"))
   (save-excursion
     (goto-char (point-min))
-    (let ((continue t) f)
-      (setq f (if (member type '("upper" "uppercase" "upper-case")) 'uppercase 'downcase))
+    (let ((continue t)
+          (fun (if (eq (aref (downcase type) 0) ?u) 'uppercase 'downcase)))
       (while continue
-        (if (web-mode-attr-next)
-            (when (looking-at "\\([[:alnum:]-]+\\)")
-              (replace-match (funcall f (match-string 0)) t)
-;;              (message "tag: %S (%S)" (match-string 0) (point))
-              ) ;when
+        (cond
+         ((not (web-mode-attribute-next))
           (setq continue nil))
+         ((looking-at "\\([[:alnum:]-]+\\)")
+          (replace-match (funcall fun (match-string 0)) t)
+          )
+         ) ;cond
         ) ;while
       )))
 
-;; todo : passer de règle en règle et mettre un \n à la fin
+;; todo : passer de règle en règle et mettre un \n à la fin
 (defun web-mode-css-indent ()
-  "Indent CSS parts"
-  (interactive)
   (save-excursion
     (goto-char (point-min))
     (let ((continue t) rule part-end)
       (while continue
-        (if (web-mode-part-next)
-            (when (eq (get-text-property (point) 'part-side) 'css)
-              (setq part-end (web-mode-part-end-position))
-              (while (setq rule (web-mode-css-next-rule part-end))
-                (when (not (looking-at-p "[[:space:]]*\\($\\|<\\)"))
-                  (newline)
-                  (indent-for-tab-command)
-                  (setq part-end (web-mode-part-end-position)))
-                )
-              )
-          (setq continue nil)
-          )
+        (cond
+         ((not (web-mode-part-next))
+          (setq continue nil))
+         ((eq (get-text-property (point) 'part-side) 'css)
+          (setq part-end (web-mode-part-end-position))
+          (while (setq rule (web-mode-css-rule-next part-end))
+            (when (not (looking-at-p "[[:space:]]*\\($\\|<\\)"))
+              (newline)
+              (indent-according-to-mode)
+              (setq part-end (web-mode-part-end-position)))
+            )
+          )
+         ) ;cond
         )
       )))
 
 ;; tag-case=lower|upper-case , attr-case=lower|upper-case
 ;; special-chars=unicode|html-entities
 ;; smart-apostrophes=bool , smart-quotes=bool , indentation=bool
-(defun web-mode-buffer-normalize ()
+(defun web-mode-dom-normalize ()
   "Normalize buffer"
   (interactive)
   (save-excursion
@@ -4660,48 +6474,1043 @@
         (web-mode-buffer-indent))
       )))
 
-(defun web-mode-previous-usable-server-line ()
-  "Return previous non blank/comment/string line and return this line (trimmed)."
-  (interactive)
+;;---- INDENTATION -------------------------------------------------------------
+
+(defun web-mode-point-context (pos)
+  "POS should be at the beginning of the indentation."
   (save-excursion
-    (let ((continue t)
-          (line "")
-          (pos (point)))
+    (let (curr-char curr-indentation curr-line
+          language
+          options
+          reg-beg reg-col
+          prev-char prev-indentation prev-line prev-pos
+          token
+          part-language
+          depth)
+
+      (setq reg-beg (point-min)
+            reg-col 0
+            token "live"
+            options ""
+            language ""
+            prev-line ""
+            prev-char 0
+            prev-pos nil)
+
+      (when (get-text-property pos 'part-side)
+        (setq part-language (symbol-name (get-text-property pos 'part-side))))
+
+      (cond
+
+       ((and (bobp) (member web-mode-content-type '("html" "xml")))
+        (setq language web-mode-content-type)
+        )
+
+       ((string= web-mode-content-type "css")
+        (setq language "css"
+              curr-indentation web-mode-css-indent-offset))
+
+       ((member web-mode-content-type '("javascript" "json"))
+        (setq language "javascript"
+              curr-indentation web-mode-code-indent-offset))
+
+       ((or (string= web-mode-content-type "jsx")
+            (and part-language (string= part-language "jsx")))
+        (setq language "jsx"
+              curr-indentation web-mode-code-indent-offset)
+        (cond
+         ((web-mode-jsx-is-html pos)
+          (setq curr-indentation web-mode-markup-indent-offset
+                options "is-html"))
+         ((and (setq depth (get-text-property pos 'jsx-depth)) (> depth 1))
+          (when (get-text-property pos 'jsx-beg)
+            (setq depth (1- depth)))
+          (setq reg-beg (web-mode-jsx-depth-beginning-position pos depth))
+          (setq reg-beg (1+ reg-beg))
+          (save-excursion
+            (goto-char reg-beg)
+            (cond
+             ((and (not (looking-at-p "[ ]*$"))
+                   (looking-back "^[[:space:]]*{"))
+              (setq reg-col (+ (current-indentation) 1
+                               (cond
+                                ((looking-at "[ ]+") (length (match-string-no-properties 0)))
+                                (t 0))
+                               ))
+              )
+             ((looking-at-p "[ ]*\\[[ ]*$") ;; #0659
+              (setq reg-col (current-indentation))
+             )
+             (t
+              ;;(message "%S %S : %S %S" (point) (current-indentation) web-mode-code-indent-offset)
+              ;;(setq reg-col (+ (current-indentation) web-mode-code-indent-offset web-mode-jsx-expression-padding)))
+              (setq reg-col (+ (current-indentation) web-mode-code-indent-offset)))
+             )
+
+            ;;(message "%S %S %S" (point) (current-indentation) reg-col)
+            ) ;save-excursion
+          )
+         ((string= web-mode-content-type "jsx")
+          (setq reg-beg (point-min)))
+         (t
+          (setq reg-beg (or (web-mode-part-beginning-position pos) (point-min)))
+          (save-excursion
+            (goto-char reg-beg)
+            (search-backward "<" nil t)
+            (setq reg-col (current-column))
+            ) ;save-excursion
+          )
+         ) ;cond
+        ;;(message "jsx reg-beg=%S" reg-beg)
+        ) ;jsx
+
+       ((string= web-mode-content-type "php")
+        (setq language "php"
+              curr-indentation web-mode-code-indent-offset))
+
+       ((or (string= web-mode-content-type "xml"))
+        (setq language "xml"
+              curr-indentation web-mode-markup-indent-offset))
+
+       ;; TODO: est ce util ?
+       ((and (get-text-property pos 'tag-beg)
+             (get-text-property pos 'tag-name)
+             ;;(not (get-text-property pos 'part-side))
+             )
+        (setq language "html"
+              curr-indentation web-mode-markup-indent-offset))
+
+       ((and (get-text-property pos 'block-side)
+             (not (get-text-property pos 'block-beg)))
+
+        (setq reg-beg (or (web-mode-block-beginning-position pos) (point-min)))
+        (goto-char reg-beg)
+        (setq reg-col (current-column))
+        (setq language web-mode-engine)
+        (setq curr-indentation web-mode-code-indent-offset)
+
+        (cond
+         ((string= web-mode-engine "blade")
+          (save-excursion
+            (when (web-mode-rsf "{[{!]+[ ]*")
+              (setq reg-col (current-column))))
+          (setq reg-beg (+ reg-beg 2))
+          )
+         ((string= web-mode-engine "razor")
+          (setq reg-beg (+ reg-beg 2))
+          )
+         ;; tests/demo.chtml
+         ((string= web-mode-engine "ctemplate")
+          (save-excursion
+            (when (web-mode-rsf "{{#?")
+              (setq reg-col (current-column))))
+          )
+         ((string= web-mode-engine "dust")
+          (save-excursion
+            (when (web-mode-rsf "{@")
+              (setq reg-col (current-column))))
+          )
+         ((string= web-mode-engine "template-toolkit")
+          (setq reg-beg (+ reg-beg 3)
+                reg-col (+ reg-col 3))
+          )
+         ((and (string= web-mode-engine "jsp")
+               ;;(web-mode-looking-at "<%@\\|<[[:alpha:]]" reg-beg))
+               (web-mode-looking-at "<%@" reg-beg))
+          (save-excursion
+            (goto-char reg-beg)
+            (looking-at "<%@[ ]*[[:alpha:]]+[ ]+\\|</?[[:alpha:]]+[:.][[:alpha:]]+[ ]+")
+            (goto-char (match-end 0))
+            (setq reg-col (current-column))
+            )
+          )
+         ((and (string= web-mode-engine "freemarker")
+               (web-mode-looking-at "<@\\|<%@\\|<[[:alpha:]]" reg-beg))
+          (save-excursion
+            (goto-char reg-beg)
+            (looking-at "<@[[:alpha:].]+[ ]+\\|<%@[ ]*[[:alpha:]]+[ ]+\\|<[[:alpha:]]+:[[:alpha:]]+[ ]+")
+            (goto-char (match-end 0))
+            (setq reg-col (current-column))
+            )
+          )
+         ) ;cond
+        ) ;block-side
+
+       ((and part-language (member part-language '("css" "javascript")))
+        (setq reg-beg (or (web-mode-part-beginning-position pos) (point-min)))
+        (goto-char reg-beg)
+        (search-backward "<" nil t)
+        (setq reg-col (current-column))
+        (setq language part-language)
+        (cond
+         ((string= language "css")
+          (setq curr-indentation web-mode-css-indent-offset))
+         (t
+          (setq language "javascript"
+                curr-indentation web-mode-code-indent-offset))
+         )
+        ) ;part-side
+
+       (t
+        (setq language "html"
+              curr-indentation web-mode-markup-indent-offset)
+        )
+
+       ) ;cond
+
+      (cond
+       ((or (and (> pos (point-min))
+                 (eq (get-text-property pos 'part-token) 'comment)
+                 (eq (get-text-property (1- pos) 'part-token) 'comment)
+                 (progn
+                   (setq reg-beg (previous-single-property-change pos 'part-token))
+                   t))
+            (and (> pos (point-min))
+                 (eq (get-text-property pos 'block-token) 'comment)
+                 (eq (get-text-property (1- pos) 'block-token) 'comment)
+                 (progn
+                   (setq reg-beg (previous-single-property-change pos 'block-token))
+                   t))
+            (and (> pos (point-min))
+                 (eq (get-text-property pos 'tag-type) 'comment)
+                 (not (get-text-property pos 'tag-beg))
+                 (progn
+                   (setq reg-beg (web-mode-tag-beginning-position pos))
+                   t))
+            )
+        (setq token "comment"))
+       ((or (and (> pos (point-min))
+                 (member (get-text-property pos 'part-token)
+                         '(string context key))
+                 (member (get-text-property (1- pos) 'part-token)
+                         '(string context key)))
+            (and (eq (get-text-property pos 'block-token) 'string)
+                 (eq (get-text-property (1- pos) 'block-token) 'string)))
+        (setq token "string"))
+       )
+
+      (goto-char pos)
+      (setq curr-line (web-mode-trim
+                       (buffer-substring-no-properties
+                        (line-beginning-position)
+                        (line-end-position))))
+      (setq curr-char (if (string= curr-line "") 0 (aref curr-line 0)))
+
+      (when (or (member language '("php" "blade" "javascript" "jsx" "razor"))
+                (and (member language '("html" "xml"))
+                     (not (eq ?\< curr-char))))
+        (let (prev)
+          (cond
+           ((member language '("html" "xml" "javascript" "jsx"))
+            (when (setq prev (web-mode-part-previous-live-line reg-beg))
+              (setq prev-line (nth 0 prev)
+                    prev-indentation (nth 1 prev)
+                    prev-pos (nth 2 prev))
+              )
+            )
+           ((setq prev (web-mode-block-previous-live-line))
+            (setq prev-line (car prev)
+                  prev-indentation (cdr prev))
+            (setq prev-line (web-mode-clean-block-line prev-line)))
+           ) ;cond
+          ) ;let
+        (when (>= (length prev-line) 1)
+          (setq prev-char (aref prev-line (1- (length prev-line))))
+          (setq prev-line (substring-no-properties prev-line))
+          )
+        )
+
+      (cond
+       ((not (member web-mode-content-type '("html" "xml")))
+        )
+       ((member language '("javascript" "jsx"))
+        (setq reg-col (+ reg-col web-mode-script-padding)))
+       ((member language '("css"))
+        (setq reg-col (+ reg-col web-mode-style-padding)))
+       ((not (member language '("html" "xml" "razor")))
+        (setq reg-col (+ reg-col web-mode-block-padding)))
+       )
+
+      (list :curr-char curr-char
+            :curr-indentation curr-indentation
+            :curr-line curr-line
+            :language language
+            :options options
+            :prev-char prev-char
+            :prev-indentation prev-indentation
+            :prev-line prev-line
+            :prev-pos prev-pos
+            :reg-beg reg-beg
+            :reg-col reg-col
+            :token token)
+      )))
+
+(defun web-mode-indent-line ()
+
+  (web-mode-propertize)
+
+  (let ((offset nil)
+        (char nil)
+        (inhibit-modification-hooks t)
+        (adjust t))
+
+    (save-excursion
+      (back-to-indentation)
+      (setq char (char-after))
+      (let* ((pos (point))
+             (ctx (web-mode-point-context pos))
+             (curr-char (plist-get ctx :curr-char))
+             (curr-indentation (plist-get ctx :curr-indentation))
+             (curr-line (plist-get ctx :curr-line))
+             (language (plist-get ctx :language))
+             (prev-char (plist-get ctx :prev-char))
+             (prev-indentation (plist-get ctx :prev-indentation))
+             (prev-line (plist-get ctx :prev-line))
+             (prev-pos (plist-get ctx :prev-pos))
+             (reg-beg (plist-get ctx :reg-beg))
+             (reg-col (plist-get ctx :reg-col))
+             (token (plist-get ctx :token))
+             (options (plist-get ctx :options))
+             (chars (list curr-char prev-char)))
+
+        ;;(message "curr-char=[%c] prev-char=[%c]\n%S" curr-char prev-char ctx)
+
+        (cond
+
+         ((or (bobp) (= (line-number-at-pos pos) 1))
+          (setq offset 0))
+
+         ((string= token "string")
+          (cond
+           ((and web-mode-enable-sql-detection
+                 (web-mode-block-token-starts-with (concat "[ \n]*" web-mode-sql-queries)))
+            (save-excursion
+              (let (col)
+                (web-mode-block-string-beginning)
+                (skip-chars-forward "[ \"'\n]")
+                (setq col (current-column))
+                (goto-char pos)
+                (if (looking-at-p "\\(SELECT\\|INSERT\\|DELETE\\|UPDATE\\|FROM\\|LEFT\\|JOIN\\|WHERE\\|GROUP BY\\|LIMIT\\|HAVING\\|\)\\)")
+                    (setq offset col)
+                  (setq offset (+ col web-mode-sql-indent-offset)))
+                )
+              ) ;save-excursion
+            )
+           (t
+            (setq offset nil))
+           ) ;cond
+          ) ;case string
+
+         ((string= token "comment")
+          (if (eq (get-text-property pos 'tag-type) 'comment)
+              (web-mode-tag-beginning)
+            (goto-char (car
+                        (web-mode-property-boundaries
+                         (if (eq (get-text-property pos 'part-token) 'comment)
+                             'part-token
+                           'block-token)
+                         pos))))
+          (setq offset (current-column))
+          (cond
+           ((string= web-mode-engine "freemarker")
+            (setq offset (+ (current-indentation) 2)))
+           ((member (buffer-substring-no-properties (point) (+ (point) 2)) '("/*" "{*" "@*"))
+            (cond
+             ((eq ?\* curr-char)
+              (setq offset (+ offset 1)))
+             (t
+              (setq offset (+ offset 3)))
+             ) ;cond
+            )
+           ((string= (buffer-substring-no-properties (point) (+ (point) 4)) "<!--")
+            (cond
+             ((string-match-p "^<!\\[endif" curr-line)
+              )
+             ((looking-at-p "<!--\\[if")
+              (setq offset (+ offset web-mode-markup-indent-offset)))
+             ((eq ?\- curr-char)
+              (setq offset (+ offset 3)))
+             (t
+              (setq offset (+ offset 5)))
+             ) ;cond
+            )
+           ((and (string= web-mode-engine "django") (looking-back "{% comment %}"))
+            (setq offset (- offset 12)))
+           ((and (string= web-mode-engine "mako") (looking-back "<%doc%>"))
+            (setq offset (- offset 6)))
+           ((and (string= web-mode-engine "mason") (looking-back "<%doc%>"))
+            (setq offset (- offset 6)))
+           ) ;cond
+          ) ;case comment
+
+         ((and (string= web-mode-engine "mason")
+               (string-match-p "^%" curr-line))
+          (setq offset 0))
+
+         ((and (get-text-property pos 'block-beg)
+               (or (web-mode-block-is-close pos)
+                   (web-mode-block-is-inside pos)))
+          (when (web-mode-block-match)
+            (setq offset (current-indentation))))
+
+         ((eq (get-text-property pos 'block-token) 'delimiter-end)
+          (when (web-mode-block-beginning)
+            (setq reg-col (current-indentation))
+            (setq offset (current-column))))
+
+         ((and (get-text-property pos 'tag-beg)
+               (eq (get-text-property pos 'tag-type) 'end))
+          (when (web-mode-tag-match)
+            (setq offset (current-indentation))))
+
+         ((and (member language '("jsx"))
+               (eq curr-char ?\})
+               (get-text-property pos 'jsx-end))
+          (web-mode-go (1- reg-beg))
+          (setq reg-col nil)
+          (setq offset (current-column)))
+
+         ((and (member language '("html" "xml" "javascript" "jsx"))
+               (get-text-property pos 'tag-type)
+               (not (get-text-property pos 'tag-beg))
+               (or (not (string= language "jsx"))
+                   (string= options "is-html")))
+          ;;(message "html-attr")
+          (cond
+           ((and (get-text-property pos 'tag-attr)
+                 (get-text-property (1- pos) 'tag-attr)
+                 (web-mode-attribute-beginning))
+
+            (cond
+             ((eq (logand (get-text-property (point) 'tag-attr-beg) 8) 8)
+              (setq offset nil))
+             ((and web-mode-attr-value-indent-offset (web-mode-tag-beginning))
+              (setq offset (+ (current-column) web-mode-attr-value-indent-offset)))
+             (t
+              (web-mode-dom-rsf "=[ ]*[\"']?" pos)
+              (setq offset (current-column)))
+             ) ;cond
+
+            )
+           ((not (web-mode-tag-beginning))
+            )
+           ((string-match-p "^/?>" curr-line)
+            (setq offset (current-column)))
+           (web-mode-attr-indent-offset
+            (setq offset (+ (current-column) web-mode-attr-indent-offset)))
+           ((looking-at-p (concat web-mode-start-tag-regexp "[ ]*\n"))
+            (setq offset (+ (current-column) (or web-mode-attr-indent-offset 4))))
+           ((web-mode-attribute-next)
+            (setq offset (current-column)))
+           ) ;cond
+          )
+
+         ((or (member language '("html" "xml"))
+              (and (member language '("jsx"))
+                   (string= options "is-html")))
+          ;;(message "html")
+          (cond
+           ((get-text-property pos 'tag-beg)
+            (setq offset (web-mode-markup-indentation pos))
+            )
+           ((and web-mode-indentless-elements
+                 (not (string= language "jsx"))
+                 (null (get-text-property pos 'block-side))
+                 (null (get-text-property pos 'part-side))
+                 (and (null (get-text-property pos 'tag-beg))
+                      (save-excursion
+                        (and (web-mode-element-parent)
+                             (member (get-text-property (point) 'tag-name) web-mode-indentless-elements))))
+                 )
+            (setq offset nil))
+           ((or (eq (length curr-line) 0)
+                (= web-mode-indent-style 2)
+                (get-text-property pos 'tag-beg)
+                (get-text-property pos 'reg-beg))
+            (setq offset (web-mode-markup-indentation pos))
+            )
+           )
+          )
+
+         ((string= language "ctemplate")
+          (setq offset reg-col))
+
+         ((member language '("mako" "web2py"))
+          (setq offset (web-mode-python-indentation pos
+                                                    curr-line
+                                                    reg-col
+                                                    curr-indentation
+                                                    reg-beg)))
+
+         ((string= language "asp")
+          (setq offset (web-mode-asp-indentation pos
+                                                 curr-line
+                                                 reg-col
+                                                 curr-indentation
+                                                 reg-beg)))
+
+         ((member language '("lsp" "cl-emb"))
+          (setq offset (web-mode-lisp-indentation pos ctx)))
+
+         ((member curr-char '(?\} ?\) ?]))
+          (let (ori)
+            (if (get-text-property pos 'block-side)
+                (setq ori (web-mode-block-opening-paren-position pos reg-beg))
+              (setq ori (web-mode-part-opening-paren-position pos reg-beg)))
+            (cond
+             ((null ori)
+              (setq offset reg-col))
+             ((and (goto-char ori)
+                   (looking-back ")[ ]*") ;; peut-on se passer du looking-back ?
+                   (re-search-backward ")[ ]*" nil t)
+                   (web-mode-block-opening-paren reg-beg))
+              (back-to-indentation)
+              (setq offset (current-indentation))
+              )
+             (t
+              (goto-char ori)
+              (back-to-indentation)
+              (setq offset (current-indentation))
+              (when (get-text-property pos 'jsx-end)
+                (setq adjust nil))
+              ) ;t
+             ) ;cond
+            ) ;let
+          )
+
+         ((string= language "erb")
+          (setq offset (web-mode-ruby-indentation pos
+                                                  curr-line
+                                                  reg-col
+                                                  curr-indentation
+                                                  reg-beg)))
+
+         ((string= language "css")
+          (setq offset (car (web-mode-css-indentation pos
+                                                      reg-col
+                                                      curr-indentation
+                                                      language
+                                                      reg-beg))))
+
+         ((and (string= language "razor")
+               (string-match-p "^\\." curr-line)
+               (string-match-p "^\\." prev-line))
+          (setq offset prev-indentation))
+
+         ((and (string= language "razor")
+               (string-match-p "^case " curr-line)
+               (string-match-p "^case " prev-line))
+          (search-backward "case ")
+          (setq offset (current-column)))
+
+         ;; TODO : prev-pos : se plasser sur ), remonter sur ( et
+         ;; verifer que ca n'est pas if
+         ((and (member language '("javascript" "jsx" "ejs" "php"))
+               (or (and (eq prev-char ?\))
+                        (string-match-p "^\\(for\\|if\\|while\\)[ ]*(" prev-line)
+;;                        (progn (message "%S" (point)) t)
+                        )
+                   (and (member language '("javascript" "jsx"))
+                        (web-mode-part-is-opener prev-pos reg-beg))
+                   (string-match-p "^else$" prev-line))
+               (not (string-match-p "^\\([{.]\\|->\\)" curr-line)))
+          ;;(message "ici")
+          (cond
+           ((and (eq prev-char ?\))
+                 (string-match-p "^\\(for\\|if\\|while\\)[ ]*(" prev-line))
+            (setq offset (+ prev-indentation web-mode-code-indent-offset))
+            )
+           ((member language '("javascript" "jsx" "ejs"))
+            (setq offset
+                  (+ (car (web-mode-javascript-indentation pos
+                                                           reg-col
+                                                           curr-indentation
+                                                           language
+                                                           reg-beg))
+                     web-mode-code-indent-offset))
+            )
+           (t
+            (setq offset (+ prev-indentation web-mode-code-indent-offset))
+            )
+           )
+          )
+
+         ((and (member language '("javascript" "jsx" "ejs"))
+               (member ?\. chars)
+               (not (string-match-p "^\\.\\.\\." curr-line)))
+          (let (pair)
+            (setq pair (web-mode-javascript-calls-beginning-position pos reg-beg))
+            (when pair
+              (goto-char (car pair))
+              ;;(message "%S %S" (point) pair)
+              (cond
+               ((cdr (assoc "lineup-calls" web-mode-indentation-params))
+                ;;(search-forward ".")
+                (if (cdr pair)
+                    (progn
+                      (goto-char (cdr pair))
+                      (setq offset (current-column))
+                      (looking-at "\\.\\([ \t\n]*\\)")
+                      (setq offset (- offset (length (match-string-no-properties 1))))
+                      (unless (eq curr-char ?\.) (setq offset (1+ offset)))
+                      ) ;progn
+                  ;; TODO: cela devrait etre fait dans web-mode-javascript-calls-beginning-position
+                  (skip-chars-forward " \t\n")
+                  (setq offset (+ (current-indentation) web-mode-code-indent-offset))
+                  ) ;if
+                )
+               (t
+                (setq offset (+ (current-indentation) web-mode-code-indent-offset))
+                ) ;t
+               ) ;cond
+              ) ;when
+            ) ;let
+          )
+
+         ((and (member language '("javascript" "jsx" "ejs"))
+               (member ?\+ chars))
+          ;;(message "js-concat")
+          (cond
+           ((not (web-mode-javascript-string-beginning pos reg-beg))
+            )
+           ((null (cdr (assoc "lineup-concats" web-mode-indentation-params)))
+            (setq offset (+ (current-indentation) web-mode-code-indent-offset)))
+           ((not (eq curr-char ?\+))
+            (setq offset (current-column)))
+           (t
+            (setq offset (current-column))
+            (goto-char pos)
+            (looking-at "\\+[ \t\n]*")
+            (setq offset (- offset (length (match-string-no-properties 0))))
+            )
+           )
+          )
+
+         ;; #579
+         ((and (member language '("javascript" "jsx" "ejs" "php"))
+               (string-match-p "=>$" prev-line))
+          (setq offset (+ prev-indentation web-mode-code-indent-offset))
+          ;;(message "ici%S" offset)
+          )
+
+         ;; #446, #638
+         ((and (member language '("javascript" "jsx" "ejs" "php"))
+               (or (string-match-p "[&|?:+-]$" prev-line)
+                   (string-match-p "^[&|?:+-]" curr-line))
+               (not (and (string= language "php")
+                         (string-match-p "^->" curr-line)))
+               (not (and (eq prev-char ?\:)
+                         (string-match-p "^\\(case\\|default\\)" prev-line))))
+          (cond
+           ((not (funcall (if (member language '("javascript" "jsx" "ejs"))
+                              'web-mode-javascript-statement-beginning
+                            'web-mode-block-statement-beginning)
+                          pos reg-beg))
+            )
+           ((null (cdr (assoc "lineup-ternary" web-mode-indentation-params)))
+            (setq offset (+ (current-indentation) web-mode-code-indent-offset)))
+           (t
+            (setq offset (current-column))
+            (when (member curr-char '(?\+ ?\- ?\& ?\| ?\? ?\:))
+              (goto-char pos)
+              (looking-at "\\(||\\|&&\\|[&|?:+-]\\)[ \t\n]*")
+              (setq offset (- offset (length (match-string-no-properties 0)))))
+            )
+           ) ;cond
+          )
+
+         ((and (member language '("javascript" "jsx" "ejs"))
+               (or (member ?\, chars)
+                   (member prev-char '(?\( ?\[))))
+          (cond
+           ((not (web-mode-javascript-args-beginning pos reg-beg))
+            (message "no js args beg")
+            )
+           ((or (not (cdr (assoc "lineup-args" web-mode-indentation-params)))
+                (looking-at-p "\n"))
+            (if (and reg-col (> reg-col (current-indentation)))
+                (setq offset (+ reg-col web-mode-code-indent-offset))
+              (setq offset (+ (current-indentation) web-mode-code-indent-offset)))
+            )
+           ((not (eq curr-char ?\,))
+            (setq offset (current-column)))
+           (t
+            (setq offset (current-column))
+            (goto-char pos)
+            (looking-at ",[ \t\n]*")
+            (setq offset (- offset (length (match-string-no-properties 0)))))
+           ))
+
+         ((and (member language '("php" "blade")) (string-match-p "^->" curr-line))
+          (cond
+           ((not (web-mode-block-calls-beginning pos reg-beg))
+            )
+           ((cdr (assoc "lineup-calls" web-mode-indentation-params))
+            ;;(message "point=%S" (point))
+            (if (looking-back "::[ ]*")
+                (progn
+                  (re-search-backward "::[ ]*")
+                  (setq offset (current-column))
+                  ;;(message "ici%S offset=%S" (point) offset)
+                  )
+              (search-forward "->")
+              (setq offset (- (current-column) 2)))
+            )
+           (t
+            (setq offset (+ (current-indentation) web-mode-code-indent-offset)))
+           ))
+
+         ((member ?\, chars)
+          (cond
+           ((not (web-mode-block-args-beginning pos reg-beg))
+            )
+           ((cdr (assoc "lineup-args" web-mode-indentation-params))
+            (setq offset (current-column))
+            (when (eq curr-char ?\,)
+              (goto-char pos)
+              (looking-at ",[ \t\n]*")
+              (setq offset (- offset (length (match-string-no-properties 0)))))
+            )
+           (t
+            (setq offset (+ (current-indentation) web-mode-code-indent-offset)))
+           ))
+
+         ((and (string= language "php") (member ?\. chars))
+          (cond
+           ((not (web-mode-block-string-beginning pos reg-beg))
+            )
+           ((null (cdr (assoc "lineup-concats" web-mode-indentation-params)))
+            (setq offset (+ (current-indentation) web-mode-code-indent-offset)))
+           ((not (eq curr-char ?\.))
+            ;;(message "%S" (point))
+            (setq offset (current-column)))
+           (t
+            (setq offset (current-column))
+            (goto-char pos)
+            (when (cdr (assoc "lineup-quotes" web-mode-indentation-params))
+              (looking-at "\\.[ \t\n]*")
+              (setq offset (- offset (length (match-string-no-properties 0)))))
+            )))
+
+         ((member language '("javascript" "jsx" "ejs" "underscore"))
+          ;;(message "js-indent")
+          (setq offset (car (web-mode-javascript-indentation pos
+                                                             reg-col
+                                                             curr-indentation
+                                                             language
+                                                             reg-beg))))
+
+         (t
+          ;;(message "ici")
+          (setq offset (car (web-mode-bracket-indentation pos
+                                                          reg-col
+                                                          curr-indentation
+                                                          language
+                                                          reg-beg))))
+
+         ) ;cond
+
+        (when (and offset reg-col adjust (< offset reg-col)) (setq offset reg-col))
+
+        ) ;let
+      ) ;save-excursion
+
+    (when offset
+      (let ((diff (- (current-column) (current-indentation))))
+        (when (not (= offset (current-indentation)))
+          (setq web-mode-change-beg (line-beginning-position)
+                web-mode-change-end (+ web-mode-change-beg offset)))
+        (setq offset (max 0 offset))
+        (indent-line-to offset)
+        (if (> diff 0) (move-to-column (+ (current-column) diff)))
+        (when (and (string= web-mode-engine "mason")
+                   (= offset 0)
+                   (eq char ?\%))
+          (save-excursion
+            (font-lock-fontify-region (line-beginning-position) (line-end-position)))
+          ) ;when
+        ) ;let
+      ) ;when
+
+    ))
+
+(defun web-mode-markup-indentation (pos)
+  (let ((offset 0) beg ret)
+    (when (setq beg (web-mode-markup-indentation-origin pos))
+      (when (and (get-text-property pos 'jsx-depth)
+                 (not (get-text-property beg 'jsx-depth)))
+        (setq beg (web-mode-jsx-depth-beginning-position pos)))
+      (cond
+       ((null (setq ret (web-mode-element-is-opened beg pos)))
+        (setq offset (web-mode-indentation-at-pos beg)))
+       ((eq ret t)
+        (setq offset (+ (web-mode-indentation-at-pos beg) web-mode-markup-indent-offset)))
+       (t
+        (setq offset ret))
+       ) ;cond
+      ) ;when beg
+    offset))
+
+(defun web-mode-css-indentation (pos initial-column language-offset language &optional limit)
+  (let ((open-ctx (web-mode-bracket-up pos language limit)) offset)
+    (cond
+     ((or (null open-ctx) (null (plist-get open-ctx :pos)))
+      (setq offset initial-column))
+     (t
+      (setq offset (+ (plist-get open-ctx :indentation) language-offset)))
+     ) ;cond
+    (cons (if (< offset initial-column) initial-column offset) open-ctx)
+    ))
+
+(defun web-mode-javascript-indentation (pos initial-column language-offset language &optional limit)
+  (let ((open-ctx (web-mode-bracket-up pos language limit)) indentation offset sub)
+    ;;(message "pos(%S) initial-column(%S) language-offset(%S) language(%S) limit(%S)" pos initial-column language-offset language limit)
+    ;;(message "javascript-indentation: %S\nchar=%c" open-ctx (plist-get open-ctx :char))
+    (setq indentation (plist-get open-ctx :indentation))
+    (when (and initial-column (> initial-column indentation))
+      (setq indentation initial-column)
+      )
+    (cond
+     ((or (null open-ctx) (null (plist-get open-ctx :pos)))
+      (setq offset initial-column))
+     ((and (member language '("javascript" "jsx" "ejs"))
+           (eq (plist-get open-ctx :char) ?\{)
+           (web-mode-looking-back "switch[ ]*(.*)[ ]*" (plist-get open-ctx :pos)))
+      (setq sub (if (cdr (assoc "case-extra-offset" web-mode-indentation-params)) 0 1))
+      (cond
+       ((looking-at-p "case\\|default")
+        (setq offset (+ indentation (* language-offset (- 1 sub)))))
+       (t
+        (setq offset (+ indentation (* language-offset (- 2 sub)))))
+       ) ;cond switch
+      )
+     (t
+      (setq offset (+ indentation language-offset)))
+     ) ;cond
+    (cons (if (< offset initial-column) initial-column offset) open-ctx)
+    ))
+
+(defun web-mode-bracket-indentation (pos initial-column language-offset language &optional limit)
+  (save-excursion
+    (let* ((ctx (web-mode-bracket-up pos language limit))
+           (char (plist-get ctx :char))
+           (pos (plist-get ctx :pos))
+           (indentation (plist-get ctx :indentation)))
+      ;;(message "pos(%S) initial-column(%S) language-offset(%S) language(%S) limit(%S)" pos initial-column language-offset language limit)
+      ;;(message "bracket-up: %S, %c" ctx char)
+      (cond
+       ((null pos)
+        (setq indentation initial-column))
+       ((and (member language '("php"))
+             (eq char ?\{)
+             (web-mode-looking-back "switch[ ]*(.*)[ ]*" pos)
+             (not (looking-at-p "case\\|default")))
+        (setq indentation (+ indentation (* language-offset 2)))
+        )
+       ((and (member language '("php"))
+             (eq char ?\{)
+             (goto-char pos)
+             (web-mode-looking-back "[)][ ]*" pos)
+             (search-backward ")")
+             (web-mode-block-opening-paren limit))
+        (setq indentation (+ (current-indentation) language-offset))
+        )
+       (t
+        (setq indentation (+ indentation language-offset))
+        )
+       )
+      (cons (if (< indentation initial-column) initial-column indentation) ctx)
+      )))
+
+(defun web-mode-ruby-indentation (pos line initial-column language-offset limit)
+  (unless limit (setq limit nil))
+  (let (h offset prev-line prev-indentation open-ctx)
+    (setq open-ctx (web-mode-bracket-up pos "ruby" limit))
+    ;;(message "%S" open-ctx)
+    (if (plist-get open-ctx :pos)
+
+        (cond
+         ((web-mode-looking-at-p ".[ \t\n]+" (plist-get open-ctx :pos))
+          ;;(message "ici %S" (plist-get open-ctx :pos))
+          (setq offset (+ (plist-get open-ctx :indentation) language-offset)))
+         (t
+          (setq offset (1+ (plist-get open-ctx :column))))
+         )
+
+      (setq h (web-mode-previous-line pos limit))
+      (setq offset initial-column)
+      (when h
+        (setq prev-line (car h))
+        ;;(message "%S" prev-line)
+        (setq prev-indentation (cdr h))
+        (cond
+         ((string-match-p "^\\(end\\|else\\|elsif\\|when\\)" line)
+          (setq offset (- prev-indentation language-offset))
+          )
+         ((string-match-p "\\(when\\|if\\|else\\|elsif\\|unless\\|for\\|while\\|def\\|class\\)" prev-line)
+          (setq offset (+ prev-indentation language-offset))
+          )
+         (t
+          (setq offset prev-indentation)
+          )
+         )
+        ) ;when
+      ) ;if
+    offset))
+
+(defun web-mode-python-indentation (pos line initial-column language-offset limit)
+  (unless limit (setq limit nil))
+  (let (h out prev-line prev-indentation ctx)
+    (setq h (web-mode-previous-line pos limit))
+    (setq out initial-column)
+    (when h
+      (setq prev-line (car h))
+      (setq prev-indentation (cdr h))
+      (cond
+       ((string-match-p "^\\(pass\\|else\\|elif\\|when\\)" line)
+        (setq out (- prev-indentation language-offset))
+        )
+       ((string-match-p "\\(if\\|else\\|elif\\|for\\|while\\)" prev-line)
+        (setq out (+ prev-indentation language-offset))
+        )
+       (t
+        (setq out prev-indentation)
+        )
+       ) ;cond
+      ) ;when
+    ;;out
+    (if (< out initial-column) initial-column out)
+    ))
+
+(defun web-mode-lisp-indentation (pos point-ctx)
+  (let (offset open-ctx)
+    (setq open-ctx (web-mode-bracket-up pos "lsp" (plist-get point-ctx :reg-beg)))
+    ;;(message "point-ctx=%S" point-ctx)
+    ;;(message "open-ctx=%S" open-ctx)
+    (cond
+     ((null (plist-get open-ctx :pos))
+      (setq offset (plist-get point-ctx :reg-col)))
+     ((member (plist-get point-ctx :curr-char) '(?\( ?\)))
+      (if (web-mode-looking-at-p "((" (plist-get open-ctx :pos))
+          (setq offset (+ (plist-get open-ctx :column) 1))
+        (setq offset (+ (plist-get open-ctx :column) web-mode-code-indent-offset)))
+      )
+     (t
+      (goto-char (plist-get open-ctx :pos))
+      (forward-char)
+      (web-mode-rsf "[[:alnum:]-:]+ ")
+      (setq offset (current-column))
+      )
+     ) ;cond
+    offset))
+
+(defun web-mode-asp-indentation (pos line initial-column language-offset limit)
+  (unless limit (setq limit nil))
+  (let (h out prev-line prev-indentation)
+    (setq h (web-mode-previous-line pos limit))
+    (setq out initial-column)
+    (when h
+      (setq prev-line (car h))
+      (setq prev-indentation (cdr h))
+      (cond
+       ;; ----------------------------------------------------------------------
+       ;; unindent
+       ((string-match-p "\\_<\\(\\(end \\(if\\|function\\|class\\|sub\\|with\\)\\)\\|else\\|elseif\\|next\\|loop\\)\\_>" line)
+        (setq out (- prev-indentation language-offset)))
+       ;; ----------------------------------------------------------------------
+       ;; select case statement
+       ((string-match-p "\\_<\\(select case\\)\\_>" line)
+        (setq out (- prev-indentation 0)))
+       ((string-match-p "\\_<\\(end select\\)" line)
+        (setq out (- prev-indentation (* 2 language-offset))))
+       ((and (string-match-p "\\_<\\(case\\)\\_>" line) (not (string-match-p "\\_<\\(select case\\)\\_>" prev-line)))
+        (setq out (- prev-indentation language-offset)))
+       ;; ----------------------------------------------------------------------
+       ;; do nothing
+       ((string-match-p "\\_<\\(\\(end \\(if\\|function\\|class\\|sub\\|select\\|with\\)\\)\\|loop\\( until\\| while\\)?\\)\\_>" prev-line)
+        (setq out (+ prev-indentation 0)))
+       ;; indent
+       ((string-match-p "\\_<\\(\\(select \\)?case\\|else\\|elseif\\|unless\\|for\\|class\\|with\\|do\\( until\\| while\\)?\\|while\\|\\(public \\|private \\)?\\(function\\|sub\\|class\\)\\)\\_>" prev-line)
+        (setq out (+ prev-indentation language-offset)))
+       ;; single line if statement
+       ((string-match-p "\\_<if\\_>.*\\_<then\\_>[ \t]*[[:alpha:]]+" prev-line)
+        (setq out (+ prev-indentation 0)))
+       ;; normal if statement
+       ((string-match-p "\\_<\\if\\_>" prev-line)
+        (setq out (+ prev-indentation language-offset)))
+       (t
+        (setq out prev-indentation))
+       )
+      ) ;when
+    out))
+
+(defun web-mode-block-previous-live-line ()
+  (save-excursion
+    (let ((continue t) (line "") (pos (point)))
       (beginning-of-line)
-      (while (and continue
-                  (not (bobp))
-                  (forward-line -1))
-        (if (not (web-mode-is-comment-or-string-line))
-            (setq line (web-mode-trim (buffer-substring (point) (line-end-position)))))
-        (when (not (string= line "")) (setq continue nil))
+      (while (and continue (not (bobp)) (forward-line -1))
+        (when (not (web-mode-block-is-token-line))
+          (setq line (web-mode-trim (buffer-substring (point) (line-end-position)))))
+        (when (not (string= line ""))
+          (setq continue nil))
         ) ;while
       (if (string= line "")
           (progn (goto-char pos) nil)
         (cons line (current-indentation)))
       )))
 
-(defun web-mode-previous-usable-client-line ()
-  "Return previous non blank/comment/string line and return this line (trimmed)."
-  (interactive)
+(defun web-mode-part-is-opener (pos reg-beg)
   (save-excursion
-    (let ((continue t)
+    (save-match-data
+      (and pos
+           (web-mode-go (web-mode-part-opening-paren-position pos))
+           (>= (point) reg-beg)
+           ;;(progn (message "%S %S" pos (point)))
+           (looking-back "if[ ]*")))))
+
+(defun web-mode-part-previous-live-line (reg-beg)
+  (unless reg-beg (setq reg-beg (point-min)))
+  ;;(message "reg-beg=%S" reg-beg)
+  (save-excursion
+    (let ((continue (> (point) reg-beg))
           (line "")
-          (pos (point)))
+          bol-pos
+          eol-pos
+          pos)
       (beginning-of-line)
-      (while (and continue
-                  (not (bobp))
-                  (forward-line -1))
-        (if (not (web-mode-is-part-token-line))
-            (setq line (web-mode-trim (buffer-substring (point) (line-end-position)))))
-        (when (not (string= line "")) (setq continue nil))
-        )
-      (if (string= line "")
-          (progn (goto-char pos) nil)
-        (cons line (current-indentation)))
+      (while (and continue (> (point) reg-beg) (forward-line -1))
+        (setq bol-pos (point)
+              eol-pos (line-end-position))
+        (when (> reg-beg bol-pos)
+          (setq bol-pos reg-beg))
+        (when (not (web-mode-part-is-token-line bol-pos))
+          (setq line (web-mode-trim (buffer-substring bol-pos eol-pos)))
+          (when (not (string= line "")) (setq continue nil))
+          ) ;when
+        ) ;while
+      (cond
+       ((string= line "")
+        nil)
+       (t
+        (setq continue t)
+        (setq pos (1- eol-pos))
+        (while (and (>= pos bol-pos) continue)
+          (cond
+           ((eq (char-after pos) ?\s)
+            (setq pos (1- pos)))
+           ((get-text-property pos 'part-token)
+            (setq pos (1- pos)))
+           (t
+            (setq continue nil))
+           ) ;cond
+          ) ;while
+        ;;(message "%S %S : %S" bol-pos eol-pos pos)
+        (setq line (web-mode-clean-part-line line))
+        (list line (current-indentation) pos))
+       )
       )))
 
 (defun web-mode-in-code-block (open close &optional prop)
-  "Detect if point is in a block delimited by open and close."
   (save-excursion
     (let ((pos (point)) pos-open pos-close start end ret)
       (when prop
@@ -4723,17 +7532,7 @@
         ret)
       )))
 
-;; voir line-number-at-pos
-(defun web-mode-line-number (&optional pos)
-  "Return line number at point."
-  (unless pos (setq pos (point)))
-  (let (ret)
-    (setq ret (+ (count-lines 1 pos)
-                 (if (= (web-mode-column-at-pos pos) 0) 1 0)))
-    ret))
-
-(defun web-mode-clean-client-line (input)
-  "Remove comments and server scripts."
+(defun web-mode-clean-part-line (input)
   (let ((out "")
         (beg 0)
         (keep t)
@@ -4750,23 +7549,21 @@
           (setq beg i
                 keep t))
         ) ;if
-      ;;      (message "out=%s beg=%d" out beg)
       ) ;dotimes
     (if (> beg 0) (setq out (concat out (substring input beg n))))
     (setq out (if (= (length out) 0) input out))
     (web-mode-trim out)
-    ;;    (message "%S [%s] > [%s]" beg input out)
     ))
 
-(defun web-mode-clean-server-line (input)
-  "Remove comments from server line."
+(defun web-mode-clean-block-line (input)
   (let ((out "")
         (beg 0)
         (keep t)
         (n (length input)))
     (dotimes (i n)
       (if (or (not (get-text-property i 'block-side input))
-              (member (get-text-property i 'block-token input) '(comment delimiter)))
+              (member (get-text-property i 'block-token input)
+                      '(comment delimiter-beg delimiter-end)))
           (when keep
             (setq out (concat out (substring input beg i))
                   beg 0
@@ -4782,912 +7579,235 @@
     ;;    (message "%S [%s] > [%s]" beg input out)
     ))
 
+(defun web-mode-language-at-pos (&optional pos)
+  (unless pos (setq pos (point)))
+  (cond
+   ((get-text-property pos 'block-side)
+    web-mode-engine)
+   ((get-text-property pos 'part-side)
+    (symbol-name (get-text-property pos 'part-side)))
+   (t
+    web-mode-content-type)
+   ) ;cond
+  )
+
+(defun web-mode-coord-position (line column)
+  (save-excursion
+    (when (stringp line) (setq line (string-to-number line)))
+    (when (stringp column) (setq column (string-to-number column)))
+    (goto-char (point-min))
+    (forward-line (1- line))
+    (move-to-column (1- column))
+    (point)))
+
 (defun web-mode-column-at-pos (&optional pos)
-  "Column at point"
   (unless pos (setq pos (point)))
   (save-excursion
     (goto-char pos)
-    (current-column)
-    ))
-
-;; doit-on considérer que '=' est un bloc ouvrant avec ';' comme char de fin ?
-(defun web-mode-point-context (pos)
-  "POS should be at the beginning of the indentation.
-   Return ctx = plist containing
-    :block-beg, :block-column,
-    :first-char, :line (trimmed)
-    :type (live, comment, string),
-    :language (html, php, jsp, aspx, asp + javascript, css),
-    :indent-offset
-    :prev-line :prev-char :prev-props :prev-indentation"
+    (current-column)))
+
+(defun web-mode-is-single-line-block (pos)
+  (= (web-mode-line-number (web-mode-block-beginning-position pos))
+     (web-mode-line-number (web-mode-block-end-position pos))))
+
+(defun web-mode-line-number (&optional pos)
+  (unless pos (setq pos (point)))
+  (let (ret)
+    (setq ret (+ (count-lines 1 pos)
+                 (if (= (web-mode-column-at-pos pos) 0) 1 0)))))
+
+(defun web-mode-block-is-control (pos)
   (save-excursion
-    (let (ctx pos-min
-              block-beg block-column first-char line type language indent-offset
-              prev prev-line prev-char prev-props prev-indentation)
-
-      (setq pos-min (point-min))
-      (setq block-beg pos-min
-            block-column 0
-            type "live"
-            language ""
-            prev-line ""
-            prev-char 0)
-      (cond
-
-       ((bobp)
-        (setq language "html")
-        )
-
-       ((string= web-mode-content-type "css")
-        (setq language "css"
-              indent-offset web-mode-css-indent-offset))
-
-       ((member web-mode-content-type '("javascript" "json"))
-        (setq language "javascript"
-              indent-offset web-mode-code-indent-offset))
-
-       ((string= web-mode-content-type "php")
-        (setq language "php"
-              indent-offset web-mode-code-indent-offset))
-
-       ((or (string= web-mode-content-type "xml"))
-        (setq language "xml"
-              indent-offset web-mode-markup-indent-offset))
-
-       ((and (get-text-property pos 'tag-beg)
-             (get-text-property pos 'tag-name))
-        (setq language "html"
-              indent-offset web-mode-markup-indent-offset)
-        )
-
-       ((or (and (eq (get-text-property pos 'part-token) 'comment)
-                 (eq (get-text-property (1- pos) 'part-token) 'comment)
-                 (progn
-                   (setq block-beg (previous-single-property-change pos 'part-token))
-                   t))
-            (and (eq (get-text-property pos 'block-token) 'comment)
-                 (eq (get-text-property (1- pos) 'block-token) 'comment)
-                 (progn
-                   (setq block-beg (previous-single-property-change pos 'block-token))
-                   t)))
-        (setq type "comment"))
-
-       ((or (and (member (get-text-property pos 'part-token) '(string context key))
-                 (member (get-text-property (1- pos) 'part-token) '(string context key)))
-            (and (eq (get-text-property pos 'block-token) 'string)
-                 (eq (get-text-property (1- pos) 'block-token) 'string)))
-        (setq type "string"))
-
-       ((and (get-text-property pos 'block-side)
-             (not (get-text-property pos 'block-beg)))
-        (setq block-beg (or (web-mode-block-beginning-position pos) pos-min))
-        (goto-char block-beg)
-        (setq block-column (current-column))
-        (setq language web-mode-engine)
-        (setq indent-offset web-mode-code-indent-offset)
-        (cond
-         ((string= web-mode-engine "blade")
-          (setq block-beg (+ block-beg 2)
-                block-column (+ block-column 2)
-                )
-          )
-         ((string= web-mode-engine "razor")
-          (setq block-beg (+ block-beg 2)
-;;                block-column (+ block-column 2)
-                )
-          )
-         ((string= web-mode-engine "template-toolkit")
-          (setq block-beg (+ block-beg 3)
-                block-column (+ block-column 3))
-          )
-         ((and (string= web-mode-engine "jsp")
-               (web-mode-looking-at-pos "<%@\\|<[[:alpha:]]" block-beg))
-          (save-excursion
-            (goto-char block-beg)
-            (looking-at "<%@[ ]*[[:alpha:]]+[ ]+\\|</?[[:alpha:]]+:[[:alpha:]]+[ ]+")
-            (goto-char (match-end 0))
-            (setq block-column (current-column))
-            )
-          )
-         ((and (string= web-mode-engine "freemarker")
-               (web-mode-looking-at-pos "<@\\|<%@\\|<[[:alpha:]]" block-beg))
-          (save-excursion
-            (goto-char block-beg)
-            (looking-at "<@[[:alpha:].]+[ ]+\\|<%@[ ]*[[:alpha:]]+[ ]+\\|<[[:alpha:]]+:[[:alpha:]]+[ ]+")
-            (goto-char (match-end 0))
-            (setq block-column (current-column))
-            )
-          )
-         ) ;cond
-        )
-
-       ((get-text-property pos 'part-side)
-        (setq block-beg (or (previous-single-property-change pos 'part-side) pos-min))
-;;        (message "%S" block-beg)
-        (goto-char block-beg)
-        (search-backward "<" nil t)
-        (setq block-column (current-column))
-        (setq language (symbol-name (get-text-property pos 'part-side)))
-        (cond
-         ((string= language "css")
-          (setq indent-offset web-mode-css-indent-offset)
-          )
-         (t
-          (setq language "javascript"
-                indent-offset web-mode-code-indent-offset)
-          )
-         )
-        )
-
-       (t
-        (setq language "html"
-              indent-offset web-mode-markup-indent-offset)
-        )
-
-       ) ;cond
-
+    (let (control state controls pair)
       (goto-char pos)
-      (setq line (web-mode-trim (buffer-substring-no-properties (line-beginning-position)
-                                                                (line-end-position))))
-      (setq first-char (if (string= line "") 0 (aref line 0)))
-
-      (when (or (member language '("php" "javascript" "razor"))
-                (and (string= language "html") (not (eq ?\< first-char))))
-        (cond
-         ((member language '("html" "javascript"))
-          (setq prev (web-mode-previous-usable-client-line))
-          ;;          (message "prev-line=%S" prev-line)
-          (when prev
-            (setq prev-line (car prev)
-                  prev-indentation (cdr prev))
-            (setq prev-line (web-mode-clean-client-line prev-line))
-            (setq prev-props (text-properties-at (1- (length prev-line)) prev-line)))
-          )
-         (t
-          (setq prev (web-mode-previous-usable-server-line))
-          (when prev
-            (setq prev-line (car prev)
-                  prev-indentation (cdr prev))
-            (setq prev-line (web-mode-clean-server-line prev-line)))
-            ;; (message "pl=%s" prev-line)
-          )
-         ) ;cond
-        (when (>= (length prev-line) 1)
-          (setq prev-char (aref prev-line (1- (length prev-line))))
-          (setq prev-line (substring-no-properties prev-line))
-          )
-        )
-
-;;      (if (string= language "json") (setq language "javascript"))
-
-      (when (string= web-mode-content-type "html")
-        (cond
-         ((string= language "javascript")
-          (setq block-column (+ block-column web-mode-script-padding)))
-         ((string= language "css")
-          (setq block-column (+ block-column web-mode-style-padding)))
-         ((not (member language '("html" "razor")))
-          (setq block-column (+ block-column web-mode-block-padding)))
-         )
-        )
-
-      (setq ctx (list :block-beg block-beg
-                      :block-column block-column
-                      :first-char first-char
-                      :line line
-                      :type type
-                      :language language
-                      :indent-offset indent-offset
-                      :prev-line prev-line
-                      :prev-char prev-char
-                      :prev-props prev-props
-                      :prev-indentation prev-indentation))
-;;      (message "%S" ctx)
-      ctx
+      (setq controls (web-mode-block-controls-get pos))
+      (setq pair (car controls))
+      (cond
+       ((eq (car pair) 'inside)
+        )
+       ((eq (car pair) 'open)
+        (setq state t
+              control (cdr pair)))
+       ((eq (car pair) 'close)
+        (setq state nil
+              control (cdr pair)))
+       ) ;cond
+      ;;      (message "engine=%S control=%S state=%S" web-mode-engine control state)
+      (if control (cons control state) nil)
       )))
 
-(defun web-mode-indent-line ()
-  "Indent current line according to language."
-  (let ((inhibit-modification-hooks t)
-        pos
-        offset
-        ctx
-        block-beg
-        block-column
-        first-char
-        line
-        type
-        language
-        indent-offset
-        prev-line
-        prev-char
-        prev-props
-        prev-indentation)
-
-    (save-excursion
-      (back-to-indentation)
-      (setq pos (point))
-      (setq ctx (web-mode-point-context pos))
-      (setq block-beg (plist-get ctx :block-beg))
-      (setq block-column (plist-get ctx :block-column))
-      (setq first-char (plist-get ctx :first-char))
-      (setq line (plist-get ctx :line))
-      (setq type (plist-get ctx :type))
-      (setq language (plist-get ctx :language))
-      (setq indent-offset (plist-get ctx :indent-offset))
-      (setq prev-line (plist-get ctx :prev-line))
-      (setq prev-char (plist-get ctx :prev-char))
-      (setq prev-props (plist-get ctx :prev-props))
-      (setq prev-indentation (plist-get ctx :prev-indentation))
-
-      (cond
-
-       ((or (bobp)
-            (= (line-number-at-pos pos) 1))
-        (setq offset 0)
-        )
-
-       ((string= type "string")
-        (setq offset nil)
-        )
-
-       ((string= type "comment")
-        (goto-char (car
-                    (web-mode-property-boundaries
-                     (if (eq (get-text-property pos 'part-token) 'comment)
-                         'part-token
-                       'block-token)
-                     pos)))
-        (setq offset (current-column))
-        (cond
-         ((and (string= (buffer-substring-no-properties (point) (+ (point) 2)) "/*")
-                   (eq ?\* first-char))
-          (setq offset (1+ offset)))
-         ((and (string= web-mode-engine "django")
-               (looking-back "{% comment %}"))
-          (setq offset (- offset 12))
-          )
-         ((and (string= web-mode-engine "mako")
-               (looking-back "<%doc%>"))
-          (setq offset (- offset 6))
-          )
-         ) ;cond
-        ) ;case comment
-
-       ((member language '("asp" "aspx" "blade" "code" "django" "erb"
-                           "freemarker" "javascript" "jsp" "mako" "mason"
-                           "php" "python" "razor" "template-toolkit" "web2py"))
-
-        (cond
-
-         ((string-match-p "^[?%]>" line)
-          (if (web-mode-block-beginning pos)
-              (setq offset (current-column)))
-          )
-
-         ((and (string= language "mason")
-               (string-match-p "</%" line))
-          (if (web-mode-block-beginning pos)
-              (setq offset (current-column)))
-          )
-
-         ((and (string= language "web2py")
-               (string-match-p "}}" line))
-          (if (web-mode-block-beginning pos)
-              (setq offset (current-column)))
-          )
-
-         ((and (string= language "razor")
-               (string-match-p "^\\." line)
-               (string-match-p "^\\." prev-line))
-          (setq offset prev-indentation)
-          )
-
-         ((and (string= language "razor")
-               (string-match-p "^}" line))
-          (goto-char (web-mode-opening-paren-position (point)))
+(defun web-mode-block-is-opening-control (pos)
+  (save-excursion
+    (let (controls pair)
+      (goto-char pos)
+      (if (and (setq controls (web-mode-block-controls-get pos))
+               (= (length controls) 1)
+               (setq pair (car controls))
+               (eq (car pair) 'open))
+          (cdr pair)
+        nil)
+      )))
+
+(defun web-mode-markup-indentation-origin (pos)
+  (save-excursion
+    (let* ((continue (not (bobp)))
+           ;;         (pos (point))
+           (part-side (not (null (get-text-property pos 'part-side)))) ;part-side at the origin
+           (types '(start end void)))
+      (while continue
+        (forward-line -1)
+        (if (bobp)
+            (setq pos (point)
+                  continue nil)
           (back-to-indentation)
-          (setq offset (current-column))
-          )
-
-        ((and (string= language "razor")
-              (string-match-p "^case " line)
-              (string-match-p "^case " prev-line))
-         (search-backward "case ")
-         (setq offset (current-column))
-         )
-
-         ((and (string-match-p "^[=]?%]" line)
-               (string= web-mode-engine "template-toolkit"))
-          (if (web-mode-block-beginning pos)
-              (setq offset (current-column)))
-          )
-
-         ((and (string= language "php") (string-match-p "^->" line))
-          (when (web-mode-sb "->" block-beg)
-            (setq offset (current-column)))
-          )
-
-         ((and (string= language "php") (string-match-p "\\.$" prev-line))
-;;          (message "prev-line=%S" prev-line)
-          (cond
-           ((and (string-match-p "\\(=\\|echo \\)" prev-line)
-;;                 (progn (message "%S" (point)) t)
-                 (web-mode-rsb "\\(=\\|echo\\)[ ]+" block-beg))
-            (goto-char (match-end 0))
-            (setq offset (current-column))
-            )
-           ((string-match-p "^['\"$0-9]" prev-line)
-            (setq offset prev-indentation)
-            )
-           (t
-            (setq offset block-column)
-            )
-           )
-          ;;(when (web-mode-sb "->" block-beg)
-          ;;  (setq offset (current-column)))
-          )
-
-         ((and (string= language "php")
-               (or (string-match-p "^else$" prev-line)
-                   (string-match-p "^\\(if\\|for\\|foreach\\|while\\)[ ]*(.+)$" prev-line))
-               (not (string-match-p "^{" line)))
-          (setq offset (+ prev-indentation web-mode-code-indent-offset))
-          )
-
-         ((and (string= language "javascript") (eq ?\. first-char))
-          (when (web-mode-rsb "[[:alnum:][:blank:]]\\.[[:alpha:]]" block-beg)
-            (setq offset (1+ (current-column))))
-          )
-
-         ((and (member first-char '(?\? ?\. ?\:))
-               (not (string= language "erb")))
-          (web-mode-rsb "[^!=][=(]" block-beg)
-          (setq offset (1+ (current-column)))
-          (when (and (string= web-mode-engine "php")
-                     (looking-at-p " =>"))
-            (setq offset (1+ offset))
-            )
-          )
-
-         ((and (member prev-char '(?\. ?\+ ?\? ?\:))
-               (not (string-match-p "^\\(case\\|default\\)[ :]" prev-line)))
-          (web-mode-rsb "[=(]" block-beg)
-          (skip-chars-forward "= (")
-          (setq offset (current-column))
-          )
-
-         ((string= language "erb")
-          (setq offset (web-mode-ruby-indentation pos
-                                                  line
-                                                  block-column
-                                                  indent-offset
-                                                  block-beg))
-          )
-
-         ((member language '("mako" "web2py"))
-          (setq offset (web-mode-python-indentation pos
-                                                    line
-                                                    block-column
-                                                    indent-offset
-                                                    block-beg))
-          )
-
-         ((string= language "asp")
-          (setq offset (web-mode-asp-indentation pos
-                                                 line
-                                                 block-column
-                                                 indent-offset
-                                                 block-beg))
-          )
-
-         (t
-          (setq offset (web-mode-bracket-indentation pos
-                                                     block-column
-                                                     indent-offset
-                                                     block-beg))
-          ) ;t
-
-         )) ;end case script block
-
-       ((string= language "css")
-        (setq offset (web-mode-bracket-indentation pos
-                                                   block-column
-                                                   indent-offset
-                                                   block-beg))
-        ) ;case style
-
-       (t ; case html block
-
-        (cond
-
-         ((and prev-props (plist-get prev-props 'tag-attr))
-          (web-mode-tag-beginning)
-          (let (skip)
-            (setq skip (next-single-property-change (point) 'tag-attr))
-            (when skip
-              (goto-char skip)
-              (setq offset (current-column))
-              ))
-          )
-
-         ((and (string= web-mode-engine "mason")
-               (string-match-p "^%" line))
-          (setq offset 0)
-          )
-
-         ((or (and (eq (get-text-property pos 'tag-type) 'end)
-                   (web-mode-html-tag-match))
-              (and (string= web-mode-engine "razor")
-                   (string-match-p "^[ \t]*}" line)
-                   (web-mode-block-match))
-              (and (get-text-property pos 'block-beg)
-                   (looking-at-p web-mode-close-block-regexp)
-                   ;;(progn (message "%S %S" (point) web-mode-close-block-regexp) t)
-                   ;;(funcall web-mode-engine-control-matcher)
-                   (web-mode-block-match))
-              )
-          (setq offset (current-indentation))
-          )
-
-         ((or (eq (length line) 0)
-              (= web-mode-indent-style 2)
-              (get-text-property pos 'tag-beg)
-              (get-text-property pos 'block-beg))
-          (setq offset (web-mode-markup-indentation pos))
-          )
-
-         ) ;cond
-
-        ) ;end case html block
-
-       ) ;end switch language block
-
-      ) ;save-excursion
-
-    (when offset
-      (let ((diff (- (current-column) (current-indentation))))
-        (setq offset (max 0 offset))
-        (indent-line-to offset)
-        (if (> diff 0) (forward-char diff))
-
-        (when (and (string= web-mode-engine "mason")
-                   (= offset 0)
-                   (string-match-p "^%" line))
-          (web-mode-highlight-region (line-beginning-position) (line-end-position)))
-
-        ) ;let
-      ) ;when
-
-    ))
-
-(defun web-mode-is-active-block (pos)
-  "web-mode-is-active-block"
-  (save-excursion
-    (let (ctrl state)
-      (goto-char pos)
-      ;;(message "pos=%S" pos)
-      (when (looking-at web-mode-active-block-regexp)
-
-        (cond
-
-         ((string= web-mode-engine "php")
-          (setq ctrl (match-string-no-properties 3))
-          (if (member ctrl '("else" "elseif"))
-              (setq ctrl nil)
-            (setq state (not (string= "end" (match-string-no-properties 2))))
-            )
-          )
-
-         ((string= web-mode-engine "django")
-          (setq ctrl (match-string-no-properties 2))
-          (if (member ctrl '("else" "elseif" "elsif" "elif" "empty"))
-              (setq ctrl nil)
-            (setq state (not (string= "end" (match-string-no-properties 1))))
-            )
-          )
-
-         ((string= web-mode-engine "web2py")
-          (setq ctrl (match-string-no-properties 1))
-          (if (member ctrl '("else" "elif" "except" "finally"))
-              (setq ctrl nil)
-            (setq state (not (member ctrl '("end" "pass" "return"))))
-            )
-          )
-
-         ((string= web-mode-engine "mason")
-          (setq ctrl (match-string-no-properties 1))
-          (setq state (not (eq ?\/ (aref (match-string-no-properties 0) 1))))
-          )
-
-         ((string= web-mode-engine "smarty")
-          (setq ctrl (match-string-no-properties 1))
-          (if (member ctrl '("else" "elseif"))
-              (setq ctrl nil)
-            (setq state (not (eq ?\/ (aref (match-string-no-properties 0)
-                                           (length (web-mode-engine-delimiter-open web-mode-engine "{"))))))
-            )
-          )
-
-         ((string= web-mode-engine "dust")
-          (setq ctrl (match-string-no-properties 1))
-          (if (or (member ctrl '("else"))
-                  (eq ?\/ (char-after (1- (web-mode-block-end-position)))))
-              (setq ctrl nil)
-            (setq state (not (eq ?\/ (aref (match-string-no-properties 0) 1))))
-            )
-          )
-
-         ((string= web-mode-engine "closure")
-          (setq ctrl (match-string-no-properties 1))
-          (if (or (member ctrl '("else" "elseif" "case" "default"))
-                  (eq ?\/ (char-after (1- (web-mode-block-end-position)))))
-              (setq ctrl nil)
-            (setq state (not (eq ?\/ (aref (match-string-no-properties 0) 1))))
-            )
-          )
-
-         ((string= web-mode-engine "ctemplate")
-          (setq ctrl (match-string-no-properties 1))
-          (setq state (not (eq ?\/ (aref (match-string-no-properties 0) 2))))
-          )
-
-         ((string= web-mode-engine "mako")
-          (cond
-           ((eq (char-after pos) ?\<)
-            (if (eq ?\/ (char-after (1- (web-mode-block-end-position))))
-                (setq ctrl nil)
-              (setq ctrl (match-string-no-properties 1))
-              (setq state (not (eq ?\/ (aref (match-string-no-properties 0) 2))))))
-           (t
-            (if (member (match-string-no-properties 3) '("elif" "else"))
-                (setq ctrl nil)
-              (setq ctrl (match-string-no-properties 3))
-              (setq state (null (match-string-no-properties 2))))
-            ;;              (message "%S ctrl=%S state=%S" pos ctrl state)
-            ;;            (message "%S - %S" (match-string-no-properties 2) (match-string-no-properties 3))
-            ) ;t
-           ) ;cond
-          )
-
-         ((string= web-mode-engine "velocity")
-          (setq ctrl (match-string-no-properties 1))
-          (if (member ctrl '("else" "elseif"))
-              (setq ctrl nil)
-            (setq state (not (string= "end" (match-string-no-properties 1))))
-            )
-          )
-
-         ((string= web-mode-engine "blade")
-          (setq ctrl (match-string-no-properties 2))
-          (cond
-           ((string= ctrl "stop")
-            (setq ctrl "section"))
-           ((member ctrl '("else" "elseif"))
-            (setq ctrl nil))
-           (t
-            (setq state (not (string= "end" (match-string-no-properties 1)))))
-           )
-          )
-
-         ((string= web-mode-engine "go")
-          (setq ctrl (match-string-no-properties 1))
-          (if (member ctrl '("else"))
-              (setq ctrl nil)
-            (setq state (not (string= "end" ctrl)))
-            )
-          )
-
-         ((string= web-mode-engine "erb")
-          (setq ctrl (match-string-no-properties 1))
-          (if (member ctrl '("else"))
-              (setq ctrl nil)
-            (setq state (not (string= "end" ctrl)))
-            )
-          )
-
-         ((string= web-mode-engine "template-toolkit")
-          (setq ctrl (match-string-no-properties 1))
-          (if (member ctrl '("else"))
-              (setq ctrl nil)
-            (setq state (not (string= "end" ctrl)))
-            )
-          )
-
-         ((string= web-mode-engine "jsp")
-          (cond
-           ((eq (aref (match-string-no-properties 0) 1) ?\%)
-            (setq ctrl (match-string-no-properties 2)
-                  state t)
-            )
-           (t
-            (setq ctrl (match-string-no-properties 1))
-            (if (or (member ctrl '("h:inputtext" "jsp:usebean" "jsp:forward" "struts:property"))
-                    (eq ?\/ (char-after (1- (web-mode-block-end-position)))))
-                (setq ctrl nil)
-              (setq state (not (eq ?\/ (aref (match-string-no-properties 0) 1))))
-              )
-            )
-           )
-          )
-
-         ((string= web-mode-engine "freemarker")
-          (if (or (member (aref (match-string-no-properties 0) 1) '(?\@ ?\#))
-                  (member (aref (match-string-no-properties 0) 2) '(?\@ ?\#)))
-              (setq ctrl (match-string-no-properties 2))
-            (setq ctrl (match-string-no-properties 1))
-            )
-;;          (message "ctrl=%S" ctrl)
-          (if (or (member ctrl '("include" "setting" "import" "global" "ftl"
-                                 "nested" "return" "local" "flush" "break" "recover"))
-                  (eq ?\/ (char-after (1- (web-mode-block-end-position)))))
-              (setq ctrl nil)
-            (setq state (not (eq ?\/ (aref (match-string-no-properties 0) 1))))
-            )
-          )
-
-         ((string= web-mode-engine "underscore")
-          (cond
-           ((web-mode-block-ends-with "{ %>")
-            (setq ctrl "ctrl"
-                  state t))
-           ((looking-at-p "<% }")
-            (setq ctrl "ctrl"
-                  state nil))
-           )
-          )
-
-         ((string= web-mode-engine "razor")
-          (setq ctrl (match-string-no-properties 1)
-                state t)
-          )
-
-         ) ;cond
-
-        ) ;when
-
-;;      (message "engine=%S ctrl=%S state=%S" web-mode-engine ctrl state)
-
-      (if ctrl (cons ctrl state) nil)
-      )))
-
-(defun web-mode-markup-indentation-origin ()
-  "web-mode-indentation-origin-pos"
-  (let ((continue t) pos)
-    (while continue
-      (forward-line -1)
-      (back-to-indentation)
-      (setq continue (not (bobp)))
-      (when (or (get-text-property (point) 'tag-beg)
-                (and (get-text-property (point) 'block-beg)
-                     (web-mode-is-active-block (point))
-                     (not (looking-at-p "{% comment"))))
-        (setq continue nil
-              pos (point))
-        )
-      ) ;while
-;;    (message "indent-origin=%S" pos)
-    pos
-    ))
-
-(defun web-mode-markup-indentation (pos)
-  "markup indentation"
-  (save-excursion
-    (goto-char pos)
-    (let ((offset 0) beg ret)
-      (setq beg (web-mode-markup-indentation-origin))
-      (when beg
-        (goto-char beg)
-        (setq ret (web-mode-is-opened-element beg pos))
-        (cond
-         ((null ret)
-          (setq offset (current-indentation)))
-         ((eq ret t)
-          (setq offset (+ (current-indentation) web-mode-markup-indent-offset))
-          )
-         (t
-          (setq offset ret))
-         )
-;;        (setq offset (+ (current-indentation)
-;;                        (if (web-mode-is-opened-element beg pos)
-;;                            web-mode-markup-indent-offset
-;;                          0)))
-        ) ;when
-      offset
-      )))
-
-;; state(t) <=> start tag
-(defun web-mode-is-opened-element (pos limit)
-  "Is there any HTML element without a closing tag ?"
-  (interactive)
+          (setq pos (point))
+          (setq continue (not (or (and (null part-side)
+                                       (null (get-text-property pos 'part-side))
+                                       (get-text-property pos 'tag-beg)
+                                       (member (get-text-property pos 'tag-type) types)
+                                       (null (get-text-property (1- pos) 'invisible)))
+                                  (and part-side
+                                       (get-text-property pos 'part-side)
+                                       (get-text-property pos 'tag-beg)
+                                       (member (get-text-property pos 'tag-type) types)
+                                       (null (get-text-property (1- pos) 'invisible)))
+                                  (and (get-text-property pos 'block-beg)
+                                       (not (get-text-property pos 'tag-type))
+                                       (web-mode-block-is-control pos)
+                                       (not (looking-at-p "{% comment"))))))
+          ) ;if
+        ) ;while
+      ;;(message "indent-origin=%S" pos)
+      pos)))
+
+;;TODO : prendre en compte part-token
+;; state=t <=> start tag
+(defun web-mode-element-is-opened (pos limit)
   (let (tag
-        last-tag
+        last-end-tag
         tag-pos block-pos
         state
         n
         ret
         (continue t)
+        controls
+        control
         (buffer (current-buffer))
         (h (make-hash-table :test 'equal))
-        (h2 (make-hash-table :test 'equal))
-        ctrl)
+        (h2 (make-hash-table :test 'equal)))
+
+;;    (message "pos-ori=%S limit=%S" pos limit)
+
     (while continue
-      (setq ctrl nil
-            last-tag nil)
-      (when (or (and (get-text-property pos 'tag-beg)
-                     (member (get-text-property pos 'tag-type) '(start end)))
-                (and (get-text-property pos 'block-beg)
-                     (setq ctrl (web-mode-is-active-block pos))))
-        (if ctrl
-            (setq tag (car ctrl)
-                  state (cdr ctrl))
+      (setq control nil
+            controls nil
+            last-end-tag nil
+            tag nil)
+
+      (cond
+       ((get-text-property pos 'tag-beg)
+        (when (member (get-text-property pos 'tag-type) '(start end))
           (setq tag (get-text-property pos 'tag-name)
                 state (eq (get-text-property pos 'tag-type) 'start))
-          (if (null state) (setq last-tag (cons tag pos)))
-          )
-
-        (setq n (gethash tag h 0))
-        (if (null state)
-            (progn
-              (when (> n 0) (puthash tag (1- n) h))
-              (puthash tag (1- n) h2))
-          (puthash tag (1+ n) h)
-          (puthash tag (1+ n) h2))
-        ) ;when
-      (setq pos (1+ pos))
-      (when (null tag-pos)
-        (setq tag-pos (next-single-property-change pos 'tag-beg buffer limit)))
-      (when (null block-pos)
-        (setq block-pos (next-single-property-change pos 'block-beg buffer limit)))
-      (cond
+          (if (null state) (setq last-end-tag (cons tag pos)))
+          (setq n (gethash tag h 0))
+          (if (null state)
+              (progn
+                (when (> n 0) (puthash tag (1- n) h))
+                (puthash tag (1- n) h2))
+            (puthash tag (1+ n) h)
+            (puthash tag (1+ n) h2))
+          ) ;when
+        (when (setq pos (web-mode-tag-end-position pos))
+          (setq tag-pos nil)
+          (when (and block-pos (> pos block-pos))
+            (setq block-pos nil))
+          ) ;when
+        )
+       ((and web-mode-enable-control-block-indentation
+             (get-text-property pos 'block-beg))
+        (when (setq controls (web-mode-block-controls-get pos))
+          (dolist (control controls)
+            (setq tag (cdr control))
+            (setq n (gethash tag h 0))
+            (cond
+             ((eq (car control) 'inside)
+              )
+             ((eq (car control) 'open)
+              (puthash tag (1+ n) h))
+             ((> n 0)
+              (puthash tag (1- n) h))
+             ) ;cond
+            ) ;dolist
+          )
+        (when (setq pos (web-mode-block-end-position pos))
+          (setq block-pos nil)
+          (when (and tag-pos (> pos tag-pos))
+            (setq tag-pos nil))
+          )
+        )
+       ) ;cond
+
+;;      (message "tag=%S end-pos=%S" tag pos)
+
+      (when (and pos (< pos limit))
+        (when (or (null tag-pos) (>= pos tag-pos))
+          (setq tag-pos (web-mode-tag-next-position pos limit))
+;;          (message "from=%S tag-next-pos=%S" pos tag-pos)
+          )
+        (when (or (null block-pos) (>= pos block-pos))
+          (setq block-pos (web-mode-block-next-position pos limit))
+;;          (message "from=%S block-next-pos=%S" pos block-pos)
+          )
+        )
+
+      (cond
+       ((null pos)
+        )
        ((and (null tag-pos)
              (null block-pos))
-        (setq pos nil)
-        )
-       ((or (null block-pos)
-            (< tag-pos block-pos))
+        (setq pos nil))
+       ((and tag-pos block-pos)
+        (if (< tag-pos block-pos)
+            (progn
+              (setq pos tag-pos)
+              (setq tag-pos nil))
+          (setq pos block-pos)
+          (setq block-pos nil))
+        )
+       ((null tag-pos)
+        (setq pos block-pos)
+        (setq block-pos nil))
+       (t
         (setq pos tag-pos)
-        (setq tag-pos nil)
-        )
-       (t
-        (setq pos block-pos)
-        (setq block-pos nil)
-        )
+        (setq tag-pos nil))
        )
+
       (when (or (null pos)
                 (>= pos limit))
         (setq continue nil))
       ) ;while
+
 ;;    (message "hashtable=%S" h)
     (maphash (lambda (k v) (if (> v 0) (setq ret t))) h)
+
     (when (and (null ret)
-               last-tag
+               last-end-tag
                (> (hash-table-count h2) 1)
-               (< (gethash (car last-tag) h2) 0))
-;;      (message "last-tag=%S" last-tag)
+               (< (gethash (car last-end-tag) h2) 0))
+;;      (message "last-end-tag=%S" last-end-tag)
       (save-excursion
-        (goto-char (cdr last-tag))
-        (web-mode-html-tag-match)
-        (when (not (= (point) (cdr last-tag)))
+        (goto-char (cdr last-end-tag))
+        (web-mode-tag-match)
+        (when (not (= (point) (cdr last-end-tag)))
           (setq n (point))
           (back-to-indentation)
           (if (= n (point)) (setq ret (current-indentation))))
         ))
+
     ret))
 
-(defun web-mode-ruby-indentation (pos line initial-column language-offset limit)
-  "Calc indent column."
-  (interactive)
-  (unless limit (setq limit nil))
-  (let (h out prev-line prev-indentation ctx)
-    (setq ctx (web-mode-count-opened-blocks pos limit))
-    (if (cddr ctx)
-        (progn
-;;          (message "ctx=%S" (car (cdr ctx)))
-          (setq out (cadr ctx))
-;;          (message "out=%S" out)
-          )
-      (setq h (web-mode-previous-line pos limit))
-      (setq out initial-column)
-      (when h
-        (setq prev-line (car h))
-        (setq prev-indentation (cdr h))
-        (cond
-         ((string-match-p "^\\(end\\|else\\|elsif\\|when\\)" line)
-          (setq out (- prev-indentation language-offset))
-          )
-         ((string-match-p "\\(when\\|if\\|else\\|elsif\\|unless\\|for\\|while\\|def\\|class\\)" prev-line)
-          (setq out (+ prev-indentation language-offset))
-          )
-         (t
-          (setq out prev-indentation)
-          )
-         )
-        ) ;when
-      ) ;if
-    out
-    ))
-
-(defun web-mode-python-indentation (pos line initial-column language-offset limit)
-  "Calc indent column."
-  (interactive)
-  (unless limit (setq limit nil))
-  (let (h out prev-line prev-indentation ctx)
-;;    (setq ctx (web-mode-count-opened-blocks pos limit))
-;;    ;; (if (cddr ctx)
-;;        (progn
-;;          (message "ctx=%S" (car (cdr ctx)))
-;;          (setq out (cadr ctx))
-;;          (message "out=%S" out)
-;;          )
-    (setq h (web-mode-previous-line pos limit))
-    ;;      (message "h=%S" h)
-    (setq out initial-column)
-    (when h
-      (setq prev-line (car h))
-      ;;(message "line=%S" prev-line)
-      (setq prev-indentation (cdr h))
-      (cond
-       ((string-match-p "^\\(pass\\|else\\|elif\\|when\\)" line)
-        (setq out (- prev-indentation language-offset))
-        )
-       ((string-match-p "\\(if\\|else\\|elif\\|for\\|while\\)" prev-line)
-        (setq out (+ prev-indentation language-offset))
-        )
-       (t
-        (setq out prev-indentation)
-        )
-       )
-      ) ;when
-    ;;      ) ;if
-    out
-    ))
-
-(defun web-mode-asp-indentation (pos line initial-column language-offset limit)
-  "Calc indent column."
-  (interactive)
-  (unless limit (setq limit nil))
-  (let (h out prev-line prev-indentation)
-    (setq h (web-mode-previous-line pos limit))
-    (setq out initial-column)
-    (when h
-      (setq prev-line (car h))
-      (setq prev-indentation (cdr h))
-      (cond
-       ;; ----------------------------------------------------------------------
-       ;; unindent
-       ((string-match-p "\\<\\(\\(end \\(if\\|function\\|class\\|sub\\|with\\)\\)\\|else\\|elseif\\|next\\|loop\\)\\>" line)
-        (setq out (- prev-indentation language-offset)))
-       ;; ----------------------------------------------------------------------
-       ;; select case statement
-       ((string-match-p "\\<\\(select case\\)\\>" line)
-        (setq out (- prev-indentation 0)))
-       ((string-match-p "\\<\\(end select\\)" line)
-        (setq out (- prev-indentation (* 2 language-offset))))
-       ((and (string-match-p "\\<\\(case\\)\\>" line) (not (string-match-p "\\<\\(select case\\)\\>" prev-line)))
-        (setq out (- prev-indentation language-offset)))
-       ;; ----------------------------------------------------------------------
-       ;; do nothing
-       ((string-match-p "\\<\\(\\(end \\(if\\|function\\|class\\|sub\\|select\\|with\\)\\)\\|loop\\( until\\| while\\)?\\)\\>" prev-line)
-        (setq out (+ prev-indentation 0)))
-       ;; indent
-       ((string-match-p "\\<\\(\\(select \\)?case\\|else\\|elseif\\|unless\\|for\\|class\\|with\\|do\\( until\\| while\\)?\\|while\\|\\(public \\|private \\)?\\(function\\|sub\\|class\\)\\)\\>" prev-line)
-        (setq out (+ prev-indentation language-offset)))
-       ;; single line if statement
-       ((string-match-p "\\<if\\>.*\\<then\\>[ \t]*[[:alpha:]]+" prev-line)
-        (setq out (+ prev-indentation 0)))
-       ;; normal if statement
-       ((string-match-p "\\<\\if\\>" prev-line)
-        (setq out (+ prev-indentation language-offset)))
-       (t
-        (setq out prev-indentation))
-       )
-      ) ;when
-    out
-    ))
-
 (defun web-mode-previous-line (pos limit)
-  "Previous line"
   (save-excursion
     (let (beg end line (continue t))
       (goto-char pos)
@@ -5701,277 +7821,90 @@
           (setq continue nil))
         )
       (if (<= (point) limit)
-          ;;todo : affiner (le + 3 n est pas générique cf. <?php <% <%- etc.)
+          ;;todo : affiner (le + 3 n est pas générique cf. <?php <% <%- etc.)
           (setq beg (if (< (+ limit 3) end) (+ limit 3) end))
         (setq beg (line-beginning-position))
         ) ;if
       (setq line (buffer-substring-no-properties beg end))
-      ;;      (message "line=%s" line)
       (cons line (current-indentation))
       )))
 
-(defun web-mode-bracket-indentation (pos initial-column language-offset &optional limit)
-  "Calc indent column."
-  (interactive)
+(defun web-mode-bracket-up (pos language &optional limit)
   (unless limit (setq limit nil))
-  (save-excursion
-    (let (offset n first-char block-info col block-column (continue t))
-      (goto-char pos)
-      (setq first-char (char-after)
-            block-column initial-column)
-      (while continue
-        (forward-line -1)
-        (back-to-indentation)
-        (cond
-         ((or (> limit (point))
-              (bobp))
-          (setq continue nil)
-          )
-         ((and (= (current-indentation) initial-column)
-               (not (eolp)))
-          (setq continue nil)
-          (setq limit (point))
-          )
-         )
-        )
-;;      (message "ic=%S point=%S limit=%S" initial-column (point) limit)
-      (goto-char pos)
-
-      (setq block-info (web-mode-count-opened-blocks pos limit))
-;;      (message "bi=%S" block-info)
-      (setq col initial-column)
-      (if (cddr block-info)
-          (progn
-            (setq col (car (cdr block-info)))
-            )
-        (setq n (car block-info))
-        (setq col initial-column)
-;;        (message "initial-col=%S n=%S col=%S" initial-column n col)
-        (if (member first-char '(?\} ?\) ?\])) (setq n (1- n)))
-        (setq col (+ initial-column (* n language-offset)))
-        ) ;if
-      (if (< col block-column) block-column col)
-      )))
-
-;; return (opened-blocks . (col-num . arg-inline))
-(defun web-mode-count-opened-blocks (pos &optional limit)
-  "Count opened opened block at point."
-  (interactive)
-  (unless limit (setq limit nil))
+  ;;(message "pos(%S) language(%S) limit(%S)" pos language limit)
   (save-excursion
     (goto-char pos)
     (let ((continue t)
-          (match "")
-          (case-found nil)
-          (case-count 0)
-          (queues (make-hash-table :test 'equal))
-          (opened-blocks 0)
-          (col-num 0)
-          (regexp "[\]\[}{)(]\\|[ ;\t]\\(break[ ;]\\|case[ :]\\|default[ :]\\)")
-          (num-opened 0)
-          close-char n queue arg-inline arg-inline-checked char lines)
-
-      (while (and continue (re-search-backward regexp limit t))
-;;        (message "%S: %c" (point) (char-after))
-        (unless (web-mode-is-comment-or-string)
-          (setq match (match-string-no-properties 0))
-          (when (> (length match) 1)
-;;            (message "match=%S" (match-string-no-properties 0))
-            (skip-chars-forward "[ \t]")
-            (setq match (replace-regexp-in-string "\\`[ ;\t]*" "" (replace-regexp-in-string "[ :;]*\\'" "" match)))
-            )
-          (setq char (aref match 0))
-;;          (message "match:%S" match)
-
-          (cond
-
-           ((member char '(?\{ ?\( ?\[))
-            (cond
-             ((eq char ?\() (setq close-char ?\)))
-             ((eq char ?\{) (setq close-char ?\}))
-             ((eq char ?\[) (setq close-char ?\])))
-
-            (setq queue (gethash char queues nil))
-            (setq queue (push (cons (point) (web-mode-line-number)) queue))
-            (puthash char queue queues)
-;;            (message "%c queue=%S" char queue)
-
-            (setq queue (gethash close-char queues nil))
-            (setq n (length queue))
-            (cond
-             ((> n 0)
-              (setq queue (cdr queue))
-              (puthash close-char queue queues)
-              ;;(message "%c queue=%S" close-char queue)
-              (setq queue (gethash char queues nil))
-              (setq queue (cdr queue))
-              (puthash char queue queues)
-;;              (message "(%S) %c queue=%S" (point) char queue)
-              )
-             ((= n 0)
-              (setq num-opened (1+ num-opened))
-;;              (message "num-opened=%S %S" num-opened (point))
-              )
-             )
-
-            (when (and (= num-opened 1) (null arg-inline-checked))
-              (setq arg-inline-checked t)
-;;              (when (not (member (char-after (1+ (point))) '(?\n ?\r ?\{)))
-              (when (not (web-mode-is-void-after (1+ (point)))) ;(not (looking-at-p ".[ ]*$"))
-;;                (message "not void after %S" (1+ (point)))
-                (setq arg-inline t
-;;                      continue nil
-                      col-num (1+ (current-column)))
-                (when (looking-at ".[ ]+")
-                  (setq col-num (+ col-num (1- (length (match-string-no-properties 0)))))
-                  )
-                ) ;when
-;;              (message "pt=%S" (point))
-              )
-
-            ) ;case
-
-           ((member char '(?\} ?\) ?\]))
-            (setq queue (gethash char queues nil))
-            (setq queue (push (point) queue))
-            (puthash char queue queues)
-            ;;            (message "%c queue=%S" char queue)
-            )
-
-           ((member match '("case" "default"))
-            (setq case-found t
-                  case-count (1+ case-count))
-            )
-
-           ((string= match "break")
-            (setq case-count (1- case-count))
-            )
-
-           ) ;cond
-
-          ) ;unless
+          (regexp "[\]\[}{)(]")
+          (char nil)
+          (map nil)
+          (key nil)
+          (value 0)
+          (searcher (if (get-text-property pos 'block-side) 'web-mode-block-rsb 'web-mode-part-rsb)))
+      (while (and continue (funcall searcher regexp limit))
+        (setq char (aref (match-string-no-properties 0) 0))
+        (setq key (cond ((eq char ?\)) ?\()
+                        ((eq char ?\}) ?\{)
+                        ((eq char ?\]) ?\[)
+                        (t             char)))
+        (setq value (or (plist-get map key) 0))
+        (setq value (if (member char '(?\( ?\{ ?\[)) (1+ value) (1- value)))
+        (setq map (plist-put map key value))
+        (setq continue (< value 1))
+        ;;(message "pos=%S char=%c key=%c value=%S map=%S" (point) char key value map)
         ) ;while
-
-      (unless arg-inline
-;;        (message "%S" queues)
-        (maphash
-         (lambda (char queue)
-           (when (member char '(?\{ ?\( ?\[))
-;;             (message "%c => %S" char queue)
-             (dolist (pair queue)
-               (setq n (cdr pair))
-               (unless (member n lines)
-                 (push n lines))
-               )
-             ) ;when
-           )
-         queues)
-;;        (message "lines=%S case-found=%S case-count=%S" lines case-found case-count)
-        (setq opened-blocks (length lines))
-        (when (and case-found (> case-count 0))
-          (goto-char pos)
-          (back-to-indentation)
-;;          (message "%S" (point))
-;;          (when (not (looking-at-p "case\\|}"))
-;;            (setq opened-blocks (1+ opened-blocks))
-          (setq opened-blocks (+ opened-blocks case-count))
-;;            )
-          )
-        ) ;unless
-
-;;      (message "opened-blocks(%S) col-num(%S) arg-inline(%S)" opened-blocks col-num arg-inline)
-
-      (cons opened-blocks (cons col-num arg-inline))
-
-      )))
-
-(defun web-mode-is-void-after (pos)
-  "Only spaces or comment after pos"
-  (save-excursion
-    (goto-char pos)
-;;    (message "pos=%S" pos)
-    (let ((eol (line-end-position)) (continue t) c (ret t) part-side)
-      (setq part-side (not (null (get-text-property pos 'part-side))))
-      (while continue
-        (setq c (char-after pos))
-;;        (message "%S c='%c'" pos c)
-        (cond
-         ((member c '(?\s ?\n)) )
-         ((and part-side (eq (get-text-property pos 'part-token) 'comment)) )
-         ((and part-side (get-text-property pos 'block-side)) )
-         ((and (not part-side) (eq (get-text-property pos 'block-token) 'comment)) )
-         (t (setq ret nil
-                  continue nil))
-         )
-        (when continue
-          (setq pos (1+ pos))
-          (when (>= pos eol) (setq continue nil)))
-        ) ;while
-      ret)))
+      (list :pos (if (> value 0) (point) nil)
+            :char char
+            :column (current-column)
+            :indentation (current-indentation))
+      ) ;let
+    ))
 
 (defun web-mode-count-char-in-string (char string)
-  "Count char in string."
   (let ((n 0))
     (dotimes (i (length string))
       (if (eq (elt string i) char)
           (setq n (1+ n))))
     n))
 
-;; (defun web-mode-scan-at-pos ()
-;;   "web mode scan at point"
-;;   (save-excursion
-;;     (let (scan-beg scan-end (pos (point)))
-;;       (cond
-;;        ((web-mode-rsb-client "^[ ]*<")
-;;         (setq scan-beg (point))
-;;         (goto-char pos)
-;;         (setq scan-end (if (web-mode-rsf-client "[[:alnum:] /\"]>[ ]*$") (point) (point-max)))
-;;         ;;              (message "scan-end=%S" scan-end)
-;;         ;;            (setq scan-end (point-max))
-;;         )
-;;        (t
-;;         (setq scan-beg 1
-;;               scan-end (point-max))
-;;         )
-;;        ) ;cond
-;;       ;;(message "scan-region (%S) > (%S)" scan-beg scan-end)
-;;       ;;          (setq scan-end (point-max))
-;;       (web-mode-highlight-region scan-beg scan-end)
-;;       ) ;save-excursion
-;;     ))
-
 (defun web-mode-mark-and-expand ()
   "Mark and expand."
   (interactive)
-;;  (message "last-input-event=%S" last-input-event)
   (web-mode-mark (point)))
 
-;; todo : pb du engine=go ... selection d'un bloc
 (defun web-mode-mark (pos)
-  "Mark at point."
-
   (let ((beg pos) (end pos) prop reg-beg boundaries)
 
     (if mark-active
         (setq reg-beg (region-beginning))
-      (setq web-mode-expand-initial-pos (point)))
-
-    ;;    (message "regs=%S %S %S %S" (region-beginning) (region-end) (point-min) (point-max))
-
-    ;;    (message "before=%S" web-mode-expand-previous-state)
+      (setq web-mode-expand-initial-pos (point)
+            web-mode-expand-initial-scroll (window-start))
+      )
+
+    ;; (message "regs=%S %S %S %S" (region-beginning) (region-end) (point-min) (point-max))
+    ;; (message "before=%S" web-mode-expand-previous-state)
 
     (cond
 
      ((and mark-active
            (= (region-beginning) (point-min))
-           (or (= (region-end) (point-max)) (= (1+ (region-end)) (point-max))))
+           (or (= (region-end) (point-max))
+               (= (1+ (region-end)) (point-max))))
       (deactivate-mark)
       (goto-char (or web-mode-expand-initial-pos (point-min)))
-      (recenter))
-
-    ((and (member (get-text-property pos 'block-token) '(comment string))
-          (not (string= web-mode-expand-previous-state "block-token")))
-
+      (setq web-mode-expand-previous-state nil)
+      (when web-mode-expand-initial-scroll
+        (set-window-start (selected-window) web-mode-expand-initial-scroll))
+      )
+
+     ((string= web-mode-expand-previous-state "elt-content")
+      (web-mode-element-parent)
+      ;;(message "pos=%S" (point))
+      (web-mode-element-select)
+      (setq web-mode-expand-previous-state "html-parent"))
+
+     ((and (member (get-text-property pos 'block-token) '(comment string))
+           (not (member web-mode-expand-previous-state '("block-token" "block-body" "block-side"))))
       (when (eq (get-text-property pos 'block-token) (get-text-property (1- pos) 'block-token))
         (setq beg (or (previous-single-property-change pos 'block-token) (point-min))))
       (when (eq (get-text-property pos 'block-token) (get-text-property (1+ pos) 'block-token))
@@ -5981,33 +7914,24 @@
       (exchange-point-and-mark)
       (setq web-mode-expand-previous-state "block-token"))
 
-     ((and (eq (get-text-property pos 'block-side) t)
+     ((and (get-text-property pos 'block-side)
+           (not (member web-mode-expand-previous-state '("block-body" "block-side")))
            (not (member web-mode-engine '(django go)))
-           (setq boundaries (web-mode-in-code-block "{" "}" 'block-side))
-           (not (string= web-mode-expand-previous-state "server-block")))
+           (setq boundaries (web-mode-in-code-block "{" "}" 'block-side)))
       (set-mark (car boundaries))
       (goto-char (cdr boundaries))
-      ;;      (message "char=[%c]" (char-before (- (point) 1)))
-      (if (eq ?\% (char-before (- (point) 1)))
-          (setq web-mode-expand-previous-state "block-side")
-        (setq web-mode-expand-previous-state "server-block"))
       (exchange-point-and-mark)
-      )
-
-     ((and (eq (get-text-property pos 'block-side) t)
-           (not (string= web-mode-expand-previous-state "block-side")))
-      (when (eq (get-text-property pos 'block-side) (get-text-property (1- pos) 'block-side))
-        (setq beg (or (previous-single-property-change pos 'block-side) (point-min))))
-      (when (eq (get-text-property pos 'block-side) (get-text-property (1+ pos) 'block-side))
-        (setq end (next-single-property-change pos 'block-side)))
-      (set-mark beg)
-      (goto-char end)
+      (setq web-mode-expand-previous-state "block-body"))
+
+     ((and (get-text-property pos 'block-side)
+           (not (member web-mode-expand-previous-state '("block-side"))))
+      (set-mark (web-mode-block-beginning-position pos))
+      (goto-char (1+ (web-mode-block-end-position pos)))
       (exchange-point-and-mark)
       (setq web-mode-expand-previous-state "block-side"))
 
      ((and (get-text-property pos 'part-token)
            (not (string= web-mode-expand-previous-state "part-token")))
-
       (when (eq (get-text-property pos 'part-token) (get-text-property (1- pos) 'part-token))
         (setq beg (previous-single-property-change pos 'part-token)))
       (when (eq (get-text-property pos 'part-token) (get-text-property (1+ pos) 'part-token))
@@ -6023,90 +7947,104 @@
       (set-mark (car boundaries))
       (goto-char (cdr boundaries))
       (exchange-point-and-mark)
-      (setq web-mode-expand-previous-state "client-part")
-      )
+      (setq web-mode-expand-previous-state "client-part"))
 
      ((and (get-text-property pos 'part-side)
            (not (string= web-mode-expand-previous-state "part-side")))
-
       (when (eq (get-text-property pos 'part-side) (get-text-property (1- pos) 'part-side))
         (setq beg (previous-single-property-change pos 'part-side)))
       (when (eq (get-text-property pos 'part-side) (get-text-property (1+ pos) 'part-side))
         (setq end (next-single-property-change pos 'part-side)))
-      (set-mark beg)
-      (goto-char end)
-      (exchange-point-and-mark)
-      (setq web-mode-expand-previous-state "part-side"))
-
-     ;;     ((and (eq (get-text-property pos 'markup-type) 'attr)
-     ((and (get-text-property pos 'tag-attr)
-           (not (string= web-mode-expand-previous-state "html-attr")))
-
-      ;; todo: tester que le car précédent n'est pas un
-      (when (eq (get-text-property pos 'part-token) (get-text-property (1- pos) 'part-token))
-        (setq beg (previous-single-property-change pos 'part-token)))
-      (when (eq (get-text-property pos 'part-token) (get-text-property (1+ pos) 'part-token))
-        (setq end (next-single-property-change pos 'part-token)))
+      (when (eq (char-after beg) ?\n)
+        (setq beg (1+ beg)))
       (set-mark beg)
       (goto-char end)
+      (when (looking-back "^[ \t]+")
+        (beginning-of-line))
       (exchange-point-and-mark)
+      (setq web-mode-expand-previous-state "part-side"))
+
+     ((and (get-text-property pos 'tag-attr)
+           (not (member web-mode-expand-previous-state '("html-attr" "html-tag"))))
+      (web-mode-attribute-select pos)
       (setq web-mode-expand-previous-state "html-attr"))
 
-     ((and mark-active
-           (eq ?\< (char-after)))
-
-      (web-mode-element-parent)
-      (if (= reg-beg (region-beginning))
-          (mark-whole-buffer)
-        (web-mode-element-select))
-      )
+     ((and (eq (get-text-property pos 'tag-type) 'comment)
+           (not (member web-mode-expand-previous-state '("html-tag" "html-comment" "html-elt" "html-parent"))))
+      (web-mode-tag-select)
+      (setq web-mode-expand-previous-state "html-comment"))
+
+     ((and (get-text-property pos 'tag-name)
+           (not (member web-mode-expand-previous-state '("html-tag" "html-elt" "html-parent"))))
+      (web-mode-tag-select)
+      (setq web-mode-expand-previous-state "html-tag"))
+
+     ((and (get-text-property pos 'tag-beg)
+           (string= web-mode-expand-previous-state "html-tag"))
+      (web-mode-element-select)
+      (setq web-mode-expand-previous-state "html-elt"))
 
      (t
-      (web-mode-element-select)
-      ;;(mark-whole-buffer)
-      )
+      (cond
+       ((not (web-mode-element-parent))
+        (push-mark (point))
+        (push-mark (point-max) nil t)
+        (goto-char (point-min))
+        (setq web-mode-expand-previous-state "mark-whole"))
+       ((not (= (web-mode-tag-end-position (point)) (1- beg)))
+        (web-mode-element-content-select)
+        (setq web-mode-expand-previous-state "elt-content"))
+       (t
+        (web-mode-element-select)
+        (setq web-mode-expand-previous-state "html-parent"))
+       )
+      ) ;t
 
      ) ;cond
 
-;;    (message "after=%S" web-mode-expand-previous-state)
+    ;;(message "w=%S" (window-end))
+    ;;(message "after=%S" web-mode-expand-previous-state)
 
     ))
 
+(defun web-mode-block-kill ()
+  "Kill the current block."
+  (interactive)
+  (web-mode-block-select)
+  (when mark-active
+    (kill-region (region-beginning) (region-end))))
+
 (defun web-mode-block-select ()
   "Select the current block."
   (interactive)
   (let (beg)
-    (setq beg (web-mode-block-beginning-position (point)))
-    (when beg
+    (when (setq beg (web-mode-block-beginning-position (point)))
       (goto-char beg)
       (set-mark (point))
       (web-mode-block-end)
-      (exchange-point-and-mark)
-      )
+      (exchange-point-and-mark))
     beg))
 
 (defun web-mode-tag-select ()
-  "Select the current HTML tag."
+  "Select the current html tag."
   (interactive)
   (let (beg)
-    (setq beg (web-mode-tag-beginning-position (point)))
-    (when beg
+    (when (setq beg (web-mode-tag-beginning-position (point)))
       (goto-char beg)
       (set-mark (point))
       (web-mode-tag-end)
-      (exchange-point-and-mark)
-      )
+      (exchange-point-and-mark))
     beg))
 
 (defun web-mode-element-content-select ()
-  "Select the content of a HTML element."
+  "Select the content of a html element."
   (interactive)
   (let (pos beg end)
     (web-mode-element-select)
     (when mark-active
       (setq pos (point))
       (deactivate-mark)
-      (web-mode-html-tag-match)
+      (web-mode-tag-match)
       (setq end (point))
       (goto-char pos)
       (web-mode-tag-end)
@@ -6116,10 +8054,10 @@
       )))
 
 (defun web-mode-element-select ()
-  "Select the current HTML element (including opening and closing tags)."
+  "Select the current html element (including opening and closing tags)."
   (interactive)
-  (let (type (pos (point)))
-    (setq type (get-text-property pos 'tag-type))
+  (let* ((pos (point))
+         (type (get-text-property pos 'tag-type)))
     (if type
         (cond
          ((member type '(start void))
@@ -6141,19 +8079,15 @@
     ))
 
 (defun web-mode-element-is-collapsed (&optional pos)
-  "Is the HTML element collapsed ?"
   (unless pos (setq pos (point)))
-  (let (boundaries ret)
-    (setq boundaries (web-mode-element-boundaries pos))
-    (setq ret (and boundaries
-                   (or (= (car (car boundaries)) (car (cdr boundaries)))
-                       (= (cdr (car boundaries)) (1- (car (cdr boundaries)))))
-                   ))
-;;    (when ret (message "elt(%S) is collapsed" pos))
-    ret))
+  (let (boundaries)
+    (and (setq boundaries (web-mode-element-boundaries pos))
+         (or (= (car (car boundaries)) (car (cdr boundaries)))
+             (= (cdr (car boundaries)) (1- (car (cdr boundaries)))))
+         )))
 
 (defun web-mode-element-transpose ()
-  "Transpose two HTML elements."
+  "Transpose two html elements."
   (interactive)
   (let (pos start1 end1 start2 end2)
     (save-excursion
@@ -6161,7 +8095,7 @@
       (cond
        ((get-text-property pos 'tag-type)
         (setq start1 (web-mode-element-beginning-position pos)
-            end1 (1+ (web-mode-element-end-position pos)))
+              end1 (1+ (web-mode-element-end-position pos)))
         )
        ((setq start1 (web-mode-element-parent-position pos))
         (setq end1 (1+ (web-mode-element-end-position pos)))
@@ -6176,46 +8110,33 @@
                 end2 (1+ (web-mode-element-end-position (point))))
           )
         )
-;;      (message "start1(%S) end1(%S) start2(%S) end2(%S)"
-;;               start1 end1 start2 end2)
       (transpose-regions start1 end1 start2 end2)
       ) ;save-excursion
-    start2
-    ))
+    start2))
 
 (defun web-mode-element-children-fold-or-unfold (&optional pos)
-  "Fold/Unfold all the children of the current HTML element."
+  "Fold/Unfold all the children of the current html element."
   (interactive)
   (unless pos (setq pos (point)))
-  (let (child children)
-    (save-excursion
-      (setq children (reverse (web-mode-element-children-position pos)))
-      (dolist (child children)
-;;        (message "child(%S)" child)
-        (goto-char child)
-        (web-mode-fold-or-unfold)
-        )
-      )))
+  (save-excursion
+    (dolist (child (reverse (web-mode-element-children pos)))
+      (goto-char child)
+      (web-mode-fold-or-unfold))
+    ))
 
 (defun web-mode-element-mute-blanks ()
   "Mute blanks."
   (interactive)
-  (let (pos parent beg end child children elt)
+  (let (pos parent beg end children elt)
     (setq pos (point))
     (save-excursion
       (when (and (setq parent (web-mode-element-boundaries pos))
-                 (setq child (web-mode-element-child-position (point))))
-        (setq children (reverse (web-mode-element-children-position)))
-;;        (message "%S %S" parent children)
-        ;;        (setq end (car (cdr parent)))
-        ;;        (message "end=%S" end)
+                 (web-mode-element-child-position (point)))
+        (setq children (reverse (web-mode-element-children)))
         (goto-char (car (cdr parent)))
         (dolist (child children)
           (setq elt (web-mode-element-boundaries child))
-          ;;          (message "child=%S elt=%S" child elt)
           (when (> (point) (1+ (cddr elt)))
-            ;;(message "%S-->" (point))
-            ;;(message "%S<!--" (1+ (cddr elt)))
             (when (and (not (eq (get-text-property (point) 'part-token) 'comment))
                        (not (eq (get-text-property (1+ (cddr elt)) 'part-token) 'comment)))
               (web-mode-insert-text-at-pos "-->" (point))
@@ -6228,82 +8149,109 @@
                    (not (eq (get-text-property (1+ (cdr (car parent))) 'part-token) 'comment)))
           (web-mode-insert-text-at-pos "-->" (point))
           (web-mode-insert-text-at-pos "<!--" (1+ (cdr (car parent)))))
-
         ) ;when
       )))
 
-(defun web-mode-element-children-position (&optional pos)
+(defun web-mode-element-children (&optional pos)
   (unless pos (setq pos (point)))
   (let ((continue t) (i 0) child children)
     (save-excursion
       (when (and (member (get-text-property pos 'tag-type) '(start end))
                  (setq child (web-mode-element-child-position pos)))
         (while continue
-          (setq i (1+ i))
           (cond
+           ((> (setq i (1+ i)) 100)
+            (setq continue nil)
+            (message "element-children ** warning **"))
            ((= i 1)
-            (goto-char child)
-            )
+            (goto-char child))
            ((web-mode-element-sibling-next)
             )
            (t
-            (setq continue nil)
-            )
+            (setq continue nil))
            ) ;cond
-          (when (> i 100)
-            (message "** invalid loop **")
-            (setq continue nil))
           (when continue
-;;            (message "child(%S)" (point))
             (setq children (append children (list (point)))))
           ) ;while
         ) ;when
       ) ;save-excursion
-    children
-    ))
-
-;; return ((start-tag-beg . start-tag-end) . (end-tag-beg end-tag-end))
-;; car and cdr are the same with void elements
+    children))
+
 (defun web-mode-element-boundaries (&optional pos)
-  "pos should be in a tag"
+  "Return ((start-tag-beg . start-tag-end) . (end-tag-beg . end-tag-end))
+First level car and cdr are the same with void elements.
+Pos should be in a tag."
   (unless pos (setq pos (point)))
   (let (start-tag-beg start-tag-end end-tag-beg end-tag-end)
-    (save-excursion
-      (cond
-       ((eq (get-text-property pos 'tag-type) 'start)
-        (setq start-tag-beg (web-mode-tag-beginning-position pos)
-              start-tag-end (web-mode-tag-end-position pos))
-        (web-mode-html-tag-match pos)
-;;        (message "pos=%S point=%S" pos (point))
-        (setq end-tag-beg (point)
-              end-tag-end (web-mode-tag-end-position (point)))
-        )
-       ((eq (get-text-property pos 'tag-type) 'end)
-        (setq end-tag-beg (web-mode-tag-beginning-position pos)
-              end-tag-end (web-mode-tag-end-position pos))
-        (web-mode-html-tag-match pos)
-        (setq start-tag-beg (point)
-              start-tag-end (web-mode-tag-end-position (point)))
-        )
-       ((eq (get-text-property pos 'tag-type) 'void)
-        (setq start-tag-beg (web-mode-tag-beginning-position pos)
-              start-tag-end (web-mode-tag-end-position pos))
-        (setq end-tag-beg start-tag-beg
-              end-tag-end start-tag-end)
-        )
-       ) ;cond
+    (cond
+     ((eq (get-text-property pos 'tag-type) 'start)
+      (setq start-tag-beg (web-mode-tag-beginning-position pos)
+            start-tag-end (web-mode-tag-end-position pos))
+      (when (setq pos (web-mode-tag-match-position pos))
+        (setq end-tag-beg pos
+              end-tag-end (web-mode-tag-end-position pos)))
       )
+     ((eq (get-text-property pos 'tag-type) 'end)
+      (setq end-tag-beg (web-mode-tag-beginning-position pos)
+            end-tag-end (web-mode-tag-end-position pos))
+      (when (setq pos (web-mode-tag-match-position pos))
+        (setq start-tag-beg pos
+              start-tag-end (web-mode-tag-end-position pos)))
+      )
+     ((eq (get-text-property pos 'tag-type) 'void)
+      (setq start-tag-beg (web-mode-tag-beginning-position pos)
+            start-tag-end (web-mode-tag-end-position pos))
+      (setq end-tag-beg start-tag-beg
+            end-tag-end start-tag-end)
+      )
+     ) ;cond
     (if (and start-tag-beg start-tag-end end-tag-beg end-tag-end)
         (cons (cons start-tag-beg start-tag-end) (cons end-tag-beg end-tag-end))
       nil)
     ))
 
-(defun web-mode-element-wrap ()
-  "Wrap current REGION with start and end tags."
+(defun web-mode-surround ()
+  "Surround each line of the current REGION with a start/end tag."
+  (interactive)
+  (when mark-active
+    (let (beg end line-beg line-end pos tag tag-start tag-end)
+      (save-excursion
+        (setq tag (read-from-minibuffer "Tag name? ")
+              tag-start (concat "<" tag ">")
+              tag-end (concat "</" tag ">")
+              pos (point)
+              beg (region-beginning)
+              end (region-end)
+              line-beg (web-mode-line-number beg)
+              line-end (web-mode-line-number end))
+        (goto-char end)
+        (unless (bolp)
+          (insert tag-end)
+          (back-to-indentation)
+          (when (> beg (point))
+            (goto-char beg))
+          (insert tag-start))
+        (while (> line-end line-beg)
+          (forward-line -1)
+          (setq line-end (1- line-end))
+          (unless (looking-at-p "[[:space:]]*$")
+            (end-of-line)
+            (insert tag-end)
+            (back-to-indentation)
+            (when (> beg (point))
+              (goto-char beg))
+            (insert tag-start))
+          ) ;while
+        (deactivate-mark)
+        ))))
+
+(defun web-mode-element-wrap (&optional tag-name)
+  "Wrap current REGION with start and end tags.
+Prompt user if TAG-NAME isn't provided."
   (interactive)
   (let (beg end pos tag sep)
     (save-excursion
-      (setq tag (read-from-minibuffer "Tag name? "))
+      (setq tag (or tag-name (read-from-minibuffer "Tag name? ")))
       (setq pos (point))
       (cond
        (mark-active
@@ -6311,11 +8259,9 @@
               end (region-end)))
        ((get-text-property pos 'tag-type)
         (setq beg (web-mode-element-beginning-position pos)
-              end (1+ (web-mode-element-end-position pos)))
-        )
+              end (1+ (web-mode-element-end-position pos))))
        ((setq beg (web-mode-element-parent-position pos))
-        (setq end (1+ (web-mode-element-end-position pos)))
-        )
+        (setq end (1+ (web-mode-element-end-position pos))))
        )
       ;;      (message "beg(%S) end(%S)" beg end)
       (when (and beg end (> end 0))
@@ -6325,91 +8271,129 @@
         (when (string= sep "\n") (indent-region beg (+ end (* (+ 3 (length tag)) 2))))
         )
       ) ;save-excursion
-    (if beg (goto-char beg))
-    beg))
-
-(defun web-mode-element-vanish ()
-  "Vanish the current HTML element. The content of the element is kept."
-  (interactive)
+    (web-mode-go beg)))
+
+(defun web-mode-element-vanish (&optional arg)
+  "Vanish the current html element. The content of the element is kept."
+  (interactive "p")
   (let (type (pos (point)) start-b start-e end-b end-e)
-    (setq type (get-text-property pos 'tag-type))
-    (when type
-      (cond
-       ((member type '(void))
-        (web-mode-element-kill)
-        (set-mark (point))
-        (web-mode-tag-match)
-        (web-mode-tag-end)
-        (exchange-point-and-mark))
-       ((member type '(start))
-        (setq start-b (web-mode-tag-beginning-position)
-              start-e (web-mode-tag-end-position))
-        (when (web-mode-tag-match)
-          (setq end-b (web-mode-tag-beginning-position)
-                end-e (web-mode-tag-end-position)))
-        )
-       (t
-        (setq end-b (web-mode-tag-beginning-position)
-              end-e (web-mode-tag-end-position))
-        (when (web-mode-tag-match)
+    (while (>= arg 1)
+      (setq type (get-text-property pos 'tag-type))
+      (when type
+        (cond
+         ((member type '(void))
+          (web-mode-element-kill)
+          (set-mark (point))
+          (web-mode-tag-match)
+          (web-mode-tag-end)
+          (exchange-point-and-mark))
+         ((member type '(start))
           (setq start-b (web-mode-tag-beginning-position)
-                start-e (web-mode-tag-end-position)))
-        ) ;t
-       ) ;cond
-      (when (and start-b end-b)
-        (goto-char end-b)
-        (delete-region end-b (1+ end-e))
-        (delete-blank-lines)
-        (goto-char start-b)
-        (delete-region start-b (1+ start-e))
-        (delete-blank-lines)
-        (web-mode-buffer-indent)
-        )
-;;        (message "start %S %S - end %S %S" start-b start-e end-b end-e))
-      ) ;when
-    ))
-
-(defun web-mode-element-kill ()
-  "Kill the current HTML element."
-  (interactive)
-  (web-mode-element-select)
-  (when mark-active
-    (kill-region (region-beginning) (region-end))))
-
-(defun web-mode-block-kill ()
-  "Kill the current block."
-  (interactive)
-  (web-mode-block-select)
-  (when mark-active
-    (kill-region (region-beginning) (region-end))))
-
-(defun web-mode-element-clone ()
-  "Clone the current HTML element."
-  (interactive)
-  (let ((offset 0))
+                start-e (web-mode-tag-end-position))
+          (when (web-mode-tag-match)
+            (setq end-b (web-mode-tag-beginning-position)
+                  end-e (web-mode-tag-end-position)))
+          )
+         (t
+          (setq end-b (web-mode-tag-beginning-position)
+                end-e (web-mode-tag-end-position))
+          (when (web-mode-tag-match)
+            (setq start-b (web-mode-tag-beginning-position)
+                  start-e (web-mode-tag-end-position)))
+          ) ;t
+         ) ;cond
+        (when (and start-b end-b)
+          (goto-char end-b)
+          (delete-region end-b (1+ end-e))
+          (delete-blank-lines)
+          (goto-char start-b)
+          (delete-region start-b (1+ start-e))
+          (delete-blank-lines)
+          (web-mode-buffer-indent)
+          )
+        ;;        (message "start %S %S - end %S %S" start-b start-e end-b end-e))
+        ) ;when
+      (skip-chars-forward "[:space:]\n")
+      (setq arg (1- arg))
+      ) ;while
+    ) ;let
+  )
+
+(defun web-mode-element-kill (&optional arg)
+  "Kill the current html element."
+  (interactive "p")
+  (while (>= arg 1)
+    (setq arg (1- arg))
     (web-mode-element-select)
     (when mark-active
-      (save-excursion
-        (goto-char (region-beginning))
-        (setq offset (current-column)))
-      (kill-region (region-beginning) (region-end))
-      (yank)
-      (newline)
-      (indent-line-to offset)
-      (yank))))
+      (kill-region (region-beginning) (region-end)))
+    ) ;while
+  )
+
+(defun web-mode-element-clone (&optional arg)
+  "Clone the current html element."
+  (interactive "p")
+  (let (col pos)
+    (while (>= arg 1)
+      (setq arg (1- arg)
+            col 0)
+      (web-mode-element-select)
+      (when mark-active
+        (save-excursion
+          (goto-char (region-beginning))
+          (setq col (current-column)))
+        (kill-region (region-beginning) (region-end))
+        (yank)
+        (newline)
+        (indent-line-to col)
+        (setq pos (point))
+        (yank)
+        (goto-char pos))
+      )
+    ) ;let
+  )
+
+(defun web-mode-element-insert ()
+  "Insert an html element."
+  (interactive)
+  (let (tag-name)
+    (cond
+     ((and (get-text-property (point) 'tag-type)
+           (not (get-text-property (point) 'tag-beg)))
+      (message "element-insert ** invalid context **"))
+     ((not (and (setq tag-name (read-from-minibuffer "Tag name? "))
+                (> (length tag-name) 0)))
+      (message "element-insert ** failure **"))
+     ((web-mode-element-is-void tag-name)
+      (insert (concat "<" tag-name "/>"))
+      )
+     (mark-active
+      (let ((beg (region-beginning)) (end (region-end)))
+        (deactivate-mark)
+        (goto-char end)
+        (insert "</" tag-name ">")
+        (goto-char beg)
+        (insert "<" tag-name ">")
+        )
+      )
+     (t
+      (insert (concat "<" tag-name ">" "</" tag-name ">"))
+      (web-mode-sb "</")
+      )
+     ) ;cond
+    ))
 
 (defun web-mode-element-rename ()
-  "Rename the current HTML element."
+  "Rename the current html element."
   (interactive)
   (save-excursion
     (let (pos tag-name)
-      (setq tag-name (read-from-minibuffer "Tag name? "))
+      (setq tag-name (read-from-minibuffer "New tag name? "))
       (when (and (> (length tag-name) 0)
                  (web-mode-element-beginning)
                  (looking-at "<\\([[:alnum:]]+\\(:?[-][[:alpha:]]+\\)?\\)"))
         (setq pos (point))
-;;        (message "match=%S" (match-string-no-properties 1))
-        (unless (web-mode-is-void-element)
+        (unless (web-mode-element-is-void)
             (save-match-data
               (web-mode-tag-match)
               (if (looking-at "</[ ]*\\([[:alnum:]]+\\(:?[-][[:alpha:]]+\\)?\\)")
@@ -6417,29 +8401,112 @@
                 )))
         (goto-char pos)
         (replace-match (concat "<" tag-name))
-;;        (web-mode-scan-at-pos)
         ))))
 
 (defun web-mode-current-trimmed-line ()
-  "Line at point, trimmed."
   (web-mode-trim (buffer-substring-no-properties
                   (line-beginning-position)
                   (line-end-position))))
 
 (defun web-mode-trim (string)
-  "Remove white spaces in beginning and ending of STRING."
   (replace-regexp-in-string "\\`[ \t\n]*" "" (replace-regexp-in-string "[ \t\n]*\\'" "" string)))
 
-(defun web-mode-is-void-element (&optional tag)
-  "Test if tag is a void tag."
-  (if tag
-      (car (member (downcase tag) web-mode-void-elements))
-    (eq (get-text-property (point) 'tag-type) 'void)
+(defun web-mode-block-is-token-line ()
+  (save-excursion
+    (let ((continue t) (counter 0))
+      (beginning-of-line)
+      (back-to-indentation)
+      (while (and continue (not (eolp)))
+        (cond
+         ((get-text-property (point) 'block-token)
+          (setq counter (1+ counter)))
+         ((not (eq ?\s (following-char)))
+          (setq continue nil
+                counter 0))
+         ) ;cond
+        (forward-char)
+        ) ;while
+      (> counter 0)
+      )))
+
+(defun web-mode-part-is-token-line (pos)
+  (save-excursion
+    (let ((continue t)
+          (counter 0))
+      (goto-char pos)
+      (setq continue (not (eolp)))
+      (while continue
+        (forward-char)
+        (cond
+         ((eolp)
+          (setq continue nil))
+         ((or (get-text-property (point) 'block-side)
+              (member (get-text-property (point) 'part-token) '(comment string)))
+          (setq counter (1+ counter)))
+         ((eq ?\s (following-char))
+          )
+         (t
+          (setq continue nil
+                counter 0))
+         )
+        ) ;while
+      (> counter 0))))
+
+(defun web-mode-is-content (&optional pos)
+  (unless pos (setq pos (point)))
+  (not (or (get-text-property pos 'part-side)
+           (get-text-property pos 'tag-type)
+           (get-text-property pos 'block-side)
+           )))
+
+(defun web-mode-is-comment-or-string (&optional pos)
+  (unless pos (setq pos (point)))
+  (not (null (or (eq (get-text-property pos 'tag-type) 'comment)
+                 (member (get-text-property pos 'block-token) '(comment string))
+                 (member (get-text-property pos 'part-token) '(comment string))))))
+
+;; NOTE: we look at the firt one
+(defun web-mode-block-is-open (&optional pos)
+  (unless pos (setq pos (point))))
+
+;; NOTE: we look at the last one
+(defun web-mode-block-is-close (&optional pos)
+  (unless pos (setq pos (point)))
+  (and (get-text-property pos 'block-side)
+       (eq (caar (web-mode-block-controls-get pos)) 'close)))
+
+;; NOTE: we look at the first one
+(defun web-mode-block-is-inside (&optional pos)
+  (unless pos (setq pos (point)))
+  (and (get-text-property pos 'block-side)
+       (eq (caar (web-mode-block-controls-get pos)) 'inside)))
+
+(defun web-mode-element-is-void (&optional tag)
+  (cond
+   ((and tag (member tag '("div" "li" "a" "p")))
+    nil)
+   ((and tag (string= web-mode-content-type "jsx"))
+    (member (downcase tag) '("img" "br" "hr")))
+   (tag
+    (car (member (downcase tag) web-mode-void-elements)))
+   (t
+    (eq (get-text-property (point) 'tag-type) 'void))
+   ))
+
+(defun web-mode-toggle-current-element-highlight ()
+  "Toggle highlighting of the current html element."
+  (interactive)
+  (if web-mode-enable-current-element-highlight
+      (progn
+        (web-mode-delete-tag-overlays)
+        (setq web-mode-enable-current-element-highlight nil))
+    (setq web-mode-enable-current-element-highlight t)
     ))
 
 (defun web-mode-fold-or-unfold (&optional pos)
-  "Toggle folding on an HTML element or a control block."
+  "Toggle folding on an html element or a control block."
   (interactive)
+  (web-mode-propertize)
   (web-mode-with-silent-modifications
    (save-excursion
      (if pos (goto-char pos))
@@ -6447,10 +8514,13 @@
        (when (looking-back "^[\t ]*")
          (back-to-indentation))
        (setq overlays (overlays-at (point)))
+       (dolist (elt overlays)
+         (when (and (not overlay)
+                    (eq (overlay-get elt 'font-lock-face) 'web-mode-folded-face))
+           (setq overlay elt)))
        (cond
         ;; *** unfolding
-        (overlays
-         (setq overlay (car overlays))
+        (overlay
          (setq beg-inside (overlay-start overlay)
                end-inside (overlay-end overlay))
          (remove-overlays beg-inside end-inside)
@@ -6461,39 +8531,34 @@
          (when (not (web-mode-element-is-collapsed (point)))
            (web-mode-tag-beginning)
            (when (eq (get-text-property (point) 'tag-type) 'end)
-             (web-mode-html-tag-match))
+             (web-mode-tag-match))
            (setq beg-outside (point))
            (web-mode-tag-end)
            (setq beg-inside (point))
            (goto-char beg-outside)
-           (when (web-mode-html-tag-match)
+           (when (web-mode-tag-match)
              (setq end-inside (point))
              (web-mode-tag-end)
              (setq end-outside (point)))
            )
          )
         ;; *** block folding
-        ((cdr (web-mode-is-active-block (point)))
-         (setq beg-outside (point))
-         (web-mode-block-end)
-         (setq beg-inside (point))
-         (goto-char beg-outside)
-         (when (web-mode-html-tag-match)
+        ((cdr (web-mode-block-is-control (point)))
+         (setq beg-outside (web-mode-block-beginning-position (point)))
+         (setq beg-inside (1+ (web-mode-block-end-position (point))))
+         (when (web-mode-block-match)
            (setq end-inside (point))
-           (web-mode-block-end)
-           (setq end-outside (point)))
+           (setq end-outside (1+ (web-mode-block-end-position (point)))))
          )
         ) ;cond
-       (when end-outside
-         ;;(message "beg-out(%d) beg-in(%d) end-in(%d) end-out(%d)" beg-outside beg-inside end-inside end-outside)
+       (when (and beg-inside beg-outside end-inside end-outside)
          (setq overlay (make-overlay beg-outside end-outside))
          (overlay-put overlay 'font-lock-face 'web-mode-folded-face)
-         (put-text-property beg-inside end-inside 'invisible t)
-         )
+         (put-text-property beg-inside end-inside 'invisible t))
        ))))
 
 (defun web-mode-toggle-comments ()
-  "Toggle comments visbility"
+  "Toggle comments visbility."
   (interactive)
   (save-excursion
     (if web-mode-comments-invisible
@@ -6519,419 +8584,420 @@
       ) ;let
     ))
 
-(defun web-mode-is-single-line-block (pos)
-  "Is block at POS spread on a single line ?"
-  (= (web-mode-line-number (web-mode-block-beginning-position pos))
-     (web-mode-line-number (web-mode-block-end-position pos))))
+(defun web-mode-comment-or-uncomment-region (beg end &optional arg)
+  (interactive)
+  (save-excursion
+    (push-mark end)
+    (goto-char beg)
+    (setq mark-active t)
+    (web-mode-comment-or-uncomment)
+    (pop-mark)))
 
 (defun web-mode-comment-or-uncomment ()
   "Comment or uncomment line(s), block or region at POS."
   (interactive)
-;;  (message "%S" (point))
-  (save-excursion
-    (unless mark-active
-      (skip-chars-forward "[:space:]" (line-end-position)))
-    (if (web-mode-is-comment)
-	(web-mode-uncomment (point))
-      (web-mode-comment (point))))
-;;  (message "%S" (point))
-;;  (web-mode-highlight-region (point-min) (point-max))
+  ;; TODO : if mark is at eol, mark--
+  (if (and (not mark-active) (looking-at-p "[[:space:]]*$"))
+      (web-mode-comment-insert)
+    (when (and (use-region-p) (eq (point) (region-end)))
+      (if (bolp) (backward-char))
+      (exchange-point-and-mark))
+    (skip-chars-forward "[:space:]" (line-end-position))
+    (cond
+     ((or (eq (get-text-property (point) 'tag-type) 'comment)
+          (eq (get-text-property (point) 'block-token) 'comment)
+          (eq (get-text-property (point) 'part-token) 'comment))
+      (web-mode-uncomment (point)))
+     (t
+      (web-mode-comment (point)))
+     )
+    ) ;if
+  )
+
+(defun web-mode-comment-insert ()
+  (cond
+   ((get-text-property (point) 'block-side)
+    (insert "/*  */")
+    (search-backward " */"))
+   ((get-text-property (point) 'part-side)
+    (insert "/*  */")
+    (search-backward " */"))
+   (t
+    (insert "<!--  -->")
+    (search-backward " -->"))
+   )
   )
 
+(defun web-mode-comment-indent-new-line (&optional soft)
+  (interactive)
+  (let (ctx)
+    (setq ctx (web-mode-comment-context))
+    (if (null ctx) nil
+      ;; (message "ctx=%S" ctx)
+      (newline 1)
+      (indent-line-to (plist-get ctx :col))
+      (insert (concat (plist-get ctx :prefix) ""))
+      ) ;if
+    ))
+
+(defun web-mode-comment-context (&optional pos)
+  (cond
+   (pos
+    )
+   ((and (eolp) (not (bobp)))
+    (setq pos (1- (point))))
+   (t
+    (setq pos (point)))
+   ) ;cond
+  (let (beg col prefix type format)
+    (cond
+     ((eq (get-text-property pos 'block-token) 'comment)
+      (setq type "block"))
+     ((eq (get-text-property pos 'tag-type) 'comment)
+      (setq type "tag"))
+     ((eq (get-text-property pos 'part-token) 'comment)
+      (setq type "part"))
+     )
+    (if (null type) nil
+      (save-excursion
+        (goto-char pos)
+        (web-mode-comment-beginning)
+        (setq beg (point)
+              col (current-column))
+        (cond
+         ((looking-at-p "/\\*")
+          (setq format "/*"
+                prefix " * "))
+         ((looking-at-p "//")
+          (setq format "//"
+                prefix "//"))
+         ((looking-at-p "#")
+          (setq format "#"
+                prefix "#"))
+         ((looking-at-p ";")
+          (setq format ";"
+                prefix ";"))
+         ) ;cond
+        (list :beg beg :col col :prefix prefix :type type :format format)))))
+
 (defun web-mode-comment (pos)
-  "Comment line(s) at point."
-  (interactive)
-;;  (message "pt=%S" pos)
-  (save-excursion
-    (let (ctx language sel beg end tmp block-side single-line-block)
-
-      (setq block-side (get-text-property pos 'block-side))
-      (setq single-line-block (web-mode-is-single-line-block pos))
-
-      (cond
-
-       ((and block-side
-             (string= web-mode-engine "django")
-             single-line-block)
-        (web-mode-comment-django-block pos)
-        )
-
-       ((and block-side
-             (string= web-mode-engine "dust")
-             single-line-block)
-        (web-mode-comment-dust-block pos)
-        )
-
-       ((and block-side
-             (string= web-mode-engine "erb")
-             single-line-block)
-        (web-mode-comment-erb-block pos)
-        )
-
-       ((and block-side
-             (string= web-mode-engine "aspx")
-             single-line-block)
-        (web-mode-comment-aspx-block pos)
-        )
-
-       ((and block-side
-             (string= web-mode-engine "jsp")
-             single-line-block)
-        (web-mode-comment-jsp-block pos)
-        )
-
-       ((and block-side
-             (string= web-mode-engine "go")
-             single-line-block)
-        (web-mode-comment-go-block pos)
-        )
-
-       ((and block-side
-             (string= web-mode-engine "php")
-             single-line-block)
-        (web-mode-comment-php-block pos)
-        )
+  (let (ctx language col sel beg end tmp block-side single-line-block pos-after content)
+
+    (setq pos-after pos)
+
+    (setq block-side (get-text-property pos 'block-side))
+    (setq single-line-block (web-mode-is-single-line-block pos))
+
+    (cond
+
+     ((and block-side (string= web-mode-engine "erb"))
+      (web-mode-comment-erb-block pos)
+      )
+
+     ((and single-line-block block-side
+           (intern-soft (concat "web-mode-comment-" web-mode-engine "-block")))
+        (funcall (intern (concat "web-mode-comment-" web-mode-engine "-block")) pos)
+        )
+
+     (t
+      (setq ctx (web-mode-point-context
+                 (if mark-active (region-beginning) (line-beginning-position))))
+      (setq language (plist-get ctx :language))
+      (setq col (current-column))
+      (cond
+       (mark-active
+        ;;(message "%S %S" (point) col)
+        )
+       ((and (member language '("html" "xml"))
+             (get-text-property (progn (back-to-indentation) (point)) 'tag-beg))
+        (web-mode-element-select))
+       (t
+        (end-of-line)
+        (set-mark (line-beginning-position)))
+       ) ;cond
+
+      (setq beg (region-beginning)
+            end (region-end))
+
+      (when (> (point) (mark))
+        (exchange-point-and-mark))
+
+      (if (and (eq (char-before end) ?\n)
+               (not (eq (char-after end) ?\n)))
+          (setq end (1- end)))
+
+      (setq sel (buffer-substring-no-properties beg end))
+
+      (cond
+
+       ((member language '("html" "xml"))
+        (cond
+           ((and (= web-mode-comment-style 2) (string= web-mode-engine "django"))
+            (setq content (concat "{# " sel " #}")))
+           ((and (= web-mode-comment-style 2) (member web-mode-engine '("ejs" "erb")))
+            (setq content (concat "<%# " sel " %>")))
+           ((and (= web-mode-comment-style 2) (string= web-mode-engine "aspx"))
+            (setq content (concat "<%-- " sel " --%>")))
+           ((and (= web-mode-comment-style 2) (string= web-mode-engine "smarty"))
+            (setq content (concat "{* " sel " *}")))
+           ((and (= web-mode-comment-style 2) (string= web-mode-engine "blade"))
+            (setq content (concat "{{-- " sel " --}}")))
+           ((and (= web-mode-comment-style 2) (string= web-mode-engine "ctemplate"))
+            (setq content (concat "{{!-- " sel " --}}")))
+           ((and (= web-mode-comment-style 2) (string= web-mode-engine "razor"))
+            (setq content (concat "@* " sel " *@")))
+           (t
+            (setq content (concat "<!-- " sel " -->"))
+            (when (< (length sel) 1)
+              (search-backward " -->")
+              (setq pos-after nil))
+            ))
+        ) ;case html
+
+       ((member language '("php" "javascript" "java" "jsx"))
+        (let (alt)
+          (cond
+           ((get-text-property pos 'jsx-depth)
+            (setq content (concat "{/* " sel " */}")))
+           ((and (setq alt (cdr (assoc language web-mode-comment-formats)))
+                 (string= alt "//"))
+            (setq content (replace-regexp-in-string (concat "\n[ ]\\{" (number-to-string col) "\\}") "\n// " sel))
+            (setq content (concat "// " content))
+            ;;(setq content (replace-regexp-in-string "^[ ]*" alt sel))
+            )
+           (web-mode-comment-prefixing
+            (setq content (replace-regexp-in-string (concat "\n[ ]\\{" (number-to-string col) "\\}") "\n* " sel))
+            (setq content (concat "/* " content "*/")))
+           (t
+            (setq content (concat "/* " sel " */")))
+           ) ;cond
+          ) ;let
+        )
+
+       ((member language '("erb"))
+        (setq content (replace-regexp-in-string "^[ ]*" "#" sel)))
+
+       ((member language '("asp"))
+        (setq content (replace-regexp-in-string "^[ ]*" "''" sel)))
 
        (t
-
-        (setq ctx (web-mode-point-context
-                   (if mark-active (region-beginning) (line-beginning-position))))
-;;        (message "ctx=%S" ctx)
-        (setq language (plist-get ctx :language))
-
-        (if mark-active
-            (progn
-              (setq beg (region-beginning)
-                    end (region-end))
-;;              (message "beg=%S end=%S" beg end)
-              )
-          (if (and (string= language "html")
-                   (progn (back-to-indentation) t)
-                   (get-text-property (point) 'tag-beg))
-              ;;              (progn
-              ;;                (back-to-indentation)
-              (web-mode-element-select)
-            ;;            )
-            (end-of-line)
-            (set-mark (line-beginning-position))
-            ) ;if
-          (setq beg (region-beginning)
-                end (region-end))
-          ) ;if mark-active
-
-        (when (> (point) (mark))
-          (exchange-point-and-mark))
-
-        (if (eq (char-before end) ?\n)
-            (setq end (1- end)))
-
-;;        (message "language=%S beg=%S end=%S" language beg end)
-        (setq sel (web-mode-trim (buffer-substring-no-properties beg end)))
-        ;;      (message "[language=%s] sel=%s" language sel)
+        (setq content (concat "/* " sel " */")))
+
+       ) ;cond
+
+      (when content
         (delete-region beg end)
         (deactivate-mark)
-
-        (cond
-
-         ((string= language "html")
-
-          (cond
-           ((and (= web-mode-comment-style 2) (string= web-mode-engine "django"))
-            (web-mode-insert-and-indent (concat "{# " sel " #}"))
-            )
-           ((and (= web-mode-comment-style 2) (string= web-mode-engine "erb"))
-            (web-mode-insert-and-indent (concat "<%# " sel " %>"))
-            )
-           ((and (= web-mode-comment-style 2) (string= web-mode-engine "aspx"))
-            (web-mode-insert-and-indent (concat "<%-- " sel " --%>"))
-            )
-           ((and (= web-mode-comment-style 2) (string= web-mode-engine "smarty"))
-            (web-mode-insert-and-indent (concat
-                                         (web-mode-engine-delimiter-open web-mode-engine "{")
-                                         "* "
-                                         sel
-                                         " *"
-                                         (web-mode-engine-delimiter-close web-mode-engine "}")))
-            )
-           ((and (= web-mode-comment-style 2) (string= web-mode-engine "blade"))
-            (web-mode-insert-and-indent (concat "{{-- " sel " --}}"))
-            )
-           ((and (= web-mode-comment-style 2) (string= web-mode-engine "razor"))
-            (web-mode-insert-and-indent (concat "@* " sel " *@"))
-            )
-           (t
-            (web-mode-insert-and-indent (concat "<!-- " sel " -->"))
-            )
-           )
-
-          )
-
-         ((member language '("php" "javascript" "css"))
-          (web-mode-insert-and-indent (concat "/* " sel " */")))
-
-         ((member language '("erb"))
-          (web-mode-insert-and-indent (replace-regexp-in-string "^" "#" sel)))
-
-         ((member language '("asp"))
-          (web-mode-insert-and-indent (replace-regexp-in-string "^" "''" sel)))
-
-         (t
-          (web-mode-insert-and-indent (concat "/* " sel " */")))
-
-         ) ;cond
-
-        ) ;t
-       ) ;cond
-
-      )
-    ) ;save-excursion
-;;  (message "%S" (point))
-;;  (goto-char pos)
-  )
-
-(defun web-mode-looking-at-pos (regexp pos)
-  "Performs a looking-at at POS."
-  (save-excursion
-    (goto-char pos)
-    (looking-at regexp)
+        (let (beg end)
+          (setq beg (point-at-bol))
+          (insert content)
+          (setq end (point-at-eol))
+          (indent-region beg end)
+          )
+        ) ;when
+
+      ) ;t
+     ) ;cond
+
+    (when pos-after (goto-char pos-after))
+
     ))
 
-(defun web-mode-insert-text-at-pos (text pos)
-  "Insert TEXT at POS."
-  (save-excursion
-    (goto-char pos)
-    (insert text)
-    ))
-
-(defun web-mode-remove-text-at-pos (n &optional pos)
-  "Remove N chars at POS."
-  (unless pos (setq pos (point)))
-  (delete-region pos (+ pos n)))
-
-(defun web-mode-uncomment-erb-block (pos)
-  "Uncomment an erb block."
+(defun web-mode-comment-ejs-block (pos)
   (let (beg end)
     (setq beg (web-mode-block-beginning-position pos)
           end (web-mode-block-end-position pos))
-    (web-mode-remove-text-at-pos 1 (+ beg 2))
-    ))
+    (web-mode-insert-text-at-pos "//" (+ beg 2))))
 
 (defun web-mode-comment-erb-block (pos)
-  "Turn an erb block into a comment block."
+  (let (beg end)
+    (setq beg (web-mode-block-beginning-position pos)
+          end (web-mode-block-end-position pos))
+    (web-mode-insert-text-at-pos "#" (+ beg 2))))
+
+(defun web-mode-comment-django-block (pos)
+  (let (beg end)
+    (setq beg (web-mode-block-beginning-position pos)
+          end (web-mode-block-end-position pos))
+    (web-mode-insert-text-at-pos "#" end)
+    (web-mode-insert-text-at-pos "#" (1+ beg))))
+
+(defun web-mode-comment-dust-block (pos)
+  (let (beg end)
+    (setq beg (web-mode-block-beginning-position pos)
+          end (web-mode-block-end-position pos))
+    (web-mode-insert-text-at-pos "!" end)
+    (web-mode-insert-text-at-pos "!" (1+ beg))))
+
+(defun web-mode-comment-aspx-block (pos)
+  (let (beg end)
+    (setq beg (web-mode-block-beginning-position pos)
+          end (web-mode-block-end-position pos))
+    (web-mode-insert-text-at-pos "#" end)
+    (web-mode-insert-text-at-pos "#" (1+ beg))))
+
+(defun web-mode-comment-jsp-block (pos)
+  (let (beg end)
+    (setq beg (web-mode-block-beginning-position pos)
+          end (web-mode-block-end-position pos))
+    (web-mode-insert-text-at-pos "--" (+ beg 2))))
+
+(defun web-mode-comment-go-block (pos)
+  (let (beg end)
+    (setq beg (web-mode-block-beginning-position pos)
+          end (web-mode-block-end-position pos))
+    (web-mode-insert-text-at-pos "/" (+ beg 2))))
+
+(defun web-mode-comment-php-block (pos)
   (let (beg end)
     (setq beg (web-mode-block-beginning-position pos)
           end (web-mode-block-end-position pos))
-    (web-mode-insert-text-at-pos "#" (+ beg 2))
+    (web-mode-insert-text-at-pos "*/" (- end 1))
+    (web-mode-insert-text-at-pos "/*" (+ beg (if (web-mode-looking-at "<\\?php" beg) 5 3)))))
+
+(defun web-mode-comment-boundaries (&optional pos)
+  (interactive)
+  (unless pos (setq pos (point)))
+  (let ((beg pos) (end pos) prop)
+    (save-excursion
+      (goto-char pos)
+      (setq prop
+            (cond
+             ((eq (get-text-property pos 'block-token) 'comment) 'block-token)
+             ((eq (get-text-property pos 'tag-type) 'comment) 'tag-type)
+             ((eq (get-text-property pos 'part-token) 'comment) 'part-token)
+             (t nil)
+             ))
+      (if (null prop)
+          (setq beg nil
+                end nil)
+        (when (and (not (bobp))
+                   (eq (get-text-property pos prop) (get-text-property (1- pos) prop)))
+          (setq beg (or (previous-single-property-change pos prop) (point-min))))
+        (when (and (not (eobp))
+                   (eq (get-text-property pos prop) (get-text-property (1+ pos) prop)))
+          (setq end (or (next-single-property-change pos prop) (point-max)))))
+      (when (and beg (string= (buffer-substring-no-properties beg (+ beg 2)) "//"))
+        (goto-char end)
+        (while (and (looking-at-p "\n[ ]*//")
+                    (not (eobp)))
+          (search-forward "//")
+          (backward-char 2)
+          ;;(message "%S" (point))
+          (setq end (next-single-property-change (point) prop))
+          (goto-char end)
+          ;;(message "%S" (point))
+          ) ;while
+        ) ;when
+      (when end (setq end (1- end)))
+      ) ;save-excursion
+    ;;(message "beg=%S end=%S" beg end)
+    (if (and beg end) (cons beg end) nil)
     ))
 
+(defun web-mode-uncomment (pos)
+  (let ((beg pos) (end pos) (sub2 "") comment boundaries)
+    (save-excursion
+      (cond
+       ((and (get-text-property pos 'block-side)
+             (intern-soft (concat "web-mode-uncomment-" web-mode-engine "-block")))
+        (funcall (intern (concat "web-mode-uncomment-" web-mode-engine "-block")) pos))
+       ((and (setq boundaries (web-mode-comment-boundaries pos))
+             (setq beg (car boundaries))
+             (setq end (1+ (cdr boundaries)))
+             (> (- end beg) 4))
+        ;;(message "beg(%S) end(%S)" beg end)
+        (setq comment (buffer-substring-no-properties beg end))
+        (setq sub2 (substring comment 0 2))
+        (cond
+         ((member sub2 '("<!" "<%"))
+          (setq comment (replace-regexp-in-string "\\(^<[!%]--[ ]?\\|[ ]?--[%]?>$\\)" "" comment)))
+         ((string= sub2 "{#")
+          (setq comment (replace-regexp-in-string "\\(^{#[ ]?\\|[ ]?#}$\\)" "" comment)))
+         ((string= sub2 "{/") ;jsx comments
+          (setq comment (replace-regexp-in-string "\\(^{/\\*[ ]?\\|[ ]?\\*/}$\\)" "" comment)))
+         ((string= sub2 "/*")
+          (setq comment (replace-regexp-in-string "\\(^/\\*[ ]?\\|[ ]?\\*/$\\|^[ \t]*\\*\\)" "" comment)))
+         ((string= sub2 "//")
+          (setq comment (replace-regexp-in-string "\\(//\\)" "" comment)))
+         ) ;cond
+        (delete-region beg end)
+        (web-mode-insert-and-indent comment)
+        (goto-char beg)
+        )
+       ) ;cond
+      (indent-according-to-mode)
+      )))
+
+(defun web-mode-uncomment-erb-block (pos)
+  (let (beg end)
+    (setq beg (web-mode-block-beginning-position pos)
+          end (web-mode-block-end-position pos))
+    (if (string-match-p "<[%[:alpha:]]" (buffer-substring-no-properties (+ beg 2) (- end 2)))
+        (progn
+          (web-mode-remove-text-at-pos 2 (1- end))
+          (web-mode-remove-text-at-pos 3 beg))
+      (web-mode-remove-text-at-pos 1 (+ beg 2))
+      ) ;if
+    )
+  )
+
+(defun web-mode-uncomment-ejs-block (pos)
+  (let (beg end)
+    (setq beg (web-mode-block-beginning-position pos)
+          end (web-mode-block-end-position pos))
+    (web-mode-remove-text-at-pos 1 (+ beg 2))))
+
 (defun web-mode-uncomment-django-block (pos)
-  "Uncomment a django block."
   (let (beg end)
     (setq beg (web-mode-block-beginning-position pos)
           end (web-mode-block-end-position pos))
     (web-mode-remove-text-at-pos 2 (1- end))
-    (web-mode-remove-text-at-pos 2 beg)
-    ))
-
-(defun web-mode-comment-django-block (pos)
-  "Turn a django block into a comment block."
-  (let (beg end)
-    (setq beg (web-mode-block-beginning-position pos)
-          end (web-mode-block-end-position pos))
-    (web-mode-insert-text-at-pos "#" end)
-    (web-mode-insert-text-at-pos "#" (1+ beg))
-    ))
-
-(defun web-mode-uncomment-dust-block (pos)
-  "Uncomment a dust block."
+    (web-mode-remove-text-at-pos 2 beg)))
+
+(defun web-mode-uncomment-ctemplate-block (pos)
   (let (beg end)
     (setq beg (web-mode-block-beginning-position pos)
           end (web-mode-block-end-position pos))
-    (web-mode-remove-text-at-pos 1 (1- end))
-    (web-mode-remove-text-at-pos 1 (1+ beg))
-    ))
-
-(defun web-mode-comment-dust-block (pos)
-  "Turn a dust block into a comment block."
-  (let (beg end)
-    (setq beg (web-mode-block-beginning-position pos)
-          end (web-mode-block-end-position pos))
-    (web-mode-insert-text-at-pos "!" end)
-    (web-mode-insert-text-at-pos "!" (1+ beg))
-    ))
-
-(defun web-mode-comment-aspx-block (pos)
-  "Turn a aspx block into a comment block."
-  (let (beg end)
-    (setq beg (web-mode-block-beginning-position pos)
-          end (web-mode-block-end-position pos))
-    (web-mode-insert-text-at-pos "#" end)
-    (web-mode-insert-text-at-pos "#" (1+ beg))
-    ))
-
-(defun web-mode-uncomment-aspx-block (pos)
-  "Uncomment a aspx block."
+    (web-mode-remove-text-at-pos 5 (- end 4))
+    (web-mode-remove-text-at-pos 5 beg)))
+
+(defun web-mode-uncomment-dust-block (pos)
   (let (beg end)
     (setq beg (web-mode-block-beginning-position pos)
           end (web-mode-block-end-position pos))
     (web-mode-remove-text-at-pos 1 (1- end))
-    (web-mode-remove-text-at-pos 1 (1+ beg))
-    ))
-
-(defun web-mode-uncomment-jsp-block (pos)
-  "Uncomment a jsp block."
-  (let (beg end)
-    (setq beg (web-mode-block-beginning-position pos)
-          end (web-mode-block-end-position pos))
-    (web-mode-remove-text-at-pos 2 (+ beg 2))
-    ))
-
-(defun web-mode-comment-jsp-block (pos)
-  "Turn a jsp block into a comment block."
-  (let (beg end)
-    (setq beg (web-mode-block-beginning-position pos)
-          end (web-mode-block-end-position pos))
-    (web-mode-insert-text-at-pos "--" (+ beg 2))
-    ))
-
-(defun web-mode-uncomment-go-block (pos)
-  "Uncomment a go block."
-  (let (beg end)
-    (setq beg (web-mode-block-beginning-position pos)
-          end (web-mode-block-end-position pos))
-    (web-mode-remove-text-at-pos 1 (+ beg 2))
-    ))
-
-(defun web-mode-comment-go-block (pos)
-  "Turn a go block into a comment block."
-  (let (beg end)
-    (setq beg (web-mode-block-beginning-position pos)
-          end (web-mode-block-end-position pos))
-    (web-mode-insert-text-at-pos "/" (+ beg 2))
-    ))
-
-(defun web-mode-comment-php-block (pos)
-  "Turn a php block into a comment block."
+    (web-mode-remove-text-at-pos 1 (1+ beg))))
+
+(defun web-mode-uncomment-aspx-block (pos)
   (let (beg end)
     (setq beg (web-mode-block-beginning-position pos)
           end (web-mode-block-end-position pos))
-    (web-mode-insert-text-at-pos "*/" (- end 1))
-    (web-mode-insert-text-at-pos "/*" (+ beg (if (web-mode-looking-at-pos "<\\?php" beg) 5 3)))
-    ))
-
-(defun web-mode-uncomment (&optional pos)
-  "Uncomment line(s) at point."
-  (interactive)
-  (unless pos (setq pos (point)))
-  (let ((beg pos)
-        (end pos)
-        (sub2 "")
-        comment prop)
-
-    (cond
-
-     ((and (get-text-property pos 'block-side)
-           (string= web-mode-engine "django"))
-        (web-mode-uncomment-django-block pos)
-        )
-
-     ((and (get-text-property pos 'block-side)
-           (string= web-mode-engine "dust"))
-        (web-mode-uncomment-dust-block pos)
-        )
-
-     ((and (get-text-property pos 'block-side)
-           (string= web-mode-engine "erb"))
-        (web-mode-uncomment-erb-block pos)
-        )
-
-     ((and (get-text-property pos 'block-side)
-           (string= web-mode-engine "aspx"))
-      (web-mode-uncomment-aspx-block pos)
-      )
-
-     ((and (get-text-property pos 'block-side)
-           (string= web-mode-engine "jsp"))
-      (web-mode-uncomment-jsp-block pos)
-      )
-
-     ((and (get-text-property pos 'block-side)
-           (string= web-mode-engine "go"))
-      (web-mode-uncomment-go-block pos)
-      )
-
-     (t
-
-      (cond
-       ((eq (get-text-property pos 'block-token) 'comment)
-        (setq prop 'block-token))
-       ((eq (get-text-property pos 'tag-type) 'comment)
-        (setq prop 'tag-type))
-       ((eq (get-text-property pos 'part-token) 'comment)
-        (setq prop 'part-token))
-       )
-
-      (if (and (not (bobp))
-               (eq (get-text-property pos prop) (get-text-property (- pos 1) prop)))
-          (setq beg (or (previous-single-property-change pos prop)
-                        (point-min))))
-
-      (if (and (not (eobp))
-               (eq (get-text-property pos prop) (get-text-property (+ pos 1) prop)))
-          (setq end (or (next-single-property-change pos prop)
-                        (point-max))))
-
-      ;;    (message "beg(%d) end(%d)" beg end)
-
-      (when (> (- end beg) 4)
-
-        (setq comment (buffer-substring-no-properties beg end))
-        ;;    (message "before[%s]" comment)
-
-        (setq sub2 (substring comment 0 2))
-
-        (cond
-
-         ((member sub2 '("<!" "<%"))
-          (setq comment (replace-regexp-in-string "\\(^<[!%]--[ ]?\\|[ ]?--[%]?>$\\)" "" comment)))
-
-         ((string= sub2 "{#")
-          (setq comment (replace-regexp-in-string "\\(^{#[ ]?\\|[ ]?#}$\\)" "" comment)))
-
-         ((string= sub2 "/*")
-          (setq comment (replace-regexp-in-string "\\(^/\\*[ ]?\\|[ ]?\\*/$\\)" "" comment)))
-
-         ((string= sub2 "//")
-          (setq comment (replace-regexp-in-string "\\(^//\\)" "" comment)))
-
-         )
-
-        ;;    (message "after[%s]" comment)
-
-        (delete-region beg end)
-        (web-mode-insert-and-indent comment)
-        (goto-char beg)
-;;        (back-to-indentation)
-
-        ) ;when
-
-      ))
-    (indent-for-tab-command)
-    ))
+    (web-mode-remove-text-at-pos 1 (1- end))
+    (web-mode-remove-text-at-pos 1 (1+ beg))))
+
+(defun web-mode-uncomment-jsp-block (pos)
+  (let (beg end)
+    (setq beg (web-mode-block-beginning-position pos)
+          end (web-mode-block-end-position pos))
+    (web-mode-remove-text-at-pos 2 (+ beg 2))))
+
+(defun web-mode-uncomment-go-block (pos)
+  (let (beg end)
+    (setq beg (web-mode-block-beginning-position pos)
+          end (web-mode-block-end-position pos))
+    (web-mode-remove-text-at-pos 1 (+ beg 2))))
 
 (defun web-mode-snippet-names ()
-  "Return the snippet names."
-  (interactive)
-  (let (codes snippet)
+  (let (codes)
     (dolist (snippet web-mode-snippets)
       (add-to-list 'codes (car snippet) t))
     codes))
 
 (defun web-mode-snippet-insert (code)
-  "Insert snippet."
+  "Insert a snippet."
   (interactive
-   (list (completing-read
-          "Snippet: " (web-mode-snippet-names))))
+   (list (completing-read "Snippet: " (web-mode-snippet-names))))
   (let (beg
         (continue t)
         (counter 0)
@@ -6941,9 +9007,8 @@
         (l (length web-mode-snippets))
         pos)
     (when mark-active
-      (setq sel (web-mode-trim
-                 (buffer-substring-no-properties
-                  (region-beginning) (region-end))))
+      (setq sel (web-mode-trim (buffer-substring-no-properties
+                                (region-beginning) (region-end))))
       (delete-region (region-beginning) (region-end)))
     (while (and continue (< counter l))
       (setq snippet (nth counter web-mode-snippets))
@@ -6953,31 +9018,69 @@
     (when snippet
       (setq snippet (cdr snippet))
       (setq beg (point-at-bol))
-      (insert (car snippet))
-;;      (message "insert[1] (%S)" (point))
-      (setq pos (point))
+      (insert snippet)
+      (setq pos (point)
+            end (point))
+      (when (string-match-p "|" snippet)
+        (search-backward "|")
+        (delete-char 1)
+        (setq pos (point)
+              end (1- end)))
       (when sel
         (insert sel)
-        (setq pos (point)))
-      (if (cdr snippet) (insert (cdr snippet)))
-;;      (message "insert[2] (%S)" (point))
+        (setq pos (point)
+              end (+ end (length sel))))
+      (goto-char end)
       (setq end (point-at-eol))
       (unless sel (goto-char pos))
       (indent-region beg end))
-;;      (message "indent : beg(%S) end(%S)" beg end)
     ))
 
+(defun web-mode-looking-at (regexp pos)
+  (save-excursion
+    (goto-char pos)
+    (looking-at regexp)))
+
+(defun web-mode-looking-at-p (regexp pos)
+  (save-excursion
+    (goto-char pos)
+    (looking-at-p regexp)))
+
+(defun web-mode-looking-back (regexp pos &optional limit greedy)
+  (save-excursion
+    (goto-char pos)
+    (if limit
+        (looking-back regexp limit greedy)
+      (looking-back regexp))))
+
+(defun web-mode-insert-text-at-pos (text pos)
+  (let ((mem web-mode-enable-auto-pairing))
+    (setq web-mode-enable-auto-pairing nil)
+    (save-excursion
+      (goto-char pos)
+      (insert text)
+      (setq web-mode-enable-auto-pairing mem)
+      )))
+
+(defun web-mode-remove-text-at-pos (n &optional pos)
+  (unless pos (setq pos (point)))
+  (delete-region pos (+ pos n)))
+
 (defun web-mode-insert-and-indent (text)
-  "Insert and indent text."
-  (interactive)
   (let (beg end)
     (setq beg (point-at-bol))
     (insert text)
     (setq end (point-at-eol))
-    (indent-region beg end)))
-
-(defun web-mode-tag-match (&optional pos)
-  "Match tag."
+    (indent-region beg end)
+    ))
+
+(defun web-mode-indentation-at-pos (pos)
+  (save-excursion
+    (goto-char pos)
+    (current-indentation)))
+
+(defun web-mode-navigate (&optional pos)
+  "Move point to the matching opening/closing tag/block."
   (interactive)
   (unless pos (setq pos (point)))
   (let (init)
@@ -6987,66 +9090,104 @@
       (back-to-indentation))
     (setq pos (point))
     (cond
-     ((web-mode-is-comment-or-string)
-      (goto-char init))
      ((and (get-text-property pos 'block-side)
            (web-mode-block-beginning)
-           (looking-at-p web-mode-active-block-regexp))
-      (funcall web-mode-engine-control-matcher))
+           (web-mode-block-controls-get (point)))
+      (web-mode-block-match))
      ((member (get-text-property pos 'tag-type) '(start end))
       (web-mode-tag-beginning)
-      (web-mode-html-tag-match))
+      (web-mode-tag-match))
      (t
       (goto-char init))
      )
     ))
 
 (defun web-mode-block-match (&optional pos)
-  "Block match"
   (unless pos (setq pos (point)))
-  (let (init)
-    (setq init pos)
+  (let (pos-ori controls control (counter 1) type (continue t) pair)
+    (setq pos-ori pos)
     (goto-char pos)
-    (if (and (get-text-property pos 'block-side)
-             (web-mode-block-beginning)
-             web-mode-active-block-regexp
-;;             (progn (message "web-mode-active-block-regexp=%S" web-mode-active-block-regexp) t)
-             (looking-at-p web-mode-active-block-regexp)
-             web-mode-engine-control-matcher)
-        (progn
-          (funcall web-mode-engine-control-matcher)
-          (point))
-      (goto-char init)
-      nil)))
-
-(defun web-mode-html-tag-match (&optional pos)
-  "Fetch HTML block."
+    (setq controls (web-mode-block-controls-get pos))
+    ;;(message "controls=%S" controls)
+    (cond
+     (controls
+      (setq pair (car controls))
+      (setq control (cdr pair))
+      (setq type (car pair))
+      (when (eq type 'inside) (setq type 'close))
+      (while continue
+        (cond
+         ((and (> pos-ori 1) (bobp))
+          (setq continue nil))
+         ((or (and (eq type 'open) (not (web-mode-block-next)))
+              (and (eq type 'close) (not (web-mode-block-previous))))
+          (setq continue nil)
+          )
+         ((null (setq controls (web-mode-block-controls-get (point))))
+          )
+         (t
+          ;;TODO : est il nécessaire de faire un reverse sur controls si on doit matcher backward
+          (dolist (pair controls)
+            (cond
+             ((not (string= (cdr pair) control))
+              )
+             ((eq (car pair) 'inside)
+              )
+             ((eq (car pair) type)
+              (setq counter (1+ counter)))
+             (t
+              (setq counter (1- counter)))
+             )
+            ) ;dolist
+          (when (= counter 0)
+            (setq continue nil))
+          ) ;t
+         ) ;cond
+        ) ;while
+      (if (= counter 0) (point) nil)
+      ) ;controls
+     (t
+      (goto-char pos-ori)
+      nil
+      ) ;controls = nul
+     ) ;conf
+    ))
+
+(defun web-mode-tag-match (&optional pos)
+  "Move point to the matching opening/closing tag."
+  (interactive)
   (unless pos (setq pos (point)))
   (let (regexp)
     (setq regexp (concat "</?" (get-text-property pos 'tag-name)))
+    (when (member (get-text-property pos 'tag-type) '(start end))
+      (web-mode-tag-beginning)
+      (setq pos (point)))
     (if (eq (get-text-property pos 'tag-type) 'end)
-        (web-mode-fetch-html-opening-tag regexp pos)
-      (web-mode-fetch-html-closing-tag regexp pos))
+        (web-mode-tag-fetch-opening regexp pos)
+      (web-mode-tag-fetch-closing regexp pos))
     t))
 
-(defun web-mode-fetch-html-opening-tag (regexp pos)
-  "Fetch opening HTML block."
-  (let ((counter 1) (n 0))
+(defun web-mode-tag-fetch-opening (regexp pos)
+  (let ((counter 1) (n 0) (type nil))
     (goto-char pos)
     (while (and (> counter 0) (re-search-backward regexp nil t))
-      (when (get-text-property (point) 'tag-beg)
+      (when (and (get-text-property (point) 'tag-beg)
+                 (member (get-text-property (point) 'tag-type) '(start end)))
         (setq n (1+ n))
-        (if (eq (get-text-property (point) 'tag-type) 'end)
-            (setq counter (1+ counter))
-          (setq counter (1- counter))))
+        (cond
+         ((eq (get-text-property (point) 'tag-type) 'end)
+          (setq counter (1+ counter)))
+         (t
+          (setq counter (1- counter))
+          )
+         )
+        )
       )
     (if (= n 0) (goto-char pos))
     ))
 
-(defun web-mode-fetch-html-closing-tag (regexp pos)
-  "Fetch closing HTML closing block."
+(defun web-mode-tag-fetch-closing (regexp pos)
   (let ((counter 1) (n 0))
-;;    (message "regexp=%S pos=%S" regexp pos)
     (goto-char pos)
     (web-mode-tag-end)
     (while (and (> counter 0) (re-search-forward regexp nil t))
@@ -7056,1131 +9197,349 @@
             (setq counter (1- counter))
           (setq counter (1+ counter))))
       )
-;;    (message "n=%S" n)
     (if (> n 0)
         (web-mode-tag-beginning)
       (goto-char pos))
     ))
 
-(defun web-mode-match-underscore-block ()
-  "Fetch underscore block."
-  (let (open)
-    (cond
-
-     ((looking-at-p "<%[ ]*}")
-      (search-forward "}")
-      (backward-char)
-      (setq open (web-mode-opening-paren-position))
-      (when open
-        (goto-char open)
-        (web-mode-block-beginning)
-        )
-      )
-
-     ((web-mode-block-ends-with "{[ ]*%>")
-      (web-mode-block-end)
-      (search-backward "{")
-      (setq open (web-mode-closing-paren-position))
-      (when open
-        (goto-char open)
-        (web-mode-block-beginning)
-        )
-      )
-
-     ) ;cond
-
-    ))
-
-(defun web-mode-match-php-block ()
-  "Fetch PHP block."
-  (let (regexp match)
-    (cond
-
-     ((looking-at-p "<\\?\\(php\\)?[ ]*}")
-      (let (open)
-        (search-forward "}")
-        (backward-char)
-        (setq open (web-mode-opening-paren-position))
-        (when open
-          (goto-char open)
-          (web-mode-block-beginning)
-          )
-        ))
-
-     ((looking-at-p "<\\?php.+{[ ]*\\?>")
-      (let (close)
-        (web-mode-block-end)
-        (search-backward "{")
-        (setq close (web-mode-closing-paren-position))
-        (when close
-          (goto-char close)
-          (web-mode-block-beginning)
-          )
-        ))
-
-     (t
-      (looking-at web-mode-active-block-regexp)
-      (setq match (match-string-no-properties 3))
-      (setq regexp (concat "<\\?\\(php[ ]+\\|[ ]*\\)?\\(end\\)?\\("
-                         (if (member match '("else" "elseif")) "if" match)
-                         "\\)"))
-      (if (or (string= "end" (match-string-no-properties 2))
-              (member match '("else" "elseif")))
-          (web-mode-fetch-opening-php-block regexp)
-        (web-mode-fetch-closing-php-block regexp))
-      ) ;t
-
-     ) ;cond
-    t))
-
-(defun web-mode-fetch-opening-php-block (regexp)
-  "Fetch PHP opening block."
-  (let ((counter 1))
-    (while (and (> counter 0) (re-search-backward regexp nil t))
-      (if (string= "end" (match-string-no-properties 2))
-          (setq counter (1+ counter))
-        (setq counter (1- counter)))
-      )
-    ))
-
-(defun web-mode-fetch-closing-php-block (regexp)
-  "Fetch PHP closing block."
-  (let ((counter 1))
-    (web-mode-block-end)
-    (while (and (> counter 0) (re-search-forward regexp nil t))
-      (if (string= "end" (match-string-no-properties 2))
-          (setq counter (1- counter))
-        (setq counter (1+ counter))
-        ) ;if
-      ) ;while
-    (web-mode-block-beginning)
-    ))
-
-(defun web-mode-match-erb-block ()
-  "Fetch ERB block."
-  (let (regexp chunk)
-    (setq chunk (buffer-substring-no-properties (+ (point) 3)
-                                                (- (web-mode-block-end-position) 2)))
-    (setq regexp web-mode-active-block-regexp)
-    (if (string-match-p "else\\|end" chunk)
-        (web-mode-fetch-opening-erb-block regexp)
-      (web-mode-fetch-closing-erb-block regexp))
-    t))
-
-(defun web-mode-fetch-opening-erb-block (regexp)
-  "Fetch erb opening block."
-  (let ((counter 1) match)
-    (while (and (> counter 0) (web-mode-rsb regexp nil t))
-      (setq match (match-string-no-properties 1))
-      (cond
-       ((string= "else" match)
-        )
-       ((not (string= "end" match))
-        (setq counter (1- counter)))
-       (t
-        (setq counter (1+ counter)))
-       )
-      )
-    ))
-
-(defun web-mode-fetch-closing-erb-block (regexp)
-  "Fetch erb closing block."
-  (let ((counter 1) match)
-    (web-mode-block-end)
-    (while (and (> counter 0) (web-mode-rsf regexp nil t))
-      (setq match (match-string-no-properties 1))
-      (cond
-       ((string= "else" match)
-        )
-       ((not (string= "end" match))
-        (setq counter (1+ counter)))
-       (t
-        (setq counter (1- counter)))
-       )
-      )
-    (web-mode-block-beginning)
-    ))
-
-(defun web-mode-match-web2py-block ()
-  "Fetch web2py block."
-  (let (regexp match)
-    (looking-at web-mode-active-block-regexp)
-    (setq match (match-string-no-properties 1))
-    ;;    (message "match=%S" match)
-    (cond
-     ((member match '("def" "return"))
-      (setq regexp "{{[ ]*\\(def\\|return\\)"))
-     ((member match '("block" "end"))
-      (setq regexp "{{[ ]*\\(block\\|end\\)"))
-     (t
-      (setq regexp web-mode-active-block-regexp))
-     )
-    (if (member match '("elif" "else" "except" "finally" "pass" "end" "return"))
-        (web-mode-fetch-opening-web2py-block regexp)
-      (web-mode-fetch-closing-web2py-block regexp))
-    t))
-
-(defun web-mode-fetch-opening-web2py-block (regexp)
-  "Fetch web2py opening block."
-  (let ((counter 1) match)
-    (while (and (> counter 0) (web-mode-rsb regexp nil t))
-      (setq match (match-string-no-properties 1))
-      (cond
-       ((member match '("else" "elif"))
-        )
-       ((not (member match '("pass" "end" "return")))
-        (setq counter (1- counter)))
-       (t
-        (setq counter (1+ counter)))
-       )
-      )
-    ))
-
-(defun web-mode-fetch-closing-web2py-block (regexp)
-  "Fetch web2py closing block."
-  (let ((counter 1) match)
-    (web-mode-block-end)
-    (while (and (> counter 0) (web-mode-rsf regexp nil t))
-      (setq match (match-string-no-properties 1))
-      (cond
-       ((member match '("else" "elif"))
-        )
-       ((not (member match '("pass" "end" "return")))
-        (setq counter (1+ counter)))
-       (t
-        (setq counter (1- counter)))
-       )
-      )
-    (web-mode-block-beginning)
-    ))
-
-(defun web-mode-match-template-toolkit-block ()
-  "Fetch TEMPLATE-TOOLKIT block."
-  (let (regexp chunk)
-    (setq chunk (buffer-substring-no-properties (+ (point) 3)
-                                                (- (web-mode-block-end-position) 2)))
-    (setq regexp web-mode-active-block-regexp)
-    (if (string-match-p "else\\|end\\|END" chunk)
-        (web-mode-fetch-opening-template-toolkit-block regexp)
-      (web-mode-fetch-closing-template-toolkit-block regexp))
-    t))
-
-(defun web-mode-fetch-opening-template-toolkit-block (regexp)
-  "Fetch template-toolkit opening block."
-  (let ((counter 1) match)
-    (while (and (> counter 0) (web-mode-rsb regexp nil t))
-      (setq match (match-string-no-properties 1))
-      (cond
-       ((member match '("else" "ELSE" "elsif" "ELSIF"))
-        )
-       ((not (member match '("end" "END")))
-        (setq counter (1- counter)))
-       (t
-        (setq counter (1+ counter)))
-       )
-      )
-    ))
-
-(defun web-mode-fetch-closing-template-toolkit-block (regexp)
-  "Fetch template-toolkit closing block."
-  (let ((counter 1) match)
-    (web-mode-block-end)
-    (while (and (> counter 0) (web-mode-rsf regexp nil t))
-      (setq match (match-string-no-properties 1))
-      (cond
-       ((member match '("else" "ELSE" "elsif" "ELSIF"))
-        )
-       ((not (member match '("end" "END")))
-        (setq counter (1+ counter)))
-       (t
-        (setq counter (1- counter)))
-       )
-      )
-    (web-mode-block-beginning)
-    ))
-
-
-(defun web-mode-match-blade-block ()
-  "Fetch blade block."
-  (let (beg end match regexp)
-    (looking-at web-mode-active-block-regexp)
-    (setq match (match-string-no-properties 2))
-    (setq regexp (cond
-                  ((member match '("else" "elseif")) "@\\(end\\)?\\(if\\)")
-                  ((string= match "stop") "@\\(section\\|stop\\)")
-                  ((string= match "section") "@\\(endsection\\|stop\\|section\\)")
-                  (t (concat "@\\(end\\)?\\(" match "\\)"))))
-    (if (or (string= "end" (match-string-no-properties 1))
-            (member match '("else" "elseif" "stop")))
-        (web-mode-fetch-opening-blade-block regexp)
-      (web-mode-fetch-closing-blade-block regexp))
-    t))
-
-(defun web-mode-fetch-opening-blade-block (regexp)
-  "Fetch blade opening block."
-  (let ((counter 1))
-    (while (and (> counter 0) (web-mode-rsb regexp nil t))
-      (if (member (match-string-no-properties 1) '("end" "endsection" "stop"))
-          (setq counter (1+ counter))
-        (setq counter (1- counter)))
-      )
-    ))
-
-(defun web-mode-fetch-closing-blade-block (regexp)
-  "Fetch blade closing block."
-  (let ((counter 1))
-    (web-mode-block-end)
-    (while (and (> counter 0) (web-mode-rsf regexp nil t))
-      (if (member (match-string-no-properties 1) '("end" "endsection" "stop"))
-          (setq counter (1- counter))
-        (setq counter (1+ counter)))
-      )
-    (goto-char (match-beginning 0))
-    ))
-
-(defun web-mode-match-django-block ()
-  "Fetch django block."
-  (let (match regexp)
-    (looking-at web-mode-active-block-regexp)
-    (setq match (match-string-no-properties 2))
-    (setq regexp (concat "{%[-]?[ ]+\\(end\\)?\\("
-                         (cond
-                          ((member match '("else" "elseif" "elsif" "elif")) "if")
-                          ((member match '("empty")) "for")
-                          (t match))
-                         "\\)"))
-;;    (message "%S" regexp)
-    (if (or (string= "end" (match-string-no-properties 1))
-            (member match '("else" "elseif" "elsif" "elif" "empty")))
-        (web-mode-fetch-opening-django-block regexp)
-      (web-mode-fetch-closing-django-block regexp))
-    t))
-
-(defun web-mode-fetch-opening-django-block (regexp)
-  "Fetch django opening block."
-  (let ((counter 1))
-    (while (and (> counter 0) (web-mode-rsb regexp nil t))
-      (if (string= "end" (match-string-no-properties 1))
-          (setq counter (1+ counter))
-        (setq counter (1- counter)))
-      )
-    ))
-
-(defun web-mode-fetch-closing-django-block (regexp)
-  "Fetch django closing block."
-  (let ((counter 1))
-    (web-mode-block-end)
-    (while (and (> counter 0) (web-mode-rsf regexp nil t))
-      (if (string= "end" (match-string-no-properties 1))
-          (setq counter (1- counter))
-        (setq counter (1+ counter)))
-      )
-    (web-mode-block-beginning)
-    ))
-
-(defun web-mode-match-smarty-block ()
-  "Fetch smarty block."
-  (let (match regexp)
-    (looking-at web-mode-active-block-regexp)
-    (setq match (match-string-no-properties 1))
-    (setq regexp (concat (web-mode-engine-delimiter-open web-mode-engine "{") "/?" (if (string= match "else") "if" match)))
-    (if (or (eq ?\/ (aref (match-string-no-properties 0) (length (web-mode-engine-delimiter-open web-mode-engine "{"))))
-            (string= match "else"))
-        (web-mode-fetch-opening-smarty-block regexp)
-      (web-mode-fetch-closing-smarty-block regexp))
-    t))
-
-(defun web-mode-fetch-opening-smarty-block (regexp)
-  "Fetch smarty opening block."
-  (let ((counter 1))
-    (while (and (> counter 0) (web-mode-rsb regexp nil t))
-      (if (eq ?\/ (aref (match-string-no-properties 0) (length (web-mode-engine-delimiter-open web-mode-engine "{"))))
-          (setq counter (1+ counter))
-        (setq counter (1- counter)))
-      )
-    ))
-
-(defun web-mode-fetch-closing-smarty-block (regexp)
-  "Fetch smarty closing block."
-  (let ((counter 1))
-    (web-mode-block-end)
-    (while (and (> counter 0) (web-mode-rsf regexp nil t))
-      (if (eq ?\/ (aref (match-string-no-properties 0) (length (web-mode-engine-delimiter-open web-mode-engine "{"))))
-          (setq counter (1- counter))
-        (setq counter (1+ counter)))
-      )
-    (web-mode-block-beginning)
-    ))
-
-(defun web-mode-match-mason-block ()
-  "Fetch mason block."
-  (let (match regexp)
-    (looking-at web-mode-active-block-regexp)
-
-    (setq match (match-string-no-properties 1))
-    (setq regexp (concat "</?%" match))
-    (if (eq ?\/ (aref (match-string-no-properties 0) 1))
-        (web-mode-fetch-opening-mason-block regexp)
-      (web-mode-fetch-closing-mason-block regexp))
-    t))
-
-(defun web-mode-fetch-opening-mason-block (regexp)
-  "Fetch mason opening block."
-  (let ((counter 1))
-    (while (and (> counter 0) (web-mode-rsb regexp nil t))
-      (if (eq ?\/ (aref (match-string-no-properties 0) 1))
-          (setq counter (1+ counter))
-        (setq counter (1- counter)))
-      )
-    ))
-
-(defun web-mode-fetch-closing-mason-block (regexp)
-  "Fetch mason closing block."
-  (let ((counter 1))
-    (web-mode-block-end)
-    (while (and (> counter 0) (web-mode-rsf regexp nil t))
-      (if (eq ?\/ (aref (match-string-no-properties 0) 1))
-          (setq counter (1- counter))
-        (setq counter (1+ counter)))
-      )
-    (web-mode-block-beginning)
-    ))
-
-(defun web-mode-match-dust-block ()
-  "Fetch dust block."
-  (let (match regexp (continue t))
-    (looking-at web-mode-active-block-regexp)
-    (cond
-     ((string= (match-string-no-properties 0) "{:else")
-      (while continue
-        (if (web-mode-block-previous)
-            (when (cdr (web-mode-is-active-block (point)))
-              (setq continue nil))
-          (setq continue nil)
-          )
-        ) ;while
-      )
-     (t
-      (setq match (match-string-no-properties 1))
-      (setq regexp (concat "{[#/:?@><+^]?" match))
-      (if (eq ?\/ (aref (match-string-no-properties 0) 1))
-          (web-mode-fetch-opening-dust-block regexp)
-        (web-mode-fetch-closing-dust-block regexp)))
-     ) ;cond
-    t))
-
-(defun web-mode-fetch-opening-dust-block (regexp)
-  "Fetch dust opening block."
-  (let ((counter 1))
-    (while (and (> counter 0) (web-mode-rsb regexp nil t))
-      (if (eq ?\/ (aref (match-string-no-properties 0) 1))
-          (setq counter (1+ counter))
-        (setq counter (1- counter)))
-      )
-    ))
-
-(defun web-mode-fetch-closing-dust-block (regexp)
-  "Fetch dust closing block."
-  (let ((counter 1))
-    (web-mode-block-end)
-    (while (and (> counter 0) (web-mode-rsf regexp nil t))
-      (if (eq ?\/ (aref (match-string-no-properties 0) 1))
-          (setq counter (1- counter))
-        (setq counter (1+ counter)))
-      )
-    (web-mode-block-beginning)
-    ))
-
-(defun web-mode-match-closure-block ()
-  "Fetch closure block."
-  (let (match regexp (continue t))
-    (looking-at web-mode-active-block-regexp)
-    (cond
-     ((member (match-string-no-properties 0) '("{else" "{elseif"))
-      (while continue
-        (if (web-mode-block-previous)
-            (when (looking-at-p "{if")
-              (setq continue nil))
-          (setq continue nil)
-          )
-        ) ;while
-      )
-     ((member (match-string-no-properties 0) '("{ifempty"))
-      (while continue
-        (if (web-mode-block-previous)
-            (when (looking-at-p "{foreach")
-              (setq continue nil))
-          (setq continue nil)
-          )
-        ) ;while
-      )
-     ((member (match-string-no-properties 0) '("{case" "{default"))
-      (while continue
-        (if (web-mode-block-previous)
-            (when (looking-at-p "{switch")
-              (setq continue nil))
-          (setq continue nil)
-          )
-        ) ;while
-      )
-     (t
-      (setq match (match-string-no-properties 1))
-      (setq regexp (concat "{/?" match))
-      (if (eq ?\/ (aref (match-string-no-properties 0) 1))
-          (web-mode-fetch-opening-closure-block regexp)
-        (web-mode-fetch-closing-closure-block regexp)))
-     ) ;cond
-    t))
-
-(defun web-mode-fetch-opening-closure-block (regexp)
-  "Fetch closure opening block."
-  (let ((counter 1))
-    (while (and (> counter 0) (web-mode-rsb regexp nil t))
-      (if (eq ?\/ (aref (match-string-no-properties 0) 1))
-          (setq counter (1+ counter))
-        (setq counter (1- counter)))
-      )
-    ))
-
-(defun web-mode-fetch-closing-closure-block (regexp)
-  "Fetch closure closing block."
-  (let ((counter 1))
-    (web-mode-block-end)
-    (while (and (> counter 0) (web-mode-rsf regexp nil t))
-      (if (eq ?\/ (aref (match-string-no-properties 0) 1))
-          (setq counter (1- counter))
-        (setq counter (1+ counter)))
-      )
-    (web-mode-block-beginning)
-    ))
-
-(defun web-mode-match-velocity-block ()
-  "Fetch velocity block."
-  (let (regexp match)
-    (looking-at web-mode-active-block-regexp)
-    (setq match (match-string-no-properties 1))
-    (setq regexp web-mode-active-block-regexp)
-    (if (member match '("else" "elseif" "end"))
-        (web-mode-fetch-opening-velocity-block regexp)
-      (web-mode-fetch-closing-velocity-block regexp))
-    t))
-
-(defun web-mode-fetch-opening-velocity-block (regexp)
-  "Fetch velocity opening block."
-  (let ((counter 1) match)
-    (while (and (> counter 0) (web-mode-rsb regexp nil t))
-      (setq match (match-string-no-properties 1))
-      (cond
-       ((string= "end" match)
-        (setq counter (1+ counter)))
-       ((string= "else" match)
-        )
-       (t
-        (setq counter (1- counter)))
-       )
-      )
-    ))
-
-(defun web-mode-fetch-closing-velocity-block (regexp)
-  "Fetch velocity closing block."
-  (let ((counter 1) match)
-    (web-mode-block-end)
-    (while (and (> counter 0) (web-mode-rsf regexp nil t))
-      (setq match (match-string-no-properties 1))
-      (cond
-       ((string= "end" match)
-        (setq counter (1- counter)))
-       ((string= "else" match)
-        )
-       (t
-        (setq counter (1+ counter)))
-       )
-      )
-    (goto-char (match-beginning 0))
-    ))
-
-(defun web-mode-match-ctemplate-block ()
-  "Fetch ctemplate block."
-  (let (regexp)
-    (looking-at web-mode-active-block-regexp)
-    (setq regexp (concat "{{[#^/]" (match-string-no-properties 1)))
-    (if (looking-at-p "{{/")
-        (web-mode-fetch-opening-ctemplate-block regexp)
-      (web-mode-fetch-closing-ctemplate-block regexp))
-    t))
-
-(defun web-mode-fetch-opening-ctemplate-block (regexp)
-  "Fetch ctemplate opening block."
-  (let ((counter 1))
-    (while (and (> counter 0) (web-mode-rsb regexp nil t))
-      (if (eq ?\/ (aref (match-string-no-properties 0) 2))
-          (setq counter (1+ counter))
-        (setq counter (1- counter)))
-      )
-    ))
-
-(defun web-mode-fetch-closing-ctemplate-block (regexp)
-  "Fetch ctemplate closing block."
-  (let ((counter 1))
-    (web-mode-block-end)
-    (while (and (> counter 0) (web-mode-rsf regexp nil t))
-      (if (eq ?\/ (aref (match-string-no-properties 0) 2))
-          (setq counter (1- counter))
-        (setq counter (1+ counter)))
-      )
-    (web-mode-block-beginning)
-    ))
-
-(defun web-mode-match-go-block ()
-  "Fetch go block."
-  (let (regexp match)
-    (looking-at web-mode-active-block-regexp)
-    (setq match (match-string-no-properties 1))
-    (setq regexp web-mode-active-block-regexp)
-    (if (member match '("end" "else"))
-        (web-mode-fetch-opening-go-block regexp)
-      (web-mode-fetch-closing-go-block regexp))
-    t))
-
-(defun web-mode-fetch-opening-go-block (regexp)
-  "Fetch go opening block."
-  (let ((counter 1) match)
-    (while (and (> counter 0) (web-mode-rsb regexp nil t))
-      (setq match (match-string-no-properties 1))
-      (cond
-       ((string= "end" match)
-        (setq counter (1+ counter)))
-       ((string= "else" match)
-        )
-       (t
-        (setq counter (1- counter)))
-       )
-      )
-    ))
-
-(defun web-mode-fetch-closing-go-block (regexp)
-  "Fetch go closing block."
-  (let ((counter 1) match)
-    (web-mode-block-end)
-    (while (and (> counter 0) (web-mode-rsf regexp nil t))
-      (setq match (match-string-no-properties 1))
-      (cond
-       ((string= "end" match)
-        (setq counter (1- counter))
-        )
-       ((string= "else" match)
-        )
-       (t
-        (setq counter (1+ counter))
-        )
-       )
-      )
-    (web-mode-block-beginning)
-    ))
-
-(defun web-mode-match-mako-block ()
-  "Fetch mako active block."
-  (let (regexp match)
-    (looking-at web-mode-active-block-regexp)
-;;    (message "regexp=%S" web-mode-active-block-regexp)
-;;    (message "%S %S" (match-string-no-properties 0) (match-string-no-properties 1))
-    (cond
-     ((match-string-no-properties 1)
-      (setq match (match-string-no-properties 1))
-      (setq regexp (concat "</?%" match))
-      (if (eq (aref (match-string-no-properties 0) 1) ?\/)
-          (web-mode-fetch-opening-mako-block regexp)
-        (web-mode-fetch-closing-mako-block regexp))
-      )
-     (t
-      (setq match (match-string-no-properties 3))
-      (setq regexp (if (member match '("elif" "else")) "if" match))
-      (setq regexp (concat "%[ ]+\\(end\\)?\\(" regexp "\\)"))
-;;      (message "regexp=%S" regexp)
-      (if (or (match-string-no-properties 2)
-              (member match '("elif" "else")))
-          (web-mode-fetch-opening-mako-block regexp)
-        (web-mode-fetch-closing-mako-block regexp))
-      )
-     ) ;cond
-    t))
-
-(defun web-mode-fetch-opening-mako-block (regexp)
-  "Fetch mako opening block."
-  (let ((counter 1) match)
-    (while (and (> counter 0) (web-mode-rsb regexp nil t))
-      (setq match (match-string-no-properties 0))
-      (cond
-       ((or (eq (aref match 1) ?\/)
-            (match-string-no-properties 1))
-        (setq counter (1+ counter)))
-       (t
-        (setq counter (1- counter)))
-       )
-      )
-    ))
-
-(defun web-mode-fetch-closing-mako-block (regexp)
-  "Fetch mako closing block."
-  (let ((counter 1) match)
-    (web-mode-block-end)
-    (while (and (> counter 0) (web-mode-rsf regexp nil t))
-      (setq match (match-string-no-properties 0))
-      (cond
-       ((or (eq (aref match 1) ?\/)
-            (match-string-no-properties 1))
-        (setq counter (1- counter)))
-       (t
-        (setq counter (1+ counter)))
-       ) ;cond
-      ) ;while
-    (backward-char)
-    (web-mode-block-beginning)
-    ))
-
-(defun web-mode-match-razor-block ()
-  "Fetch razor block."
-  (let (regexp pos)
-    (cond
-     ((looking-at-p "}")
-      (setq pos (web-mode-opening-paren-position))
-      (when pos
-        (goto-char pos))
-      (web-mode-block-beginning)
-      )
-     ((looking-at-p ".*{[ ]*$")
-      (end-of-line)
-      (search-backward "{")
-      (setq pos (web-mode-closing-paren-position))
-      (when pos
-        (goto-char pos))
-      )
-     )
-    t))
-
-(defun web-mode-match-jsp-block ()
-  "Fetch jsp block."
-  (let (regexp)
-    (cond
-     ((looking-at-p "<% }")
-      (let (open)
-        (search-forward "}")
-        (backward-char)
-        (setq open (web-mode-opening-paren-position))
-        (when open
-          (goto-char open)
-          (web-mode-block-beginning))
-        ))
-     (t
-      (looking-at web-mode-active-block-regexp)
-      (setq regexp (concat "<\\(/?" (match-string-no-properties 1) "\\)\\>"))
-      (if (eq ?\/ (aref (match-string-no-properties 0) 1))
-          (web-mode-fetch-opening-jsp-block regexp)
-        (web-mode-fetch-closing-jsp-block regexp)))
-     )
-    t))
-
-(defun web-mode-fetch-opening-jsp-block (regexp)
-  "Fetch jsp opening block."
-  (let ((counter 1))
-    (while (and (> counter 0) (web-mode-rsb regexp nil t))
-      (cond
-       ((eq ?\/ (aref (match-string-no-properties 1) 0))
-        (setq counter (1+ counter)))
-       (t
-        (setq counter (1- counter)))
-       )
-      )
-    ))
-
-(defun web-mode-fetch-closing-jsp-block (regexp)
-  "Fetch jsp closing block."
-  (let ((counter 1))
-    (web-mode-block-end)
-    (while (and (> counter 0) (web-mode-rsf regexp nil t))
-      (cond
-       ((eq ?\/ (aref (match-string-no-properties 1) 0))
-        (setq counter (1- counter)))
-       (t
-        (setq counter (1+ counter)))
-       )
-      )
-    (web-mode-block-beginning)
-    ))
-
-(defun web-mode-match-freemarker-block ()
-  "Fetch freemarker block."
-  (let (regexp match tag char)
-    (looking-at "[<[]/?\\([[:alpha:]]+:[[:alpha:]]+\\|[@#][[:alpha:]._]+\\)")
-    (setq match (match-string-no-properties 0)
-          tag (match-string-no-properties 1)
-          char (if (string= (substring (match-string-no-properties 0) 0 1) "<") "<" "\\["))
-
-    (cond
-     ((member tag '("#else" "#elseif"))
-      (setq match (concat char "/#if")
-            tag "#if")
-      )
-     ((string= tag "#break")
-      (setq match (concat char "/#case")
-            tag "#case"))
-     ) ;cond
-    (setq regexp (concat char "\\(/?" tag "\\)\\>"))
-;;    (message "tag=%S regexp=%S" tag regexp)
-    (if (eq ?\/ (aref match 1))
-        (web-mode-fetch-opening-freemarker-block regexp)
-      (web-mode-fetch-closing-freemarker-block regexp))
-    t))
-
-(defun web-mode-fetch-opening-freemarker-block (regexp)
-  "Fetch freemarker opening block."
-  (let ((counter 1))
-    (while (and (> counter 0) (web-mode-rsb regexp nil t))
-      (cond
-       ((eq ?\/ (aref (match-string-no-properties 1) 0))
-        (setq counter (1+ counter)))
-       (t
-        (setq counter (1- counter)))
-       )
-      )
-    ))
-
-(defun web-mode-fetch-closing-freemarker-block (regexp)
-  "Fetch freemarker closing block."
-  (let ((counter 1))
-    (web-mode-block-end)
-    (while (and (> counter 0) (web-mode-rsf regexp nil t))
-      (cond
-       ((eq ?\/ (aref (match-string-no-properties 1) 0))
-        (setq counter (1- counter)))
-       (t
-        (setq counter (1+ counter)))
-       )
-      )
-    (web-mode-block-beginning)
-    ))
+(defun web-mode-element-tag-name (&optional pos)
+  (unless pos (setq pos (point)))
+  (save-excursion
+    (goto-char pos)
+    (if (and (web-mode-tag-beginning)
+             (looking-at "</?\\([[:alpha:]][[:alnum:]:-]*\\)"))
+        (match-string-no-properties 1)
+      nil)))
 
 (defun web-mode-element-close ()
-  "Close HTML element."
+  "Close html element."
   (interactive)
   (let (jump epp ins tag)
-    (setq epp (web-mode-element-parent-position))
+
+    (if (and (eq (char-before) ?\>)
+             (web-mode-element-is-void (get-text-property (1- (point)) 'tag-name)))
+        (unless (eq (char-before (1- (point))) ?\/)
+          (backward-char)
+          (insert "/")
+          (forward-char))
+      (setq epp (web-mode-element-parent-position)))
+
+    ;;(message "epp=%S" epp)
     (when epp
       (setq tag (get-text-property epp 'tag-name))
-      (cond
+      (setq tag (web-mode-element-tag-name epp))
+      ;;(message "tag=%S %c" tag (char-before))
+      (cond
+       ((or (null tag) (web-mode-element-is-void tag))
+        (setq epp nil))
        ((looking-back "</")
         (setq ins tag))
        ((looking-back "<")
         (setq ins (concat "/" tag)))
        (t
-        (setq ins (concat "</" tag)))
-       )
-      (unless (looking-at-p "[ ]*>")
-        (setq ins (concat ins ">")))
-      (insert ins)
-      (save-excursion
-        (search-backward "<")
-        (setq jump (and (eq (char-before) ?\>)
-                        (string= (get-text-property (1- (point)) 'tag-name) tag)))
-        (if jump (setq jump (point)))
-;;        (setq jump (looking-back (concat "<" tag ">")))
-        ) ;save-excursion
-      (if jump (goto-char jump))
+        ;;auto-close-style = 2
+        ;;(message "%S %c" (point) (char-after))
+        (when (and (looking-at-p "[[:alpha:]]") (> (length tag) 4))
+          (dolist (elt '("div" "span" "strong" "pre" "li"))
+            (when (and (string-match-p (concat "^" elt) tag) (not (string= tag elt)))
+              (setq tag elt)
+              (put-text-property epp (point) 'tag-name tag))
+            )
+          ) ;when
+        (if (web-mode-element-is-void (get-text-property (point) 'tag-name))
+            (setq ins nil
+                  epp nil)
+          (setq ins (concat "</" tag)))
+        )
+       ) ;cond
+      (when ins
+        (unless (looking-at-p "[ ]*>")
+          (setq ins (concat ins ">")))
+        (insert ins)
+        (save-excursion
+          (search-backward "<")
+          (setq jump (and (eq (char-before) ?\>)
+                          (string= (get-text-property (1- (point)) 'tag-name) tag)))
+          (if jump (setq jump (point)))
+          ) ;save-excursion
+        (if jump (goto-char jump))
+        ) ;when not ins
       ) ;when epp
-    epp
-    ))
-
-(defun web-mode-tags-pos ()
-  (save-excursion
-    (let (start-beg start-end end-beg end-end (pos (point)))
-      (cond
-       ((eq (get-text-property pos 'tag-type) 'start)
-        (setq start-beg (web-mode-tag-beginning-position pos)
-              start-end (web-mode-tag-end-position pos))
-        (when (web-mode-html-tag-match)
-          (setq end-beg (point)
-                end-end (web-mode-tag-end-position (point))))
-;;        (message "%S %S %S %S" start-beg start-end end-beg end-end)
-        )
-       ((eq (get-text-property pos 'tag-type) 'end)
-        (setq end-beg (web-mode-tag-beginning-position pos)
-              end-end (web-mode-tag-end-position pos))
-        (when (web-mode-html-tag-match)
-          (setq start-beg (point)
-                start-end (web-mode-tag-end-position (point))))
-        )
-       )
-      (if (and start-beg end-beg)
-          (cons (cons start-beg (1+ start-end))
-                (cons end-beg (1+ end-end)))
-        nil)
-      )))
-
-(defun web-mode-make-tag-overlays ()
-  (unless web-mode-start-tag-overlay
-    (setq web-mode-start-tag-overlay (make-overlay 1 1)
-          web-mode-end-tag-overlay (make-overlay 1 1))
-    (overlay-put web-mode-start-tag-overlay
-                 'font-lock-face
-                 'web-mode-current-element-highlight-face)
-    (overlay-put web-mode-end-tag-overlay
-                 'font-lock-face
-                 'web-mode-current-element-highlight-face)))
-
-(defun web-mode-delete-tag-overlays ()
-  (when web-mode-start-tag-overlay
-    (delete-overlay web-mode-start-tag-overlay)
-    (delete-overlay web-mode-end-tag-overlay)))
-
-(defun web-mode-highlight-current-element ()
-  (let ((ctx (web-mode-tags-pos)))
-    (if (null ctx)
-        (web-mode-delete-tag-overlays)
-      (web-mode-make-tag-overlays)
-      (move-overlay web-mode-start-tag-overlay (caar ctx) (cdar ctx))
-      (move-overlay web-mode-end-tag-overlay (cadr ctx) (cddr ctx)))
-    ))
+    epp))
+
+(defun web-mode-detect-content-type ()
+  (cond
+   ((and (string= web-mode-engine "none")
+         (< (point) 16)
+         (eq (char-after 1) ?\#)
+         (string-match-p "php" (buffer-substring-no-properties
+                                (line-beginning-position)
+                                (line-end-position))))
+    (web-mode-set-engine "php"))
+   ((and (string= web-mode-content-type "javascript")
+         (< (point) web-mode-chunk-length)
+         (eq (char-after (point-min)) ?\/)
+         (string-match-p "@jsx" (buffer-substring-no-properties
+                                 (line-beginning-position)
+                                 (line-end-position))))
+    (web-mode-set-content-type "jsx"))
+   ))
 
 (defun web-mode-on-after-change (beg end len)
-  "Handles auto-pairing, auto-closing, and region-refresh after buffer alteration."
-
-;;  (message "pos=%d, beg=%d, end=%d, len=%d, cur=%d" (point) beg end len (current-column))
-;;  (backtrace)
-
-  (setq web-mode-expand-initial-pos nil
-        web-mode-expand-previous-state "")
-
-  (let ((pos (point)) self-insertion chunk auto-closed auto-opened atomic-insertion)
-
-    (setq atomic-insertion (and (= len 0)
-                                (= 1 (- end beg))))
-
-    (if (not (= (point-max) (+ (buffer-size) 1)))
-
-       (setq web-mode-is-narrowed t)
-
-      ;;-- auto-closing and auto-pairing
-
-      (when (and (> web-mode-jshint-errors 0)
-                 (get-text-property pos 'part-side))
-;;        (message "%S %S" beg (1+ end))
-        (remove-overlays beg (1+ end) 'font-lock-face 'web-mode-error-face)
-        )
-
-      (when (and (> pos 3)
-                 (not (get-text-property pos 'part-side))
-                 atomic-insertion)
-
-        (setq chunk (buffer-substring-no-properties (- beg 1) end))
-
-        ;;-- auto-opening
-        (when (and (not web-mode-disable-auto-opening)
-                   (string= ">\n" chunk)
-                   (not (eobp))
-                   (eq (get-text-property (- beg 1) 'tag-type) 'start)
-                   (eq (get-text-property end 'tag-type) 'end)
-                   (string= (get-text-property (- beg 1) 'tag-name)
-                            (get-text-property end 'tag-name)))
-          (setq auto-opened t)
-          (newline-and-indent)
-          ;;(newline)
-          ;;(indent-for-tab-command)
-          (forward-line -1)
-          (indent-for-tab-command)
-          )
-
-        ;;-- auto-closing
-        (when (and (> web-mode-tag-auto-close-style 0)
-                   (or (and (= web-mode-tag-auto-close-style 2)
-                            (string-match-p "[[:alnum:]'\"]>" chunk))
-                       (string= "</" chunk))
-                   (not (get-text-property (- beg 1) 'block-side)))
-          (when (web-mode-element-close)
-            (setq auto-closed t
-                  self-insertion t))
-          )
-
-        ;;-- auto-pairing
-        (when (and (not web-mode-disable-auto-pairing)
-                   (not self-insertion))
-          (let ((i 0) expr p after pos-end (l (length web-mode-auto-pairs)))
-            (setq pos-end (if (> (+ end 10) (line-end-position))
-                              (line-end-position)
-                            (+ end 10)))
-            (setq chunk (buffer-substring-no-properties (- beg 2) end)
-                  after (buffer-substring-no-properties end pos-end))
-            (while (and (< i l) (not self-insertion))
-              (setq expr (elt web-mode-auto-pairs i))
-              (setq i (1+ i))
-;;              (message "%S" expr)
-;;              (when (string= (elt expr 0) chunk)
-;;              (message "expr1=%S after=%S" (or (elt expr 2) (elt expr 1)) after)
-              (when (and (string= (elt expr 0) chunk)
-;;                         (progn (message "%S %S" (elt expr 1) after) t)
-                         (not (string-match-p (or (elt expr 2) (elt expr 1)) after)))
-                (setq self-insertion t)
-                (insert (elt expr 1))
-                (if (not (elt expr 2))
-                    (goto-char pos)
-                  (setq p (point))
-                  (insert (elt expr 2))
-                  (goto-char p))
-                ) ;when
-              ) ;while
-            ) ;let
-          ) ;when
-
-        ) ;end auto-pairing auto-closing
-
-      ;;-- region-refresh
-      ;;      (save-match-data
-      (cond
-
-       ;; ((and nil (web-mode-is-content beg)
-       ;;       atomic-insertion
-       ;;       (not self-insertion)
-       ;;       (not (member (char-before) web-mode-electric-chars)))
-       ;;  (message "no invalidation %c" (char-before))
-       ;;  )
-
-       ((and nil ;;github:issue163
-             web-mode-has-any-large-part
-             atomic-insertion
-             (not (member (char-before) '(?\} ?\n)))
-             (not self-insertion)
-             (or (member (get-text-property beg 'part-side)
-                         '(css
-                           ;;javascript json
-                           ))
-                 (member web-mode-content-type '("css"
-                                                 ;;"javascript" "json"
-                                                 ))))
-        (if (or (string= web-mode-content-type "css")
-                (eq (get-text-property beg 'part-side) 'css))
-            (web-mode-invalidate-css-region beg end)
-          (web-mode-invalidate-javascript-region beg end))
-        )
-
-       ((and nil
-             web-mode-has-any-large-block
-             atomic-insertion
-             (not self-insertion)
-             (get-text-property beg 'block-side)
-             (member web-mode-engine '("asp")))
-        (web-mode-invalidate-asp-region beg end)
-        )
-
-       (t
-
-;;        (message "after change : pos(%d) beg(%d) end(%d) len(%d) cur(%d)" (point) beg end len (current-column))
-
-        ;; (message "start(%S) end(%S)"
-        ;;          (or (web-mode-previous-tag-at-bol-pos beg)
-        ;;              (point-min))
-        ;;          (or (web-mode-next-tag-at-eol-pos end)
-        ;;              (point-max)))
-
-        (web-mode-scan-region (or (web-mode-previous-tag-at-bol-pos beg)
-                                  (point-min))
-                              (or (web-mode-next-tag-at-eol-pos end)
-                                  (point-max)))
-        )
-       )
-      ;;        ) ;save-match-data
-
-      ;;-- auto-indentation
-      (when (and (not web-mode-disable-auto-indentation)
-                 (not auto-opened)
-                 (or auto-closed
-                     (and (> end (point-min))
-                          (get-text-property (1- end) 'tag-end)
-                          (get-text-property (line-beginning-position) 'tag-beg))))
-        (indent-for-tab-command)
-        )
-
-      (when (and (string= web-mode-engine "none")
-                 (< (point) 16)
-                 (eq (char-after 1) ?\#)
-                 (string-match-p "php" (buffer-substring-no-properties
-                                        (line-beginning-position)
-                                        (line-end-position))))
-        (web-mode-set-engine "php")
-        )
-
-      ) ;if narrowed
+ ;;(message "after-change: pos=%d, beg=%d, end=%d, len=%d, ocmd=%S, cmd=%S" (point) beg end len this-original-command this-command)
+ ;;(backtrace)
+ ;;(message "this-command=%S" this-command)
+  (when (eq this-original-command 'yank)
+    (setq web-mode-inhibit-fontification t))
+  (when (or (null web-mode-change-beg) (< beg web-mode-change-beg))
+    (setq web-mode-change-beg beg))
+  (when (or (null web-mode-change-end) (> end web-mode-change-end))
+    (setq web-mode-change-end end))
+  )
+
+(defun web-mode-complete ()
+  "Autocomple at point."
+  (interactive)
+  (let ((pos (point))
+        (char (char-before))
+        (chunk (buffer-substring-no-properties (- (point) 2) (point)))
+        (auto-closed   nil)
+        (auto-expanded nil)
+        (auto-paired   nil)
+        (auto-quoted   nil)
+        expanders)
+
+    ;;-- auto-closing
+    (when (and web-mode-enable-auto-closing
+               (>= pos 4)
+               (or (string= "</" chunk)
+                   ;;(progn (message "%c" char) nil)
+                   (and (= web-mode-auto-close-style 2)
+                        (or (string= web-mode-content-type "jsx")
+                            (not (get-text-property pos 'part-side)))
+                        (string-match-p "[[:alnum:]'\"]>" chunk)))
+               (not (get-text-property (- pos 2) 'block-side))
+               (web-mode-element-close))
+      (setq auto-closed t))
+
+    ;;-- auto-pairing
+    (when (and web-mode-enable-auto-pairing
+               (>= pos 4)
+               (not auto-closed))
+      (let ((i 0) expr after pos-end (l (length web-mode-auto-pairs)))
+        (setq pos-end (if (> (+ pos 32) (line-end-position))
+                          (line-end-position)
+                        (+ pos 10)))
+        (setq chunk (buffer-substring-no-properties (- pos 3) pos)
+              after (buffer-substring-no-properties pos pos-end))
+        (while (and (< i l) (not auto-paired))
+          (setq expr (elt web-mode-auto-pairs i)
+                i (1+ i))
+          ;;(message "chunk=%S expr=%S after=%S" chunk expr after)
+          (when (and (string= (car expr) chunk)
+                     (not (string-match-p (regexp-quote (cdr expr)) after)))
+            (setq auto-paired t)
+            (insert (cdr expr))
+            (if (string-match-p "|" (cdr expr))
+                (progn
+                  (search-backward "|")
+                  (delete-char 1))
+              (goto-char pos))
+            ) ;when
+          ) ;while
+        ) ;let
+      )
+
+    ;;-- auto-expanding
+    (when (and web-mode-enable-auto-expanding
+               (not auto-closed)
+               (not auto-paired)
+               (eq char ?\/)
+               (not (get-text-property (1- pos) 'tag-type))
+               (not (get-text-property (1- pos) 'part-side))
+               (not (get-text-property (1- pos) 'block-side))
+               (looking-back "\\(^\\|[[:punct:][:space:]>]\\)./"))
+      (setq expanders (append web-mode-expanders web-mode-extra-expanders))
+      (let ((i 0) pair (l (length expanders)))
+        (setq chunk (buffer-substring-no-properties (- pos 2) pos))
+        ;;(message "%S" chunk)
+        (while (and (< i l) (not auto-expanded))
+          (setq pair (elt expanders i)
+                i (1+ i))
+          (when (string= (car pair) chunk)
+            (setq auto-expanded t)
+            (delete-char -2)
+            (insert (cdr pair))
+            (when (string-match-p "|" (cdr pair))
+              (search-backward "|")
+              (delete-char 1))
+            ) ;when
+          ) ;while
+        ) ;let
+      )
+
+    ;;-- auto-quoting
+    (when (and web-mode-enable-auto-quoting
+               (>= pos 4)
+               (not (get-text-property pos 'block-side))
+               (not auto-closed)
+               (not auto-paired)
+               (not auto-expanded)
+               (get-text-property (- pos 2) 'tag-attr)
+               )
+      (cond
+       ((and (eq char ?\=)
+             (not (looking-at-p "[ ]*[\"']")))
+        (insert "\"\"")
+        (backward-char)
+        (setq auto-quoted t))
+       ((and (eq char ?\")
+             (looking-back "=[ ]*\"")
+             (not (looking-at-p "[ ]*[\"]")))
+        (insert-and-inherit "\"")
+        (backward-char)
+        (setq auto-quoted t))
+       ((and (eq char ?\')
+             (looking-back "=[ ]*'")
+             (not (looking-at-p "[ ]*[']")))
+        (insert-and-inherit "'")
+        (backward-char)
+        (setq auto-quoted t))
+       ((and (eq char ?\")
+             (eq (char-after) ?\"))
+        (delete-char 1)
+        (cond
+         ((looking-back "=\"\"")
+          (backward-char))
+         ((eq (char-after) ?\s)
+          (forward-char))
+         (t
+          (insert " "))
+         ) ;cond
+        )
+       ) ;cond
+      ) ;when
+
+    ;;--
+    (cond
+     ((or auto-closed auto-paired auto-expanded auto-quoted)
+      (when (and web-mode-change-end
+                 (>= (line-end-position) web-mode-change-end))
+        (setq web-mode-change-end (line-end-position)))
+      (list :auto-closed auto-closed
+            :auto-paired auto-paired
+            :auto-expanded auto-expanded
+            :auto-quoted auto-quoted))
+     (t
+      nil)
+     )
+
     ))
 
-(defun web-mode-invalidate-css-region (pos-beg pos-end)
-  "Invalidate css region (only when one char has been inserted)"
-  (save-excursion
-    (let (pair part-beg part-end)
-      (if (string= web-mode-content-type "css")
-          (setq part-beg (point-min)
-                part-end (point-max))
-        (setq part-beg (web-mode-part-beginning-position pos-beg)
-              part-end (web-mode-part-end-position pos-beg)))
-      (setq pair (web-mode-css-current-rule pos-beg part-beg part-end))
-;;      (message "css region : %S > %S" (car pair) (cdr pair))
-;;      (web-mode-highlight-region (car pair) (cdr pair) "css")
-      )))
-
-(defun web-mode-invalidate-javascript-region (pos-beg pos-end)
-  "Invalidate javascript region (only when one char has been inserted)"
-  (save-excursion
-    (let (beg end part-beg part-end lang is-token-char)
-      (goto-char pos-beg)
-      (setq is-token-char (not (null (member (char-before) '(?\" ?\' ?\/ ?\< ?\*)))))
-;;      (message "%S %c(%S)" pos-beg (char-before) is-token-char)
-      (if (member web-mode-content-type '("javascript" "json"))
-          (setq part-beg (point-min)
-                part-end (point-max)
-                lang web-mode-content-type)
-        (setq part-beg (web-mode-part-beginning-position pos-beg)
-              part-end (web-mode-part-end-position pos-beg)
-              lang (symbol-name (get-text-property pos-beg 'part-side))))
-      (cond
-       ((and (not is-token-char)
-             (get-text-property (1- pos-beg) 'part-token))
-;;        (message "nothing")
-        )
-       ((and (not is-token-char)
-             (progn (back-to-indentation) (setq beg (point)))
-             (if (>= beg part-beg) beg part-beg)
-             (progn (goto-char pos-end) (end-of-line) (setq end (point)))
-             (if (<= end part-end) end part-end))
-        ;;            (message "%S" (buffer-substring-no-properties beg end))
-        (web-mode-scan-part beg end)
-        )
-       ;;        (message "%S" (buffer-substring-no-properties part-beg part-end))
-       (t
-        (web-mode-scan-part part-beg part-end))
-       )
-      )))
-
-;; todo : gestion du remove-text-properties (ne pas toucher à pas mal de properties : block-beg, part-side etc.)
-(defun web-mode-invalidate-asp-region (pos-beg pos-end)
-  "Invalidate asp region."
-  (save-excursion
-    (let (beg end part-beg part-end)
-      (setq part-beg (web-mode-block-beginning-position pos-beg)
-            part-end (web-mode-block-end-position pos-beg))
-      (if (and part-beg part-end
-               (progn (goto-char pos-beg) t)
-               (not (member (char-after) '(?\" ?\' ?\/)))
-               (progn (back-to-indentation) t)
-               (setq beg (point))
-               (if (>= beg part-beg) beg part-beg)
-               (progn (goto-char pos-end) t)
-               (progn (end-of-line) t)
-               (setq end (point))
-               (if (<= end part-end) end part-end))
-            (web-mode-highlight-region beg end "asp")
-        (web-mode-highlight-region part-beg part-end "asp")
-        )
-      )))
+(defun web-mode-on-post-command ()
+  (let (ctx n char)
+
+    ;;(message "this-command=%S (%S)" this-command web-mode-expand-previous-state)
+    ;;(message "%S: %S %S" this-command web-mode-change-beg web-mode-change-end)
+
+    (when (and web-mode-expand-previous-state
+               (not (member this-command '(web-mode-mark-and-expand
+                                           er/expand-region
+                                           mc/mark-next-like-this))))
+      (when (eq this-command 'keyboard-quit)
+        (goto-char web-mode-expand-initial-pos))
+      (deactivate-mark)
+      (when web-mode-expand-initial-scroll
+        (set-window-start (selected-window) web-mode-expand-initial-scroll)
+        )
+      (setq web-mode-expand-previous-state nil
+            web-mode-expand-initial-pos nil
+            web-mode-expand-initial-scroll nil))
+
+    (when (member this-command '(yank))
+      (let ((beg web-mode-change-beg) (end web-mode-change-end))
+        (setq web-mode-inhibit-fontification nil)
+        (when (and web-mode-change-beg web-mode-change-end)
+          (save-excursion
+            (font-lock-fontify-region web-mode-change-beg web-mode-change-end))
+          (when web-mode-enable-auto-indentation
+            (indent-region beg end))
+          ) ;and
+        )
+      )
+
+    (when (< (point) 16)
+      (web-mode-detect-content-type))
+
+    (when (and web-mode-enable-engine-detection
+               (or (null web-mode-engine) (string= web-mode-engine "none"))
+               (< (point) web-mode-chunk-length)
+               (web-mode-detect-engine))
+      (web-mode-on-engine-setted)
+      (web-mode-buffer-highlight))
+
+    (when (> (point) 1)
+      (setq char (char-before)))
+
+    (cond
+
+     ((null char)
+      )
+
+     ((and (>= (point) 3)
+           (member this-command '(self-insert-command))
+           (not (member (get-text-property (point) 'part-token) '(comment string))))
+      (setq ctx (web-mode-complete)))
+
+     ((and web-mode-enable-auto-opening
+           (member this-command '(newline electric-newline-and-maybe-indent))
+           (or (and (not (eobp))
+                    (eq (char-after) ?\<)
+                    (eq (get-text-property (point) 'tag-type) 'end)
+                    (looking-back ">\n[ \t]*")
+                    (setq n (length (match-string-no-properties 0)))
+                    (eq (get-text-property (- (point) n) 'tag-type) 'start)
+                    (string= (get-text-property (- (point) n) 'tag-name)
+                             (get-text-property (point) 'tag-name))
+                    )
+               (and (get-text-property (1- (point)) 'block-side)
+                    (string= web-mode-engine "php")
+                    (looking-back "<\\?php[ ]*\n")
+                    (looking-at-p "[ ]*\\?>"))))
+      (newline-and-indent)
+      (forward-line -1)
+      (indent-according-to-mode)
+      )
+     ) ;cond
+
+    (when (and web-mode-enable-auto-indentation
+               (member this-command '(self-insert-command))
+               (or (and ctx
+                        (or (plist-get ctx :auto-closed)
+                            (plist-get ctx :auto-expanded)))
+                   (and (> (point) (point-min))
+                        (get-text-property (1- (point)) 'tag-end)
+                        (get-text-property (line-beginning-position) 'tag-beg))))
+      (indent-according-to-mode)
+      (when (and web-mode-change-end (> web-mode-change-end (point-max)))
+        (message "post-command: enlarge web-mode-change-end")
+        (setq web-mode-change-end (point-max))
+        )
+      ) ;when auto-indent
+
+    (when web-mode-enable-current-element-highlight
+      (web-mode-highlight-current-element))
+
+    (when (and web-mode-enable-current-column-highlight
+               (not (web-mode-buffer-narrowed-p)))
+      (web-mode-column-show))
+
+    ;;(message "post-command (%S) (%S)" web-mode-change-end web-mode-change-end)
+
+    ))
 
 (defun web-mode-dom-apostrophes-replace ()
-  "Replace ' with ."
+  "Replace char(') with char(’) in the html contents of the buffer."
   (interactive)
   (save-excursion
     (let ((min (point-min)) (max (point-max)))
@@ -8189,15 +9548,13 @@
               max (region-end))
         (deactivate-mark))
       (goto-char min)
-      (while (web-mode-rsf-content "\\([[:alpha:]]\\)'\\([[:alpha:]]\\)" max)
-        (replace-match "\\1\\2")
-        ) ;while
+      (while (web-mode-content-rsf "\\([[:alpha:]]\\)'\\([[:alpha:]]\\)" max)
+        (replace-match "\\1’\\2"))
       )))
 
 (defun web-mode-dom-entities-encode ()
-  "Replace special chars with HTML entities (e.g. é becomes &eacute;)"
   (save-excursion
-    (let (regexp ms pair elt (min (point-min)) (max (point-max)))
+    (let (regexp ms elt (min (point-min)) (max (point-max)))
       (when mark-active
         (setq min (region-beginning)
               max (region-end))
@@ -8208,18 +9565,16 @@
         (setq regexp (concat regexp (char-to-string (cdr pair))))
         )
       (setq regexp (concat regexp "]"))
-      (while (web-mode-rsf-content regexp max)
+      (while (web-mode-content-rsf regexp max)
         (setq elt (match-string-no-properties 0))
         (setq elt (aref elt 0))
         (setq elt (car (rassoc elt web-mode-html-entities)))
-;;        (message "%S" elt)
         (replace-match (concat "&" elt ";"))
         ) ;while
       )))
 
-;; ½ &frac12; &#189; &#x00BD;
 (defun web-mode-dom-entities-replace ()
-  "Replace HTML entities e.g. entities &eacute; &#233; &#x00E9; become é"
+  "Replace html entities (e.g. &eacute; &#233; or &#x00E9; become é)"
   (interactive)
   (save-excursion
     (let (ms pair elt (min (point-min)) (max (point-max)))
@@ -8228,32 +9583,29 @@
               max (region-end))
         (deactivate-mark))
       (goto-char min)
-      (while (web-mode-rsf-content "&\\([#]?[[:alnum:]]\\{2,8\\}\\);" max)
+      (while (web-mode-content-rsf "&\\([#]?[[:alnum:]]\\{2,8\\}\\);" max)
         (setq elt nil)
-;;        (message "E=%S" (match-string 1))
         (setq ms (match-string-no-properties 1))
-        (if (eq (aref ms 0) ?\#)
-            (if (eq (aref ms 1) ?x)
-                (progn
-                  (setq elt (substring ms 2))
-                  (setq elt (downcase elt))
-                  (setq elt (string-to-number elt 16))
-                  (setq elt (char-to-string elt))
-                  )
-              (setq elt (substring ms 1))
-              (setq elt (char-to-string (string-to-number elt)))
-              )
-          (setq pair (assoc ms web-mode-html-entities))
-          ;;        (message "pos=%S name=%S pair=%S" (point) name pair)
-          (if pair (setq elt (cdr pair)))
-          (if elt (setq elt (char-to-string elt)))
-          ) ;if
-        (if elt (replace-match elt))
+        (cond
+         ((not (eq (aref ms 0) ?\#))
+          (and (setq pair (assoc ms web-mode-html-entities))
+               (setq elt (cdr pair))
+               (setq elt (char-to-string elt))))
+         ((eq (aref ms 1) ?x)
+          (setq elt (substring ms 2))
+          (setq elt (downcase elt))
+          (setq elt (string-to-number elt 16))
+          (setq elt (char-to-string elt)))
+         (t
+          (setq elt (substring ms 1))
+          (setq elt (char-to-string (string-to-number elt))))
+         ) ;cond
+        (when elt (replace-match elt))
         ) ;while
       )))
 
 (defun web-mode-dom-xml-replace ()
-  "Replace &, > and < in HTML content."
+  "Replace &, > and < in html content."
   (interactive)
   (save-excursion
     (let (expr (min (point-min)) (max (point-max)))
@@ -8262,7 +9614,7 @@
               max (region-end))
         (deactivate-mark))
       (goto-char min)
-      (while (web-mode-rsf-content "[&<>]" max)
+      (while (web-mode-content-rsf "[&<>]" max)
         (replace-match (cdr (assq (char-before) web-mode-xml-chars)) t t))
       )))
 
@@ -8277,77 +9629,348 @@
         (deactivate-mark))
       (goto-char min)
       (setq expr (concat (car web-mode-smart-quotes) "\\2" (cdr web-mode-smart-quotes)))
-      (while (web-mode-rsf-content "\\(\"\\)\\(.\\{1,200\\}\\)\\(\"\\)" max)
+      (while (web-mode-content-rsf "\\(\"\\)\\(.\\{1,200\\}\\)\\(\"\\)" max)
         (replace-match expr)
         ) ;while
       )))
 
 (defun web-mode-dom-xpath (&optional pos)
-  "HTML path"
+  "Display html path."
   (interactive)
   (unless pos (setq pos (point)))
   (save-excursion
     (goto-char pos)
     (let (path)
-      (while (web-mode-element-parent (point))
+      (while (web-mode-element-parent)
         (setq path (cons (get-text-property (point) 'tag-name) path))
         )
       (message "/%s" (mapconcat 'identity path "/"))
       )))
 
-(defun web-mode-block-ends-with (regexp)
-  "Check if current ends with regexp"
+(defun web-mode-block-ends-with (regexp &optional pos)
+  (unless pos (setq pos (point)))
+  (save-excursion
+    (goto-char pos)
+    (save-match-data
+      (if (stringp regexp)
+          (and (web-mode-block-end)
+               (progn (backward-char) t)
+               (web-mode-block-skip-blank-backward)
+               (progn (forward-char) t)
+               (looking-back regexp))
+        (let ((pair regexp)
+              (block-beg (web-mode-block-beginning-position pos))
+              (block-end (web-mode-block-end-position pos)))
+          (and (web-mode-block-end)
+               (web-mode-block-sb (car pair) block-beg)
+               (not (web-mode-sf (cdr pair) block-end)))
+          ) ;let
+        ) ;if
+      )))
+
+(defun web-mode-block-token-starts-with (regexp &optional pos)
+  (unless pos (setq pos (point)))
+  (save-excursion
+    (and (goto-char pos)
+         (web-mode-block-token-beginning)
+         (skip-chars-forward "[\"']")
+         (looking-at regexp))
+    ))
+
+(defun web-mode-block-starts-with (regexp &optional pos)
+  (unless pos (setq pos (point)))
   (save-excursion
-    (and (web-mode-block-end)
-         (looking-back regexp))
+    (and (web-mode-block-beginning)
+         (web-mode-block-skip-blank-forward)
+         (looking-at regexp))
+    ))
+
+(defun web-mode-block-skip-blank-backward (&optional pos)
+  (unless pos (setq pos (point)))
+  (let ((continue t))
+    (goto-char pos)
+    (while continue
+      (if (and (get-text-property (point) 'block-side)
+               (not (bobp))
+               (or (member (char-after) '(?\s ?\n))
+                   (member (get-text-property (point) 'block-token)
+                           '(delimiter-beg delimiter-end comment))))
+          (backward-char)
+        (setq continue nil))
+      ) ;while
+    (point)))
+
+(defun web-mode-block-skip-blank-forward (&optional pos)
+  (unless pos (setq pos (point)))
+  (let ((continue t))
+    (goto-char pos)
+    (while continue
+      (if (and (get-text-property (point) 'block-side)
+               (or (member (char-after) '(?\s ?\n ?\t))
+                   (member (get-text-property (point) 'block-token)
+                           '(delimiter-beg delimiter-end comment))))
+          (forward-char)
+        (setq continue nil))
+      ) ;while
+;;    (message "pt=%S" (point))
+    (point)))
+
+(defun web-mode-tag-attributes-sort (&optional pos)
+  "Sort the attributes inside the current html tag."
+  (interactive)
+  (unless pos (setq pos (point)))
+  (save-excursion
+    (let (attrs (continue t) min max tag-beg tag-end attr attr-name attr-beg attr-end indent indentation sorter ins)
+      (if (not (member (get-text-property pos 'tag-type) '(start void)))
+          nil
+        (setq tag-beg (web-mode-tag-beginning-position pos)
+              tag-end (web-mode-tag-end-position))
+;;        (message "%S %S" tag-beg tag-end)
+        (goto-char tag-beg)
+        (while continue
+          (if (or (not (web-mode-attribute-next))
+                  (>= (point) tag-end))
+              (setq continue nil)
+            ;;(message "attr=%S" (point))
+            (setq attr-beg (web-mode-attribute-beginning-position)
+                  attr-end (1+ (web-mode-attribute-end-position)))
+            (when (null min)
+              (setq min attr-beg))
+            (setq max attr-end)
+            (goto-char attr-beg)
+            (setq attr (buffer-substring-no-properties attr-beg attr-end))
+            (if (string-match "^\\([[:alnum:]-]+\\)=" attr)
+                (setq attr-name (match-string-no-properties 1 attr))
+              (setq attr-name attr))
+            (setq indent (looking-back "^[ \t]*"))
+            (setq attrs (append attrs (list (list attr-beg attr-end attr-name attr indent))))
+            ) ;if
+          ) ;while
+        ) ;if in tag
+      (when attrs
+        (setq sorter (function
+                      (lambda (elt1 elt2)
+                        (string< (nth 2 elt1) (nth 2 elt2))
+                        )))
+        (setq attrs (sort attrs sorter))
+        (delete-region (1- min) max)
+        (setq ins "")
+        (dolist (elt attrs)
+          (if (and (nth 4 elt) (> (length ins) 1))
+              (setq ins (concat ins "\n"))
+            (setq ins (concat ins " ")))
+          (setq ins (concat ins (nth 3 elt)))
+          )
+        (goto-char (1- min))
+        (insert ins)
+        (web-mode-tag-beginning)
+        (setq min (line-beginning-position))
+        (web-mode-tag-end)
+        (setq max (line-end-position))
+        (indent-region min max)
+        )
+      ;;(message "attrs=%S" attrs)
+      )))
+
+(defun web-mode-attribute-insert ()
+  "Insert an attribute inside current tag."
+  (interactive)
+  (let (attr attr-name attr-value)
+    (cond
+     ((not (eq (get-text-property (point) 'tag-type) 'start))
+      (message "attribute-insert ** invalid context **"))
+     ((not (and (setq attr-name (read-from-minibuffer "Attribute name? "))
+                (> (length attr-name) 0)))
+      (message "attribute-insert ** failure **"))
+     (t
+      (setq attr (concat " " attr-name))
+      (when (setq attr-value (read-from-minibuffer "Attribute value? "))
+        (setq attr (concat attr "=\"" attr-value "\"")))
+      (web-mode-tag-end)
+      (re-search-backward "/?>")
+      (insert attr)
+      )
+     ) ;cond
     ))
 
-;;-- position -----------------------------------------------------------------------
-
-(defun web-mode-opening-paren-position (&optional pos limit)
-  "Fetch opening paren."
+(defun web-mode-attribute-transpose (&optional pos)
+  "Transpose the current html attribute."
+  (interactive)
+  (unless pos (setq pos (point)))
+  (let (ret attr-beg attr-end next-beg next-end tag-end)
+    (when (and (get-text-property pos 'tag-attr)
+               (setq next-beg (web-mode-attribute-next-position pos))
+               (setq next-end (web-mode-attribute-end-position next-beg))
+               (setq tag-end (web-mode-tag-end-position pos))
+               (> tag-end next-end))
+      (setq attr-beg (web-mode-attribute-beginning-position pos)
+            attr-end (web-mode-attribute-end-position pos))
+      ;;      (message "%S %S - %S %S" attr-beg attr-end next-beg next-end)
+      (transpose-regions attr-beg (1+ attr-end) next-beg (1+ next-end))
+      )))
+
+(defun web-mode-attribute-select (&optional pos)
+  "Select the current html attribute."
+  (interactive)
+  (unless pos (setq pos (point)))
+  (if (null (get-text-property pos 'tag-attr))
+      nil
+    (goto-char pos)
+    (web-mode-attribute-beginning)
+    (set-mark (point))
+    (web-mode-attribute-end)
+    (point)
+    ))
+
+(defun web-mode-attribute-kill (&optional arg)
+  "Kill the current html attribute."
+  (interactive "p")
+  (unless arg (setq arg 1))
+  (while (>= arg 1)
+    (setq arg (1- arg))
+    (web-mode-attribute-select)
+    (when mark-active
+      (let ((beg (region-beginning)) (end (region-end)))
+        (save-excursion
+          (goto-char end)
+          (when (looking-at "[ \n\t]*")
+            (setq end (+ end (length (match-string-no-properties 0)))))
+          ) ;save-excursion
+        (kill-region beg end)
+        ) ;let
+      ) ;when
+    ) ;while
+  )
+
+(defun web-mode-block-close (&optional pos)
+  "Close the first unclosed control block."
+  (interactive)
+  (unless pos (setq pos (point)))
+  (let ((continue t)
+        (h (make-hash-table :test 'equal)) ctx ctrl n closing-block)
+    (save-excursion
+      (while (and continue (web-mode-block-previous))
+        (when (setq ctx (web-mode-block-is-control (point)))
+          (setq ctrl (car ctx))
+          (setq n (gethash ctrl h 0))
+          (if (cdr ctx)
+              (puthash ctrl (1+ n) h)
+            (puthash ctrl (1- n) h))
+          (when (> (gethash ctrl h) 0)
+            (setq continue nil))
+          )
+        ) ;while
+      ) ;save-excursion
+    (when (and (null continue)
+               (setq closing-block (web-mode-closing-block ctrl)))
+      (insert closing-block)
+      (indent-according-to-mode)
+      ;;      (indent-for-tab-command)
+      )
+    ))
+
+(defun web-mode-closing-block (type)
+  (cond
+   ((string= web-mode-engine "php")       (concat "<?php end" type "; ?>"))
+   ((string= web-mode-engine "django")    (concat "{% end" type " %}"))
+   ((string= web-mode-engine "ctemplate") (concat "{{/" type "}}"))
+   ((string= web-mode-engine "blade")
+    (if (string= type "section") (concat "@show") (concat "@end" type)))
+   ((string= web-mode-engine "dust")      (concat "{/" type "}"))
+   ((string= web-mode-engine "mako")      (concat "% end" type))
+   ((string= web-mode-engine "closure")   (concat "{/" type "}"))
+   ((string= web-mode-engine "smarty")    (concat "{/" type "}"))
+   ((string= web-mode-engine "underscore")        "<% } %>")
+   ((string= web-mode-engine "lsp")               "<% ) %>")
+   ((string= web-mode-engine "erb")               "<% } %>")
+   ((string= web-mode-engine "erb")               "<% end %>")
+   ((string= web-mode-engine "go")                "{{end}}")
+   ((string= web-mode-engine "velocity")          "#end")
+   ((string= web-mode-engine "velocity")          "#{end}")
+   ((string= web-mode-engine "template-toolkit")  "[% end %]")
+   ((member web-mode-engine '("asp" "jsp"))
+    (if (string-match-p "[:.]" type) (concat "</" type ">") "<% } %>"))
+   (t nil)
+   ) ;cond
+  )
+
+;;---- POSITION ----------------------------------------------------------------
+
+(defun web-mode-comment-beginning-position (&optional pos)
+  (unless pos (setq pos (point)))
+  (car (web-mode-comment-boundaries pos)))
+
+(defun web-mode-comment-end-position (&optional pos)
+  (unless pos (setq pos (point)))
+  (cdr (web-mode-comment-boundaries pos)))
+
+(defun web-mode-part-opening-paren-position (pos &optional limit)
   (save-restriction
-    ;;    (unless paren (setq paren "("))
+    (unless limit (setq limit nil))
+    (goto-char pos)
+    (let* ((n -1)
+           (paren (char-after))
+           (pairs '((?\) . "[)(]")
+                    (?\] . "[\]\[]")
+                    (?\} . "[}{]")
+                    (?\> . "[><]")))
+           (regexp (cdr (assoc paren pairs)))
+           (continue (not (null regexp)))
+           (counter 0))
+      (while (and continue (re-search-backward regexp limit t))
+        (cond
+         ((> (setq counter (1+ counter)) 500)
+          (message "part-opening-paren-position ** warning **")
+          (setq continue nil))
+         ((or (web-mode-is-comment-or-string)
+              (get-text-property (point) 'block-side))
+          )
+         ((eq (char-after) paren)
+          (setq n (1- n)))
+         (t
+          (setq n (1+ n))
+          (setq continue (not (= n 0))))
+         )
+        ) ;while
+      (if (= n 0) (point) nil)
+      )))
+
+(defun web-mode-closing-paren-position (&optional pos limit)
+  (save-excursion
     (unless pos (setq pos (point)))
     (unless limit (setq limit nil))
     (goto-char pos)
-    (let ((continue t)
-          (n -1)
-          block-side
-          paren
-          (pairs '((")" . "[)(]")
-                   ("]" . "[\]\[]")
-                   ("}" . "[}{]")))
-          pt
-          regexp)
-      (setq paren (string (char-after)))
-      ;;      (message "paren=%S" paren)
-      (setq regexp (cdr (assoc paren pairs)))
-      (if (null regexp) (setq continue nil))
-      (setq block-side (and (get-text-property pos 'block-side)
+    (let* ((n 0)
+           (block-side (and (get-text-property pos 'block-side)
                             (not (string= web-mode-engine "razor"))))
-      (while (and continue (re-search-backward regexp limit t))
-        (unless (or (web-mode-is-comment-or-string)
-                    (and block-side (not (get-text-property (point) 'block-side))))
-          ;;          (message "pos=%S pt=%S" pos (point))
-          (if (not (string= (string (char-after)) paren))
-              (progn
-                (setq n (1+ n))
-                (if (= n 0)
-                    (setq continue nil
-                          pt (point))))
-            (setq n (1- n)))
-          ;;          (message "n=%S" n)
-          ) ;unless
-        )
-      pt
+           (paren (char-after))
+           (pairs '((?\( . "[)(]")
+                    (?\[ . "[\]\[]")
+                    (?\{ . "[}{]")
+                    (?\< . "[><]")))
+           (regexp (cdr (assoc paren pairs)))
+           (continue (not (null regexp))))
+      (while (and continue (re-search-forward regexp limit t))
+        (cond
+         ((or (web-mode-is-comment-or-string (1- (point)))
+              (and block-side (not (get-text-property (point) 'block-side))))
+          ;;(message "pt=%S" (point))
+          )
+         ((eq (char-before) paren)
+          (setq n (1+ n)))
+         (t
+          (setq n (1- n))
+          (setq continue (not (= n 0)))
+          )
+         ) ;cond
+        ) ;while
+      (if (= n 0) (1- (point)) nil)
       )))
 
 (defun web-mode-closing-delimiter-position (delimiter &optional pos limit)
-  "Fetch closing delimiter."
+  (unless pos (setq pos (point)))
+  (unless limit (setq limit nil))
   (save-excursion
-    (unless pos (setq pos (point)))
-    (unless limit (setq limit nil))
     (goto-char pos)
     (setq pos nil)
     (let ((continue t))
@@ -8357,62 +9980,456 @@
         ) ;while
       pos)))
 
-(defun web-mode-closing-paren-position (&optional pos limit)
-  "Fetch closing paren."
+(defun web-mode-tag-match-position (&optional pos)
+  (unless pos (setq pos (point)))
   (save-excursion
-    (unless pos (setq pos (point)))
-    (unless limit (setq limit nil))
+    (web-mode-tag-match pos)
+    (if (= pos (point)) nil (point))))
+
+(defun web-mode-tag-beginning-position (&optional pos)
+  (unless pos (setq pos (point)))
+  (let (beg end depth)
+    (setq depth (get-text-property pos 'jsx-depth))
+    (when (and depth (get-text-property pos 'tag-attr-beg))
+       (setq depth (get-text-property (1- pos) 'jsx-depth)))
+    (cond
+     ((null pos)
+      (setq end nil))
+     ((get-text-property pos 'tag-beg)
+      (setq beg pos))
+     ((and (> pos 1) (get-text-property (1- pos) 'tag-beg))
+      (setq beg (1- pos)))
+     ((get-text-property pos 'tag-type)
+      (setq beg (previous-single-property-change pos 'tag-beg))
+      (when beg (setq beg (1- beg)))
+      (cond
+       ((not (get-text-property beg 'tag-beg))
+        (setq beg nil))
+       ((and depth (not (eq depth (get-text-property beg 'jsx-depth))))
+        (let ((continue (> beg (point-min))))
+          (while continue
+            (setq beg (previous-single-property-change beg 'tag-beg))
+            (when beg (setq beg (1- beg)))
+            (cond
+             ((null beg)
+              (setq continue nil))
+             ((not (get-text-property beg 'tag-beg))
+              (setq continue nil
+                    beg nil))
+             ((eq depth (get-text-property beg 'jsx-depth))
+              (setq continue nil))
+             ) ;cond
+            ) ;while
+          ) ;let
+        )
+       ) ;cond
+      )
+     (t
+      (setq beg nil))
+     ) ;cond
+    beg))
+
+(defun web-mode-tag-end-position (&optional pos)
+  (unless pos (setq pos (point)))
+  (let (end depth)
+    (setq depth (get-text-property pos 'jsx-depth))
+    (when (and depth (get-text-property pos 'tag-attr-beg))
+      (setq depth (get-text-property (1- pos) 'jsx-depth)))
+    (cond
+     ((null pos)
+      (setq end nil))
+     ((get-text-property pos 'tag-end)
+      (setq end pos))
+     ((get-text-property pos 'tag-type)
+      (setq end (next-single-property-change pos 'tag-end))
+      (cond
+       ((not (get-text-property end 'tag-end))
+        (setq end nil))
+       ((and depth (not (eq depth (get-text-property end 'jsx-depth))))
+        (let ((continue (< end (point-max))))
+          (while continue
+            (setq end (1+ end))
+            (setq end (next-single-property-change end 'tag-end))
+            (cond
+             ((null end)
+              (setq continue nil))
+             ((not (get-text-property end 'tag-end))
+              (setq continue nil
+                    end nil))
+             ((eq depth (get-text-property end 'jsx-depth))
+              (setq continue nil))
+             ) ;cond
+            ) ;while
+          ) ;let
+        )
+       ) ;cond
+      )
+     (t
+      (setq end nil))
+     ) ;cond
+    end))
+
+;; TODO: prendre en compte jsx-depth
+(defun web-mode-tag-next-position (&optional pos limit)
+  (unless pos (setq pos (point)))
+  (unless limit (setq limit (point-max)))
+  (cond
+   ((or (>= pos (point-max)) (>= pos limit)) nil)
+   (t
+    (when (get-text-property pos 'tag-beg) (setq pos (1+ pos)))
+    (setq pos (next-single-property-change pos 'tag-beg))
+    (if (and pos (<= pos limit)) pos nil))
+   ))
+
+;; TODO: prendre en compte jsx-depth
+(defun web-mode-tag-previous-position (&optional pos limit)
+  (unless pos (setq pos (point)))
+  (unless limit (setq limit (point-min)))
+  (cond
+   ((or (<= pos (point-min)) (<= pos limit)) nil)
+   (t
+    (when (get-text-property pos 'tag-beg) (setq pos (1- pos)))
+    (web-mode-go (previous-single-property-change pos 'tag-beg) -1))
+   ))
+
+;; TODO: prendre en compte jsx-depth
+(defun web-mode-attribute-beginning-position (&optional pos)
+  (unless pos (setq pos (point)))
+  (cond
+   ((null (get-text-property pos 'tag-attr))
+    nil)
+   ((get-text-property pos 'tag-attr-beg)
+    pos)
+   (t
+    (setq pos (previous-single-property-change pos 'tag-attr-beg))
+    (setq pos (1- pos)))
+   ))
+
+;; TODO: retoucher en incluant un param limit et en s'inspirant de
+;;       web-mode-attribute-next-position
+(defun web-mode-attribute-end-position (&optional pos)
+  (unless pos (setq pos (point)))
+  (let (end depth)
+    (setq depth (get-text-property pos 'jsx-depth))
+    (cond
+     ((null pos)
+      (setq end nil))
+     ((get-text-property pos 'tag-attr-end)
+      (setq end pos))
+     ((get-text-property pos 'tag-attr)
+      (setq end (next-single-property-change pos 'tag-attr-end))
+      (cond
+       ((not (get-text-property end 'tag-attr-end))
+        (setq end nil))
+       ((and depth
+             (eq depth (get-text-property end 'jsx-depth))
+             (not (eq depth (get-text-property end 'jsx-end))))
+        )
+       ((and depth (eq (1+ depth) (get-text-property end 'jsx-depth)))
+        )
+       ((and depth (not (eq (1+ depth) (get-text-property end 'jsx-depth))))
+        (let ((continue (< end (point-max))))
+          (while continue
+            (setq end (1+ end))
+            (setq end (next-single-property-change end 'tag-attr-end))
+            (cond
+             ((null end)
+              (setq continue nil))
+             ((not (get-text-property end 'tag-attr-end))
+              (setq continue nil
+                    end nil))
+             ((eq (1+ depth) (get-text-property end 'jsx-depth))
+              (setq continue nil))
+             ) ;cond
+            ) ;while
+          ) ;let
+        )
+       ) ;cond
+      )
+     (t
+      (setq end nil))
+     ) ;cond
+    end))
+
+(defun web-mode-attribute-next-position (&optional pos limit)
+  (unless pos (setq pos (point)))
+  (unless limit (setq limit (point-max)))
+  (let (continue depth)
+    (when (get-text-property pos 'tag-attr-beg)
+      (setq pos (1+ pos)))
+    (if (< pos limit)
+        (setq continue t
+              depth (get-text-property pos 'jsx-depth))
+      (setq continue nil
+            pos nil))
+    (while continue
+      (setq pos (next-single-property-change pos 'tag-attr-beg))
+      (cond
+       ((null pos)
+        (setq continue nil))
+       ((>= pos limit)
+        (setq continue nil
+              pos nil))
+       ((null depth)
+        (setq continue nil))
+       ((eq depth (get-text-property pos 'jsx-depth))
+        (setq continue nil))
+       (t
+        (setq pos (1+ pos)
+              continue (< pos limit)))
+       )
+      ) ;while
+    pos))
+
+(defun web-mode-attribute-previous-position (&optional pos limit)
+  (unless pos (setq pos (point)))
+  (unless limit (setq limit (point-min)))
+  (let (continue depth)
+    (when (get-text-property pos 'tag-attr-beg)
+      (setq pos (1- pos)))
+    (if (> pos limit)
+        (setq continue t
+              depth (get-text-property pos 'jsx-depth))
+      (setq continue nil
+            pos nil))
+    (while continue
+      (setq pos (previous-single-property-change pos 'tag-attr-beg))
+      (cond
+       ((null pos)
+        (setq continue nil))
+       ((< pos limit)
+        (setq continue nil
+              pos nil))
+       ((null depth)
+        (setq continue nil))
+       ((eq depth (get-text-property pos 'jsx-depth))
+        (setq continue nil))
+       (t
+        (setq pos (1- pos)
+              continue (> pos limit)))
+       ) ;cond
+      ) ;while
+    pos))
+
+;; TODO: prendre en compte jsx-depth
+(defun web-mode-element-beginning-position (&optional pos)
+  (unless pos (setq pos (point)))
+  (cond
+   ((null (get-text-property pos 'tag-type))
+    (setq pos (web-mode-element-parent-position)))
+   ((eq (get-text-property pos 'tag-type) 'end)
+    (setq pos (web-mode-tag-match-position pos))
+    (setq pos (if (get-text-property pos 'tag-beg) pos nil)))
+   ((member (get-text-property pos 'tag-type) '(start void))
+    (setq pos (web-mode-tag-beginning-position pos)))
+   (t
+    (setq pos nil))
+   ) ;cond
+  pos)
+
+;; TODO: prendre en compte jsx-depth
+(defun web-mode-element-end-position (&optional pos)
+  (unless pos (setq pos (point)))
+  (cond
+   ((null (get-text-property pos 'tag-type))
+    (setq pos (web-mode-element-parent-position pos))
+    (when pos
+      (setq pos (web-mode-tag-match-position pos))
+      (when pos (setq pos (web-mode-tag-end-position pos)))
+      )
+    )
+   ((member (get-text-property pos 'tag-type) '(end void))
+    (setq pos (web-mode-tag-end-position pos))
+    )
+   ((member (get-text-property pos 'tag-type) '(start))
+    (setq pos (web-mode-tag-match-position pos))
+    (when pos (setq pos (web-mode-tag-end-position pos))))
+   (t
+    (setq pos nil))
+   ) ;cond
+  pos)
+
+(defun web-mode-element-child-position (&optional pos)
+  (save-excursion
+    (let (child close)
+      (unless pos (setq pos (point)))
+      (goto-char pos)
+      (cond
+       ((eq (get-text-property pos 'tag-type) 'start)
+        (web-mode-tag-match)
+        (setq close (point))
+        (goto-char pos)
+        )
+       ((eq (get-text-property pos 'tag-type) 'void)
+        )
+       ((eq (get-text-property pos 'tag-type) 'end)
+        (web-mode-tag-beginning)
+        (setq close (point))
+        (web-mode-tag-match)
+        )
+       ((web-mode-element-parent-position pos)
+        (setq pos (point))
+        (web-mode-tag-match)
+        (setq close (point))
+        (goto-char pos)
+        )
+       ) ;cond
+      (when (and close
+                 (web-mode-element-next)
+                 (< (point) close))
+        (setq child (point))
+        )
+      child)))
+
+(defun web-mode-element-parent-position (&optional pos)
+  (let (n tag-type tag-name (continue t) (tags (make-hash-table :test 'equal)))
+    (save-excursion
+      (if pos (goto-char pos))
+      (while (and continue (web-mode-tag-previous))
+        (setq pos (point)
+              tag-type (get-text-property pos 'tag-type)
+              tag-name (get-text-property pos 'tag-name)
+              n (gethash tag-name tags 0))
+        (when (member tag-type '(end start))
+          (if (eq tag-type 'end)
+              (puthash tag-name (1- n) tags)
+            (puthash tag-name (1+ n) tags)
+            (when (= n 0) (setq continue nil))
+            ) ;if
+          ) ;when
+        ) ;while
+      ) ;save-excursion
+    (if (null continue) pos nil)))
+
+(defun web-mode-element-previous-position (&optional pos limit)
+  (unless pos (setq pos (point)))
+  (unless limit (setq limit (point-min)))
+  (save-excursion
     (goto-char pos)
-    (let ((continue t)
-          paren
-          (n 0)
-          (pairs '(("(" . "[)(]")
-                   ("[" . "[\]\[]")
-                   ("{" . "[}{]")))
-          pt
-          block-side
-          regexp)
-      (setq paren (string (char-after)))
-      (setq regexp (cdr (assoc paren pairs)))
-      (if (null regexp) (setq continue nil))
-      (setq block-side (and (get-text-property pos 'block-side)
-                            (not (string= web-mode-engine "razor"))))
-      (while (and continue (re-search-forward regexp limit t))
-        (unless (or (web-mode-is-comment-or-string)
-                    (and block-side (not (get-text-property (point) 'block-side))))
-          ;;          (message "char-before=%S pt=%S" (string (char-before)) (point))
-          (if (string= (string (char-before)) paren)
-              (setq n (1+ n))
-            (setq n (1- n))
-            (when (= n 0)
-              (setq continue nil
-                    pt (1- (point))))
-            )
-          ;;        (message "pt=%S char=%S n=%S" (point) (string (char-before)) n)
-          )
-        )
-      ;;      (message "n=%S pt=%S" n pt)
-      pt
-      )))
-
-(defun web-mode-opening-paren-block-position (pos limit)
-  "Is opened code line."
+    (let ((continue (not (bobp)))
+          (props '(start void comment)))
+      (while continue
+        (setq pos (web-mode-tag-previous))
+        (cond
+         ((or (null pos) (< (point) limit))
+          (setq continue nil
+                pos nil))
+         ((member (get-text-property (point) 'tag-type) props)
+          (setq continue nil))
+         )
+        ) ;while
+      pos)))
+
+(defun web-mode-element-next-position (&optional pos limit)
+  (unless pos (setq pos (point)))
+  (unless limit (setq limit (point-max)))
   (save-excursion
     (goto-char pos)
+    (let ((continue (not (eobp)))
+          (props '(start void comment)))
+      (while continue
+        (setq pos (web-mode-tag-next))
+        (cond
+         ((or (null pos) (> (point) limit))
+          (setq continue nil
+                pos nil))
+         ((member (get-text-property (point) 'tag-type) props)
+          (setq continue nil))
+         )
+        ) ;while
+;;      (message "pos=%S" pos)
+      pos)))
+
+(defun web-mode-part-end-position (&optional pos)
+  (unless pos (setq pos (point)))
+  (cond
+   ((member web-mode-content-type web-mode-part-content-types)
+    (setq pos (point-max)))
+   ((not (get-text-property pos 'part-side))
+    (setq pos nil))
+   ((= pos (point-max))
+    (setq pos nil))
+   ((not (get-text-property (1+ pos) 'part-side))
+    pos)
+   (t
+    (setq pos (next-single-property-change pos 'part-side)))
+   ) ;cond
+  pos)
+
+(defun web-mode-part-beginning-position (&optional pos)
+  (unless pos (setq pos (point)))
+  (cond
+   ((member web-mode-content-type web-mode-part-content-types)
+    (setq pos (point-min)))
+   ((not (get-text-property pos 'part-side))
+    (setq pos nil))
+   ((= pos (point-min))
+    (setq pos nil))
+   ((not (get-text-property (1- pos) 'part-side))
+    pos)
+   (t
+    (setq pos (previous-single-property-change pos 'part-side)))
+   ) ;cond
+  pos)
+
+(defun web-mode-part-next-position (&optional pos)
+  (unless pos (setq pos (point)))
+  (cond
+   ((and (= pos (point-min)) (get-text-property pos 'part-side))
+    )
+   ((not (get-text-property pos 'part-side))
+    (setq pos (next-single-property-change pos 'part-side)))
+   ((and (setq pos (web-mode-part-end-position pos)) (>= pos (point-max)))
+    (setq pos nil))
+   ((and (setq pos (1+ pos)) (not (get-text-property pos 'part-side)))
+    (setq pos (next-single-property-change pos 'part-side)))
+   ) ;cond
+  pos)
+
+(defun web-mode-block-match-position (&optional pos)
+  (unless pos (setq pos (point)))
+  (save-excursion
+    (web-mode-block-match pos)
+    (if (= pos (point)) nil (point))))
+
+(defun web-mode-block-control-previous-position (type &optional pos)
+  (unless pos (setq pos (point)))
+  (let ((continue t) controls)
+    (while continue
+      (setq pos (web-mode-block-previous-position pos))
+      (cond
+       ((null pos)
+        (setq continue nil
+              pos nil))
+       ((and (setq controls (web-mode-block-controls-get pos))
+             (eq (car (car controls)) type))
+        (setq continue nil))
+       ) ;cond
+      ) ;while
+    pos))
+
+(defun web-mode-block-opening-paren-position (pos limit)
+  (save-excursion
+    (when (> limit pos)
+      (message "block-opening-paren-position: limit(%S) > pos(%S)" limit pos))
+    (goto-char pos)
     (let (c
           n
           pt
-          (continue t)
-          (pairs '((")" . "(")
-                   ("]" . "[")
-                   ("}" . "{")))
+          (continue (> pos limit))
+          (pairs '((?\) . ?\()
+                   (?\] . ?\[)
+                   (?\} . ?\{)))
           (h (make-hash-table :test 'equal))
           (regexp "[\]\[)(}{]"))
       (while (and continue (re-search-backward regexp limit t))
-        (unless (web-mode-is-comment-or-string)
-          (setq c (string (char-after)))
+        (cond
+         ((web-mode-is-comment-or-string)
+          )
+         (t
+          (setq c (char-after))
           (cond
-           ((member c '("(" "{" "["))
+           ((member c '(?\( ?\{ ?\[))
             (setq n (gethash c h 0))
             (if (= n 0)
                 (setq continue nil
@@ -8424,589 +10441,800 @@
             (setq n (gethash c h 0))
             (puthash c (1- n) h))
            ) ;cond
-          ) ;unless
+          ) ;t
+         ) ;cond
         ) ;while
-      ;;      (message "h=%S pt=%S" h pt)
-      pt
-      )))
-
-(defun web-mode-previous-tag-at-bol-pos (pos)
-  "Line beginning with an HTML tag. BOL is returned or nil."
-  (save-excursion
-    (goto-char pos)
-    (setq pos nil)
-    (let ((continue t))
-      (back-to-indentation)
-      (if (get-text-property (point) 'tag-beg)
-          (setq pos (line-beginning-position))
-        (while continue
-          (forward-line -1)
-          (setq pos (point))
-          (when (bobp)
-            (setq continue nil))
-          (back-to-indentation)
-          (if (get-text-property (point) 'tag-beg)
-              (setq continue nil)
-            (setq pos nil))
-          ) ;while
-        ) ;if
-      pos)))
-
-(defun web-mode-next-tag-at-eol-pos (pos)
-  "Line ending with an HTML tag. EOL is returned or nil."
-  (save-excursion
-    (goto-char pos)
-    (let ((continue t))
-      (while continue
-        (end-of-line)
-        (setq pos (point))
-        (when (eobp)
-          (setq continue nil))
-        (skip-chars-backward " ")
-        (if (and (> (point) (point-min))
-                 (get-text-property (1- (point)) 'tag-end))
-            (setq continue nil)
-          (setq pos nil))
-        (if continue (forward-line))
-        ) ;while
-      pos)))
-
-(defun web-mode-html-tag-match-position (&optional pos)
-  "Html tag match position."
-  (unless pos (setq pos (point)))
-  (save-excursion
-    (web-mode-html-tag-match pos)
-    (if (= pos (point)) nil (point))))
-
-(defun web-mode-tag-match-position (&optional pos)
-  "Match tag position."
+      pt)))
+
+(defun web-mode-block-code-beginning-position (&optional pos)
   (unless pos (setq pos (point)))
-  (save-excursion
-    (web-mode-tag-match pos)
-    (if (= pos (point)) nil (point))))
-
-(defun web-mode-block-match-position (&optional pos)
-  "Match block position."
-  (unless pos (setq pos (point)))
-  (save-excursion
-    (web-mode-block-match pos)
-    (if (= pos (point)) nil (point))))
-
-(defun web-mode-tag-beginning-position (&optional pos)
-  "Beginning position of the current tag. POINT is at <."
-  (unless pos (setq pos (point)))
-  (let (beg)
-    (cond
-     ((get-text-property pos 'tag-beg)
-      (setq beg pos))
-     ((and (> pos 1) (get-text-property (1- pos) 'tag-beg))
-      (setq beg (1- pos)))
-     ((get-text-property pos 'tag-type)
-      (setq beg (1- (previous-single-property-change pos 'tag-beg)))
-      (when (not (get-text-property beg 'tag-beg))
-        (setq beg nil)))
-     (t
-      (setq beg nil))
-     ) ;cond
-    beg))
-
-(defun web-mode-tag-end-position (&optional pos)
-  "End position of the current tag. POINT is at >."
-  (unless pos (setq pos (point)))
-  (let (end)
-    (cond
-     ((null pos)
-      (setq end nil))
-     ((get-text-property pos 'tag-end)
-      (setq end pos))
-     ((get-text-property pos 'tag-type)
-      (setq end (next-single-property-change pos 'tag-end))
-      (when (not (get-text-property end 'tag-end))
-        (setq end nil)))
-     (t
-      (setq end nil))
-     ) ;cond
-    end))
-
-(defun web-mode-part-end-position (&optional pos)
-  "End position of the current part."
-  (unless pos (setq pos (point)))
-  (cond
-   ((member web-mode-content-type '("css" "javascript" "json"))
-    (setq pos (point-max)))
-   ((not (get-text-property pos 'part-side))
-    (setq pos nil))
-   (t
-    (setq pos (next-single-property-change pos 'tag-beg))
-    (if (not (get-text-property pos 'tag-beg))
-        (setq pos nil)
-      (setq pos (1- pos)))
-    )
-   ) ;cond
+  (when (and (setq pos (web-mode-block-beginning-position pos))
+             (eq (get-text-property pos 'block-token) 'delimiter-beg))
+    (setq pos (next-single-property-change pos 'block-token)))
   pos)
 
-(defun web-mode-part-beginning-position (&optional pos)
-  "Beginning of pat"
+(defun web-mode-block-beginning-position (&optional pos)
   (unless pos (setq pos (point)))
   (cond
-   ((member web-mode-content-type '("css" "javascript" "json"))
-    (setq pos (point-min)))
-   ((not (get-text-property pos 'part-side))
-    (setq pos nil))
-   (t
-    (setq pos (previous-single-property-change pos 'tag-end))
-    (when (or (= pos (point-min))
-              (not (get-text-property (1- pos) 'tag-end)))
-      (setq pos nil)))
-   ) ;cond
-  pos)
-
-(defun web-mode-element-beginning-position (&optional pos)
-  "Beginning of element pos."
-  (unless pos (setq pos (point)))
-  (cond
-   ((null (get-text-property pos 'tag-type))
-    (setq pos (web-mode-element-parent-position)))
-   ((eq (get-text-property pos 'tag-type) 'end)
-    (setq pos (web-mode-html-tag-match-position pos))
-    (setq pos (if (get-text-property pos 'tag-beg) pos nil)))
-   ((member (get-text-property pos 'tag-type) '(start void))
-    (setq pos (web-mode-tag-beginning-position pos)))
-   (t
-    (setq pos nil))
-   ) ;cond
-  pos)
-
-(defun web-mode-element-end-position (&optional pos)
-  "End of element pos."
-  (unless pos (setq pos (point)))
-  (cond
-   ((null (get-text-property pos 'tag-type))
-    (setq pos (web-mode-element-parent-position pos))
-    (when pos
-      (setq pos (web-mode-html-tag-match-position pos))
-      (when pos (setq pos (web-mode-tag-end-position pos)))
-      )
-    )
-   ((member (get-text-property pos 'tag-type) '(end void))
-    (setq pos (web-mode-tag-end-position pos))
-    )
-   ((member (get-text-property pos 'tag-type) '(start))
-    (setq pos (web-mode-html-tag-match-position pos))
-    (when pos (setq pos (web-mode-tag-end-position pos))))
-   (t
-    (setq pos nil))
-   ) ;cond
-  pos)
-
-(defun web-mode-element-child-position (&optional pos)
-  "Child element pos."
-  (save-excursion
-    (let (child close)
-      (unless pos (setq pos (point)))
-      (goto-char pos)
-      (cond
-       ((eq (get-text-property pos 'tag-type) 'start)
-        (web-mode-html-tag-match)
-        (setq close (point))
-        (goto-char pos)
-        )
-       ((eq (get-text-property pos 'tag-type) 'void)
-        )
-       ((eq (get-text-property pos 'tag-type) 'end)
-        (web-mode-tag-beginning)
-        (setq close (point))
-        (web-mode-html-tag-match)
-        )
-       ((web-mode-element-parent-position pos)
-        (setq pos (point))
-        (web-mode-html-tag-match)
-        (setq close (point))
-        (goto-char pos)
-        )
-       ) ;cond
-      (when (and close
-                 (web-mode-element-next)
-                 (< (point) close))
-        (setq child (point))
-        )
-      child
-      )))
-
-(defun web-mode-element-parent-position (&optional pos)
-  "Parent element pos."
-  (let (n
-        tag-type
-        tag-name
-        (continue t)
-        (h (make-hash-table :test 'equal)))
-    (save-excursion
-      (if pos (goto-char pos))
-      (while (and continue (web-mode-tag-previous))
-        (setq pos (point))
-        (setq tag-type (get-text-property pos 'tag-type)
-              tag-name (get-text-property pos 'tag-name))
-        (setq n (gethash tag-name h 0))
-        (when (member tag-type '(end start))
-          (if (eq tag-type 'end)
-              (puthash tag-name (1- n) h)
-            (puthash tag-name (1+ n) h)
-            (when (= n 0) (setq continue nil))
-            ) ;if
-          ) ;when
-        ) ;while
-      ) ;save-excursion
-    (if (null continue) pos nil)
-    ))
-
-(defun web-mode-block-beginning-position (&optional pos)
-  "web-mode-block-beginning-position"
-  (unless pos (setq pos (point)))
-;;  (message "web-mode-block-beginning-position=%S" pos)
-  (cond
-   ((or (and (get-text-property pos 'block-side)
-             (= pos (point-min)))
+   ((or (and (get-text-property pos 'block-side) (= pos (point-min)))
         (get-text-property pos 'block-beg))
     )
-   ((and (> pos (point-min))
-         (get-text-property (1- pos) 'block-beg))
-    (setq pos (1- pos))
-    )
+   ((and (> pos (point-min)) (get-text-property (1- pos) 'block-beg))
+    (setq pos (1- pos)))
    ((get-text-property pos 'block-side)
     (setq pos (previous-single-property-change pos 'block-beg))
-    (setq pos (if pos (1- pos) (point-min)))
-;;    (setq pos (if pos pos (point-min)))
-    )
-   (t
-    (setq pos nil))
-   ) ;cond
-;;  (message "web-mode-block-beginning-position=%S" pos)
-  pos)
-
-(defun web-mode-block-end-position (&optional pos)
-  "web-mode-block-end-position"
-  (unless pos (setq pos (point)))
-  (cond
-   ((get-text-property pos 'block-end)
-    )
-   ((get-text-property pos 'block-side)
-    (setq pos (or (next-single-property-change pos 'block-end)
-                  (point-max)))
-    )
+    (setq pos (if (and pos (> pos (point-min))) (1- pos) (point-min))))
    (t
     (setq pos nil))
    ) ;cond
   pos)
 
-(defun web-mode-block-previous-position (&optional pos)
-  "web-mode-block-previous-position"
+(defun web-mode-block-string-beginning-position (pos &optional block-beg)
+  (unless pos (setq pos (point)))
+  (unless block-beg (setq block-beg (web-mode-block-beginning-position pos)))
+  (let (char (ori pos) (continue (not (null pos))))
+    (while continue
+      (setq char (char-after pos))
+      (cond
+       ((< pos block-beg)
+        (setq continue nil
+              pos block-beg))
+       ((and (member (get-text-property pos 'block-token) '(string comment))
+             (eq (get-text-property pos 'block-token) (get-text-property (1- pos) 'block-token)))
+        (setq pos (web-mode-block-token-beginning-position pos))
+        )
+       ((member char '(?\) ?\]))
+        (setq pos (web-mode-block-opening-paren-position pos block-beg))
+        (setq pos (1- pos))
+        )
+       ((and (> ori pos) (member char '(?\( ?\= ?\[ ?\? ?\: ?\; ?\, ?\`)))
+        (setq continue nil)
+        (web-mode-looking-at ".[ \t\n]*" pos)
+        (setq pos (+ pos (length (match-string-no-properties 0))))
+        )
+       ((web-mode-looking-back "\\_<\\(return\\|echo\\|include\\|print\\)[ \n\t]*" pos)
+        (setq continue nil))
+       (t
+        (setq pos (1- pos)))
+       ) ;cond
+      ) ;while
+    ;;(message "pos=%S" pos)
+    pos))
+
+(defun web-mode-block-statement-beginning-position (pos &optional block-beg)
+  (unless pos (setq pos (point)))
+  (setq pos (1- pos))
+  (unless block-beg (setq block-beg (web-mode-block-beginning-position pos)))
+  (let (char (continue (not (null pos))))
+    (while continue
+      (setq char (char-after pos))
+      (cond
+       ((< pos block-beg)
+        (setq continue nil
+              pos block-beg))
+       ((and (member (get-text-property pos 'block-token) '(string comment))
+             (eq (get-text-property pos 'block-token) (get-text-property (1- pos) 'block-token)))
+        (setq pos (web-mode-block-token-beginning-position pos)))
+       ((member char '(?\) ?\] ?\}))
+        (setq pos (web-mode-block-opening-paren-position pos block-beg))
+        (setq pos (1- pos)))
+       ((member char '(?\( ?\[ ?\{ ?\=))
+        (setq continue nil)
+        (web-mode-looking-at ".[ \t\n]*" pos)
+        (setq pos (+ pos (length (match-string-no-properties 0)))))
+       ((web-mode-looking-back "\\_<\\(return\\|echo\\|include\\|print\\)[ \n\t]*" pos)
+        (setq continue nil))
+       (t
+        (setq pos (1- pos)))
+       ) ;cond
+      ) ;while
+    pos))
+
+(defun web-mode-block-args-beginning-position (pos &optional block-beg)
+  (unless pos (setq pos (point)))
+  (setq pos (1- pos)) ;#512
+  (unless block-beg (setq block-beg (web-mode-block-beginning-position pos)))
+  (let (char (continue (not (null pos))))
+    (while continue
+      (setq char (char-after pos))
+      (cond
+       ((< pos block-beg)
+        (message "block-args-beginning-position ** failure **")
+        (setq continue nil
+              pos block-beg))
+       ((and (member (get-text-property pos 'block-token) '(string comment))
+             (eq (get-text-property pos 'block-token) (get-text-property (1- pos) 'block-token)))
+        (setq pos (web-mode-block-token-beginning-position pos)))
+       ((member char '(?\) ?\] ?\}))
+        (setq pos (web-mode-block-opening-paren-position pos block-beg))
+        (setq pos (1- pos)))
+       ((member char '(?\( ?\[ ?\{))
+        (setq continue nil)
+        (web-mode-looking-at ".[ \t\n]*" pos)
+        (setq pos (+ pos (length (match-string-no-properties 0)))))
+       ((and (string= web-mode-engine "php")
+             (web-mode-looking-back "\\_<\\(extends\\|implements\\)[ \n\t]*" pos))
+        (setq continue nil))
+       (t
+        (setq pos (1- pos)))
+       ) ;cond
+      ) ;while
+    pos))
+
+(defun web-mode-block-calls-beginning-position (pos &optional block-beg)
+  (unless pos (setq pos (point)))
+  (unless block-beg (setq block-beg (web-mode-block-beginning-position pos)))
+  (let (char (continue (not (null pos))))
+    (while continue
+      (setq char (char-after pos))
+      (cond
+       ((< pos block-beg)
+        (message "block-calls-beginning-position ** failure **")
+        (setq continue nil
+              pos block-beg))
+       ((and (member (get-text-property pos 'block-token) '(string comment))
+             (eq (get-text-property pos 'block-token) (get-text-property (1- pos) 'block-token)))
+        (setq pos (web-mode-block-token-beginning-position pos)))
+       ((member char '(?\) ?\]))
+        (setq pos (web-mode-block-opening-paren-position pos block-beg))
+        (setq pos (1- pos)))
+       ((member char '(?\( ?\[ ?\{ ?\} ?\= ?\? ?\: ?\; ?\,))
+        (setq continue nil)
+        (web-mode-looking-at ".[ \t\n]*" pos)
+        (setq pos (+ pos (length (match-string-no-properties 0)))))
+       ((web-mode-looking-back "\\(return\\|else\\)[ \n\t]*" pos)
+        (setq ;;pos (point)
+              continue nil))
+       (t
+        (setq pos (1- pos)))
+       ) ;cond
+      ) ;while
+    pos))
+
+(defun web-mode-javascript-string-beginning-position (pos &optional reg-beg)
+  (unless pos (setq pos (point)))
+  (let ((char nil)
+        (blockside (get-text-property pos 'block-side))
+        (i 0)
+        (continue (not (null pos))))
+    (unless reg-beg
+      (if blockside
+          (setq reg-beg (web-mode-block-beginning-position pos))
+        (setq reg-beg (web-mode-part-beginning-position pos)))
+      )
+    (while continue
+      (setq char (char-after pos))
+      (cond
+       ((> (setq i (1+ i)) 20000)
+        (message "javascript-string-beginning-position ** warning (%S) **" pos)
+        (setq continue nil
+              pos nil))
+       ((null pos)
+        (message "javascript-string-beginning-position ** invalid pos **")
+        (setq continue nil))
+       ((< pos reg-beg)
+        (message "javascript-string-beginning-position ** failure **")
+        (setq continue nil
+              pos reg-beg))
+       ((and blockside
+             (member (get-text-property pos 'block-token) '(string comment))
+             (eq (get-text-property pos 'block-token) (get-text-property (1- pos) 'block-token)))
+        (setq pos (web-mode-block-token-beginning-position pos)))
+       ((and (not blockside)
+             (member (get-text-property pos 'part-token) '(string comment))
+             (eq (get-text-property pos 'part-token) (get-text-property (1- pos) 'part-token)))
+        (setq pos (web-mode-part-token-beginning-position pos)))
+       ((and (not blockside)
+             (get-text-property pos 'block-side))
+        (when (setq pos (web-mode-block-beginning-position pos))
+          (setq pos (1- pos))))
+       ((member char '(?\) ?\] ?\}))
+        (setq pos (web-mode-part-opening-paren-position pos reg-beg))
+        (setq pos (1- pos)))
+       ((member char '(?\( ?\{ ?\[ ?\= ?\? ?\: ?\; ?\, ?\& ?\|))
+        (setq continue nil)
+        (web-mode-looking-at ".[ \t\n]*" pos)
+        (setq pos (+ pos (length (match-string-no-properties 0)))))
+       ((web-mode-looking-back "\\(return\\)[ \n\t]*" pos)
+        (setq continue nil))
+       (t
+        (setq pos (1- pos)))
+       ) ;cond
+      ) ;while
+    ;;(message "js-statement-beg:%S" pos)
+    pos))
+
+;; TODO: reg-beg : jsx-beg
+;; TODO: skipper les expr dont la depth est superieure
+
+;; NOTE: blockside is useful for ejs
+(defun web-mode-javascript-statement-beginning-position (pos &optional reg-beg)
+  (unless pos (setq pos (point)))
+  (setq pos (1- pos))
+  (let ((char nil)
+        (blockside (get-text-property pos 'block-side))
+        (i 0)
+        (is-jsx (string= web-mode-content-type "jsx"))
+        (depth-o nil) (depth-l nil)
+        (continue (not (null pos))))
+    (setq depth-o (get-text-property pos 'jsx-depth))
+    (unless reg-beg
+      (cond
+       (blockside
+        (setq reg-beg (web-mode-block-beginning-position pos)))
+       (is-jsx
+        (setq reg-beg (web-mode-jsx-depth-beginning-position pos)))
+       (t
+        (setq reg-beg (web-mode-part-beginning-position pos)))
+       ) ;cond
+      ) ;unless
+    (while continue
+      (setq char (char-after pos))
+      (cond
+       ((> (setq i (1+ i)) 20000)
+        (message "javascript-statement-beginning-position ** warning (%S) **" pos)
+        (setq continue nil
+              pos nil))
+       ((null pos)
+        (message "javascript-statement-beginning-position ** invalid pos **")
+        (setq continue nil))
+       ((< pos reg-beg)
+        (when (not is-jsx)
+          (message "javascript-statement-beginning-position ** failure **"))
+        (setq continue nil
+              pos reg-beg))
+       ((and is-jsx
+             (progn (setq depth-l (get-text-property pos 'jsx-depth)))
+             (not (eq depth-l depth-o)))
+        ;;(message "%S > depth-o(%S) depth-l(%S)" pos depth-o depth-l)
+        (setq pos (previous-single-property-change pos 'jsx-depth))
+        (setq pos (1- pos))
+        ;;(message "--> %S %S" pos (get-text-property pos 'jsx-depth))
+        )
+       ((and blockside
+             (member (get-text-property pos 'block-token) '(string comment))
+             (eq (get-text-property pos 'block-token) (get-text-property (1- pos) 'block-token)))
+        (setq pos (web-mode-block-token-beginning-position pos)))
+       ((and (not blockside)
+             (member (get-text-property pos 'part-token) '(string comment))
+             (eq (get-text-property pos 'part-token) (get-text-property (1- pos) 'part-token)))
+        (setq pos (web-mode-part-token-beginning-position pos)))
+       ((and (not blockside)
+             (get-text-property pos 'block-side))
+        (when (setq pos (web-mode-block-beginning-position pos))
+          (setq pos (1- pos))))
+       ((member char '(?\) ?\] ?\}))
+        (setq pos (web-mode-part-opening-paren-position pos reg-beg))
+        (setq pos (1- pos)))
+       ((and (eq char ?\=)
+             (web-mode-looking-back "[<>!=]+" pos reg-beg t))
+        (setq pos (- pos 1 (length (match-string-no-properties 0))))
+        ;;(setq pos (1- pos))
+        ;;(message "%S pos=%S" (match-string-no-properties 0) pos)
+        )
+       ((member char '(?\( ?\{ ?\[ ?\=))
+        (setq continue nil)
+        (web-mode-looking-at ".[ \t\n]*" pos)
+        (setq pos (+ pos (length (match-string-no-properties 0)))))
+       ((web-mode-looking-back "\\_<\\(return\\)[ \n\t]*" pos)
+        (setq continue nil)
+        (web-mode-looking-at "[ \t\n]*" pos)
+        (setq pos (+ pos (length (match-string-no-properties 0)))))
+       (t
+        (setq pos (1- pos)))
+       ) ;cond
+      ) ;while
+    ;;(message "%S -------" pos)
+    pos))
+
+(defun web-mode-javascript-args-beginning-position (pos &optional reg-beg)
+  (unless pos (setq pos (point)))
+  (setq pos (1- pos))
+  (let ((char nil)
+        (blockside (get-text-property pos 'block-side))
+        (i 0)
+        (continue (not (null pos))))
+    (unless reg-beg
+      (if blockside
+          (setq reg-beg (web-mode-block-beginning-position pos))
+        (setq reg-beg (web-mode-part-beginning-position pos)))
+      )
+    (while continue
+      (setq char (char-after pos))
+      (cond
+       ((> (setq i (1+ i)) 20000)
+        (message "javascript-args-beginning-position ** warning (%S) **" pos)
+        (setq continue nil
+              pos nil))
+       ((null pos)
+        (message "javascript-args-beginning-position ** invalid pos **")
+        (setq continue nil))
+       ((< pos reg-beg)
+        (message "javascript-args-beginning-position ** failure **")
+        (setq continue nil
+              pos reg-beg))
+       ((and blockside
+             (member (get-text-property pos 'block-token) '(string comment))
+             (eq (get-text-property pos 'block-token) (get-text-property (1- pos) 'block-token)))
+        (setq pos (web-mode-block-token-beginning-position pos)))
+       ((and (not blockside)
+             (member (get-text-property pos 'part-token) '(string comment))
+             (eq (get-text-property pos 'part-token) (get-text-property (1- pos) 'part-token)))
+        (setq pos (web-mode-part-token-beginning-position pos)))
+       ((and (not blockside)
+             (get-text-property pos 'block-side))
+        (when (setq pos (web-mode-block-beginning-position pos))
+          (setq pos (1- pos)))
+        )
+       ((member char '(?\) ?\] ?\}))
+        (when (setq pos (web-mode-part-opening-paren-position pos reg-beg))
+          (setq pos (1- pos))))
+       ((member char '(?\( ?\[ ?\{))
+;;        (web-mode-looking-at ".[ \t\n]*" pos)
+        (web-mode-looking-at ".[ ]*" pos)
+        (setq pos (+ pos (length (match-string-no-properties 0)))
+              continue nil)
+;;        (message "=>%S" pos)
+        )
+       ((web-mode-looking-back "\\_<\\(var\\|let\\|return\\|const\\)[ \n\t]+" pos)
+;;        (web-mode-looking-at "[ \t\n]*" pos)
+        (web-mode-looking-at "[ \t]*" pos)
+        (setq pos (+ pos (length (match-string-no-properties 0)))
+              continue nil))
+       (t
+        (setq pos (1- pos)))
+       ) ;cond
+      ) ;while
+    ;;(message "=%S" pos)
+    pos))
+
+(defun web-mode-javascript-calls-beginning-position (pos &optional reg-beg)
+  (unless pos (setq pos (point)))
+  ;;(message "pos=%S" pos)
+  (let ((char nil)
+        (dot-pos nil)
+        (blockside (get-text-property pos 'block-side))
+        (i 0)
+        (continue (not (null pos))))
+    (unless reg-beg
+      (if blockside
+          (setq reg-beg (web-mode-block-beginning-position pos))
+        (setq reg-beg (web-mode-part-beginning-position pos)))
+      )
+    (while continue
+      (setq char (char-after pos))
+      ;;(message "%S| %S=%c" reg-beg pos char)
+      (cond
+       ((> (setq i (1+ i)) 20000)
+        (message "javascript-calls-beginning-position ** warning (%S) **" pos)
+        (setq continue nil
+              pos nil))
+       ((null pos)
+        (message "javascript-calls-beginning-position ** invalid pos **")
+        (setq continue nil))
+       ((< pos reg-beg)
+        ;;(forward-char)
+        ;;(skip-chars-forward " \t")
+        ;;(message "pos(%S) reg-beg(%S)" pos reg-beg)
+        ;;(message "javascript-calls-beginning-position ** failure **")
+        (setq continue nil
+              pos reg-beg))
+       ((and blockside
+             (member (get-text-property pos 'block-token) '(string comment))
+             (eq (get-text-property pos 'block-token) (get-text-property (1- pos) 'block-token)))
+        (setq pos (web-mode-block-token-beginning-position pos)))
+       ((and (not blockside)
+             (member (get-text-property pos 'part-token) '(string comment))
+             (eq (get-text-property pos 'part-token) (get-text-property (1- pos) 'part-token)))
+        (setq pos (web-mode-part-token-beginning-position pos)))
+       ((and (not blockside)
+             (get-text-property pos 'block-side))
+        (when (setq pos (web-mode-block-beginning-position pos))
+          (setq pos (1- pos))))
+       ;;((member char '(?\) ?\] ?\}))
+       ;;((member char '(?\s ?\t))
+       ;; (skip-chars-backward " \t" reg-beg))
+       ((and (member char '(?\.)) (> i 1))
+        (setq dot-pos pos
+              pos (1- pos)))
+       ((member char '(?\) ?\]))
+        (when (setq pos (web-mode-part-opening-paren-position pos reg-beg))
+          (setq pos (1- pos)))
+        ;;(message "pos=%S" pos)
+        )
+       ((member char '(?\( ?\{ ?\} ?\[ ?\= ?\? ?\: ?\; ?\, ?\& ?\|))
+        (setq continue nil)
+        (web-mode-looking-at ".[ \t\n]*" pos)
+        (setq pos (+ pos (length (match-string-no-properties 0)))))
+       ((web-mode-looking-back "\\_<\\(return\\|else\\)[ \n\t]*" pos)
+        (setq continue nil))
+       (t
+        (setq pos (1- pos)))
+       ) ;cond
+      ) ;while
+    ;;(message "pos=%S dot-pos=%S" pos dot-pos)
+    (if (null pos) pos (cons pos dot-pos))
+    ))
+
+(defun web-mode-part-token-beginning-position (&optional pos)
   (unless pos (setq pos (point)))
   (cond
+   ((not (get-text-property pos 'part-token))
+    nil)
+   ((or (= pos (point-min))
+        (and (> pos (point-min))
+             (not (get-text-property (1- pos) 'part-token))))
+    pos)
+   (t
+    (setq pos (previous-single-property-change pos 'part-token))
+    (if (and pos (> pos (point-min))) pos (point-min)))
+   ))
+
+(defun web-mode-part-token-end-position (&optional pos)
+  (unless pos (setq pos (point)))
+  (cond
+   ((not (get-text-property pos 'part-token))
+    nil)
+   ((or (= pos (point-max))
+        (not (get-text-property (1+ pos) 'part-token)))
+    pos)
+   (t
+    (1- (next-single-property-change pos 'part-token)))
+   ))
+
+(defun web-mode-block-token-beginning-position (&optional pos)
+  (unless pos (setq pos (point)))
+  (cond
+   ((not (get-text-property pos 'block-token))
+    nil)
+   ((or (= pos (point-min))
+        (and (> pos (point-min))
+             (not (get-text-property (1- pos) 'block-token))))
+    pos)
+   (t
+    (setq pos (previous-single-property-change pos 'block-token))
+    (if (and pos (> pos (point-min))) pos (point-min)))
+   ))
+
+(defun web-mode-block-token-end-position (&optional pos)
+  (unless pos (setq pos (point)))
+  (cond
+   ((not (get-text-property pos 'block-token))
+    nil)
+   ((or (= pos (point-max))
+        (not (get-text-property (1+ pos) 'block-token)))
+    pos)
+   (t
+    (1- (next-single-property-change pos 'block-token)))
+   ))
+
+(defun web-mode-block-code-end-position (&optional pos)
+  (unless pos (setq pos (point)))
+  (setq pos (web-mode-block-end-position pos))
+  (cond
+   ((not pos)
+    nil)
+   ((and (eq (get-text-property pos 'block-token) 'delimiter-end)
+         (eq (get-text-property (1- pos) 'block-token) 'delimiter-end))
+    (previous-single-property-change pos 'block-token))
+   ((= pos (1- (point-max))) ;; TODO: comparer plutot avec line-end-position
+    (point-max))
+   (t
+    pos)
+   ))
+
+(defun web-mode-block-end-position (&optional pos)
+  (unless pos (setq pos (point)))
+  (cond
+   ((get-text-property pos 'block-end)
+    pos)
+   ((get-text-property pos 'block-side)
+    (or (next-single-property-change pos 'block-end)
+        (point-max)))
+   (t
+    nil)
+   ))
+
+(defun web-mode-block-previous-position (&optional pos)
+  (unless pos (setq pos (point)))
+  (cond
+   ((= pos (point-min))
+    (setq pos nil))
    ((get-text-property pos 'block-side)
     (setq pos (web-mode-block-beginning-position pos))
-    (when (and pos (> pos (point-min)))
+    (cond
+     ((or (null pos) (= pos (point-min)))
+      (setq pos nil)
+      )
+     ((and (setq pos (previous-single-property-change pos 'block-beg))
+           (> pos (point-min)))
       (setq pos (1- pos))
-      (while (and (> pos (point-min))
-                  (eq (char-after pos) ?\n))
-        (setq pos (1- pos))
-        )
-      ;;            (message "pos=%S  <%c>" pos (char-after pos))
-      (if (get-text-property pos 'block-side)
-          (setq pos (web-mode-block-beginning-position pos))
-        (setq pos (previous-single-property-change pos 'block-side))
-        (when (and pos (> pos (point-min)))
-          (setq pos (web-mode-block-beginning-position (1- pos))))
-        ) ;if
-      ) ;when
+      )
+     )
+    ) ;block-side
+   ((get-text-property (1- pos) 'block-side)
+    (setq pos (web-mode-block-beginning-position (1- pos)))
     )
    (t
     (setq pos (previous-single-property-change pos 'block-side))
-    (when (and pos (> pos (point-min)))
+    (cond
+     ((and (null pos) (get-text-property (point-min) 'block-beg))
+      (setq pos (point-min)))
+     ((and pos (> pos (point-min)))
       (setq pos (web-mode-block-beginning-position (1- pos))))
+     )
     )
    ) ;conf
   pos)
 
-(defun web-mode-block-next-position (&optional pos)
-  "web-mode-block-next-position"
-  (unless pos (setq pos (point)))
-  (if (get-text-property pos 'block-side)
-      (if (= pos (point-min))
-          (set pos (point-min))
-        (setq pos (web-mode-block-end-position pos))
-        (when (and pos (> (point-max) pos))
-          (setq pos (1+ pos))
-          (if (not (get-text-property pos 'block-side))
-              (setq pos (next-single-property-change pos 'block-side)))
-          ) ;when
-        )
-    (setq pos (next-single-property-change pos 'block-side)))
-  pos)
-
-(defun web-mode-part-next-position (&optional pos)
-  "web-mode-part-next-position"
+(defun web-mode-block-next-position (&optional pos limit)
   (unless pos (setq pos (point)))
-  (if (get-text-property pos 'part-side)
-      (if (= pos (point-min))
-          (set pos (point-min))
-        (setq pos (web-mode-part-end-position pos))
-        (when (and pos (> (point-max) pos))
-          (setq pos (1+ pos))
-          (if (not (get-text-property pos 'part-side))
-              (setq pos (next-single-property-change pos 'part-side)))
-          ) ;when
-        )
-    (setq pos (next-single-property-change pos 'part-side)))
-  pos)
-
-;;--- /positions
-
-;;--- nav
-
-(defun web-mode-tag-beginning (&optional pos)
-  "Fetch html tag beg."
+  (unless limit (setq limit (point-max)))
+  (cond
+   ((and (get-text-property pos 'block-side)
+         (setq pos (web-mode-block-end-position pos))
+         (< pos (point-max))
+         (setq pos (1+ pos)))
+    (unless (get-text-property pos 'block-beg)
+      (setq pos (next-single-property-change pos 'block-side)))
+    )
+   (t
+    (setq pos (next-single-property-change pos 'block-side)))
+   ) ;cond
+  (if (and pos (<= pos limit)) pos nil))
+
+;;---- EXCURSION ---------------------------------------------------------------
+
+(defun web-mode-backward-sexp (n)
+  (interactive "p")
+  (if (< n 0) (web-mode-forward-sexp (- n))
+    (let (pos)
+      (dotimes (_ n)
+        (skip-chars-backward "[:space:]")
+        (setq pos (point))
+        (cond
+         ((bobp) nil)
+         ((get-text-property (1- pos) 'block-end)
+          (backward-char 1)
+          (web-mode-block-beginning))
+         ((get-text-property (1- pos) 'block-token)
+          (backward-char 1)
+          (web-mode-block-token-beginning))
+         ((get-text-property (1- pos) 'part-token)
+          (backward-char 1)
+          (web-mode-part-token-beginning))
+         ((get-text-property (1- pos) 'tag-end)
+          (backward-char 1)
+          (web-mode-element-beginning))
+         ((get-text-property (1- pos) 'tag-attr)
+          (backward-char 1)
+          (web-mode-attribute-beginning))
+         ((get-text-property (1- pos) 'tag-type)
+          (backward-char 1)
+          (web-mode-tag-beginning))
+         ((get-text-property (1- pos) 'jsx-end)
+          (backward-char 1)
+          (web-mode-jsx-beginning))
+         (t
+          (let ((forward-sexp-function nil))
+            (backward-sexp))
+          ) ;case t
+         ) ;cond
+        ) ;dotimes
+      ))) ;let if defun
+
+(defun web-mode-forward-sexp (n)
+  (interactive "p")
+  (if (< n 0) (web-mode-backward-sexp (- n))
+    (let (pos)
+      (dotimes (_ n)
+        (skip-chars-forward "[:space:]")
+        (setq pos (point))
+        (cond
+         ((eobp) nil)
+         ((get-text-property pos 'block-beg)
+          (web-mode-block-end))
+         ((get-text-property pos 'block-token)
+          (web-mode-block-token-end))
+         ((get-text-property pos 'part-token)
+          (web-mode-part-token-end))
+         ((get-text-property pos 'tag-beg)
+          (web-mode-element-end))
+         ((get-text-property pos 'tag-attr)
+          (web-mode-attribute-end))
+         ((get-text-property pos 'tag-type)
+          (web-mode-tag-end))
+         ((get-text-property pos 'jsx-beg)
+          (web-mode-jsx-end))
+         (t
+          (let ((forward-sexp-function nil))
+            (forward-sexp))
+          ) ;case t
+         ) ;cond
+        ) ;dotimes
+      ))) ;let if defun
+
+(defun web-mode-comment-beginning ()
+  "Fetch current comment beg."
   (interactive)
-  (unless pos (setq pos (point)))
-  (setq pos (web-mode-tag-beginning-position pos))
-  (when pos (goto-char pos))
-  pos)
-
-(defun web-mode-tag-end (&optional pos)
-  "Fetch html tag end."
+  (web-mode-go (web-mode-comment-beginning-position (point))))
+
+(defun web-mode-comment-end ()
+  "Fetch current comment end."
   (interactive)
-  (unless pos (setq pos (point)))
-  (setq pos (web-mode-tag-end-position pos))
-  (when pos
-    (setq pos (1+ pos))
-    (goto-char pos))
-  pos)
-
-(defun web-mode-tag-previous (&optional pos)
+  (web-mode-go (web-mode-comment-end-position (point)) 1))
+
+(defun web-mode-tag-beginning ()
+  "Fetch current html tag beg."
+  (interactive)
+  (web-mode-go (web-mode-tag-beginning-position (point))))
+
+(defun web-mode-tag-end ()
+  "Fetch current html tag end."
+  (interactive)
+  (web-mode-go (web-mode-tag-end-position (point)) 1))
+
+(defun web-mode-tag-previous ()
   "Fetch previous tag."
   (interactive)
-  (unless pos (setq pos (point)))
-  (if (bobp)
-      (setq pos nil)
-    (when (get-text-property pos 'tag-beg)
-      (setq pos (1- pos)))
-    (setq pos (previous-single-property-change pos 'tag-beg))
-    (when pos
-      (setq pos (1- pos))
-      (goto-char pos))
-    )
-  pos)
-
-(defun web-mode-tag-next (&optional pos)
-  "Fetch next tag. Might be HTML comment or server tag (ie. JSP)."
+  (web-mode-go (web-mode-tag-previous-position (point))))
+
+(defun web-mode-tag-next ()
+  "Fetch next tag. Might be html comment or server tag (e.g. jsp)."
   (interactive)
-  (unless pos (setq pos (point)))
-  (if (eobp)
-      (setq pos nil)
-    (when (get-text-property pos 'tag-beg)
-      (setq pos (1+ pos)))
-    (setq pos (next-single-property-change pos 'tag-beg))
-    (when pos (goto-char pos)))
-  pos)
-
-(defun web-mode-skip-html-tag (&optional back bound context)
-  "Skip html tag. (smartparens helper)"
+  (web-mode-go (web-mode-tag-next-position (point))))
+
+(defun web-mode-attribute-beginning ()
+  "Fetch html attribute beginning."
   (interactive)
-  (let ((pos (point)) mb me skipped back delim)
-
-    (cond
-     (back
-      (unless (or (bobp)
-                  (get-text-property (1- pos) 'tag-end))
-        (when (web-mode-tag-previous)
-          (web-mode-tag-end)
-          )
-        ) ;unless
-      )
-     (t
-      (unless (or (eobp)
-                  (get-text-property pos 'tag-beg))
-        (web-mode-tag-next))
-      )
-     ) ;cond
-
-    (cond
-     ((get-text-property (point) 'tag-beg)
-      (setq mb (point)
-            me (1+ (web-mode-tag-end-position)))
-      )
-     ((and (not (bobp))
-           (get-text-property (1- (point)) 'tag-end))
-      (setq mb (point)
-            me (web-mode-tag-beginning-position (1- (point))))
-      )
-     ) ;cond
-
-    (if (and mb me)
-        (progn
-          (setq skipped (- (point) pos))
-          (setq delim (buffer-substring-no-properties mb me))
-          ;;    (message "%S" (list :mb mb :me me :skipped skipped :back back :delim delim))
-          (list :mb mb :me me :skipped skipped :back back :delim delim)
-          )
-      nil)
-
-    ))
-
-(defun web-mode-get-html-tag (&optional back bound context)
-  "Get html tag. (smartparens helper)"
+  (web-mode-go (web-mode-attribute-beginning-position (point))))
+
+(defun web-mode-attribute-end ()
+  "Fetch html attribute end."
   (interactive)
-  (let (ctx beg end (pos (point)))
-    (message "pos=%S" pos)
-    (setq ctx (web-mode-skip-html-tag back bound context))
-    (message "ctx=%S" ctx)
-    (cond
-     ((null ctx)
-;;      (message "ici")
+  (web-mode-go (web-mode-attribute-end-position (point)) 1))
+
+(defun web-mode-attribute-next (&optional arg)
+  "Fetch next attribute."
+  (interactive "p")
+  (unless arg (setq arg 1))
+  (cond
+   ((= arg 1) (web-mode-go (web-mode-attribute-next-position (point))))
+   ((< arg 1) (web-mode-element-previous (* arg -1)))
+   (t
+    (while (>= arg 1)
+      (setq arg (1- arg))
+      (web-mode-go (web-mode-attribute-next-position (point)))
       )
-     ((get-text-property (point) 'tag-beg)
-      (setq beg (point)
-            end (1+ (web-mode-tag-end-position)))
-      )
-     ((get-text-property (1- (point)) 'tag-end)
-      (setq beg (web-mode-tag-end-position (1- (point)))
-            end (point))
-      )
-     )
-    (if (null ctx) nil
-;;      (message "%S" (list :beg beg :end end :op "<" :cl ">" :prefix "" :suffix "" :from pos))
-      (list :beg beg :end end :op "<" :cl ">" :prefix "" :suffix "" :from pos))
-    ))
-
-(defun web-mode-tag-get (&optional pos)
-  "Tag get"
-  (unless pos (setq pos (point)))
-  (let (out)
-    (cond
-     ((get-text-property pos 'tag-name)
-      (setq out (buffer-substring-no-properties (web-mode-tag-beginning-position)
-                                                (web-mode-tag-end-position)))
-      )
-     (t
-      (setq out "")
+    )
+   )
+  )
+
+(defun web-mode-attribute-previous (&optional arg)
+  "Fetch previous attribute."
+  (interactive "p")
+  (unless arg (setq arg 1))
+  (unless arg (setq arg 1))
+  (cond
+   ((= arg 1) (web-mode-go (web-mode-attribute-previous-position (point))))
+   ((< arg 1) (web-mode-element-next (* arg -1)))
+   (t
+    (while (>= arg 1)
+      (setq arg (1- arg))
+      (web-mode-go (web-mode-attribute-previous-position (point)))
       )
-     ) ;cond
-    out
-    ))
-
-(defun web-mode-attr-next (&optional pos)
-  "Fetch next attr."
-  (interactive)
-  (let ((continue t))
-    (unless pos (setq pos (point)))
-    (while continue
-      (setq pos (next-single-property-change pos 'tag-attr))
-;;      (message "pos=%S" pos)
-      (cond
-       ((null pos)
-        (setq continue nil
-              pos nil))
-       ((get-text-property pos 'tag-attr)
-        (setq continue nil)
-        )
-       )
-      ) ;while
-    (when pos (goto-char pos))
-    pos))
-
-(defun web-mode-element-previous ()
+    )
+   )
+  )
+
+(defun web-mode-element-previous (&optional arg)
   "Fetch previous element."
-  (interactive)
-  (let (continue ret (pos (point)) (props '(start void)))
-    (setq continue (not (bobp)))
-    (while continue
-      (setq ret (web-mode-tag-previous))
-      (when (or (null ret)
-                (member (get-text-property (point) 'tag-type) props))
-        (setq continue nil)
-        )
+  (interactive "p")
+  (unless arg (setq arg 1))
+  (cond
+   ((= arg 1) (web-mode-go (web-mode-element-previous-position (point))))
+   ((< arg 1) (web-mode-element-next (* arg -1)))
+   (t
+    (while (>= arg 1)
+      (setq arg (1- arg))
+      (web-mode-go (web-mode-element-previous-position (point)))
       ) ;while
-    (unless ret (goto-char pos))
-    ret))
-
-(defun web-mode-element-next ()
+    ) ;t
+   ) ;cond
+  )
+
+(defun web-mode-element-next (&optional arg)
   "Fetch next element."
-  (interactive)
-  (let (continue ret (pos (point)) (props '(start void)))
-    (setq continue (not (eobp)))
-    (while continue
-      (setq ret (web-mode-tag-next))
-      (when (or (null ret)
-                (member (get-text-property (point) 'tag-type) props))
-        (setq continue nil)
-        )
+  (interactive "p")
+  (unless arg (setq arg 1))
+  (cond
+   ((= arg 1) (web-mode-go (web-mode-element-next-position (point))))
+   ((< arg 1) (web-mode-element-previous (* arg -1)))
+   (t
+    (while (>= arg 1)
+      (setq arg (1- arg))
+      (web-mode-go (web-mode-element-next-position (point)))
       ) ;while
-    (unless ret (goto-char pos))
-    ret))
+    ) ;t
+   ) ;cond
+  )
 
 (defun web-mode-element-sibling-next ()
-  "Fetch next element."
+  "Fetch next sibling element."
   (interactive)
-  (let (parent ret (pos (point)))
+  (let ((pos (point)))
     (save-excursion
       (cond
        ((not (get-text-property pos 'tag-type))
-        (when (and (web-mode-element-parent)
-                   (web-mode-html-tag-match)
-                   (web-mode-element-next))
-          (setq ret (point))
-          )
+        (if (and (web-mode-element-parent)
+                 (web-mode-tag-match)
+                 (web-mode-element-next))
+            (setq pos (point))
+          (setq pos nil))
         )
        ((eq (get-text-property pos 'tag-type) 'start)
-        (when (and (web-mode-html-tag-match)
-                   (web-mode-element-next))
-          (setq ret (point))
-          )
-        )
+        (if (and (web-mode-tag-match)
+                 (web-mode-element-next))
+            (setq pos (point))
+          (setq pos nil))
+        )
+       ((web-mode-element-next)
+        (setq pos (point)))
        (t
-        (when (web-mode-element-next)
-          (setq ret (point))
-          )
-        )
+        (setq pos nil))
        ) ;cond
-
-      ) ;save
-    (if ret (goto-char ret))
-    ))
-
-
-(defun web-mode-element-beginning (&optional pos)
+      ) ;save-excursion
+    (web-mode-go pos)))
+
+(defun web-mode-element-sibling-previous ()
+  "Fetch previous sibling element."
+  (interactive)
+  (let ((pos (point)))
+    (save-excursion
+      (cond
+       ((not (get-text-property pos 'tag-type))
+        (if (and (web-mode-element-parent)
+                 (web-mode-tag-previous)
+                 (web-mode-element-beginning))
+            (setq pos (point))
+          (setq pos nil))
+        )
+       ((eq (get-text-property pos 'tag-type) 'start)
+        (if (and (web-mode-tag-beginning)
+                 (web-mode-tag-previous)
+                 (web-mode-element-beginning))
+            (setq pos (point))
+          (setq pos nil))
+        )
+       ((and (web-mode-element-beginning)
+             (web-mode-tag-previous)
+             (web-mode-element-beginning))
+        (setq pos (point)))
+       (t
+        (setq pos nil))
+       ) ;cond
+      ) ;save-excursion
+    (web-mode-go pos)))
+
+(defun web-mode-element-beginning ()
   "Move to beginning of element."
   (interactive)
-  (unless pos (setq pos (point)))
-  (setq pos (web-mode-element-beginning-position pos))
-  (when pos (goto-char pos))
-  pos)
-
-(defun web-mode-element-end (&optional pos)
+  (web-mode-go (web-mode-element-beginning-position (point))))
+
+(defun web-mode-element-end ()
   "Move to end of element."
   (interactive)
-  (unless pos (setq pos (point)))
-  (setq pos (web-mode-element-end-position pos))
-  (when pos
-    (setq pos (1+ pos))
-    (goto-char pos))
-  pos)
-
-(defun web-mode-element-parent (&optional pos)
+  (web-mode-go (web-mode-element-end-position (point)) 1))
+
+(defun web-mode-element-parent ()
   "Fetch parent element."
   (interactive)
-  (unless pos (setq pos (point)))
-  (setq pos (web-mode-element-parent-position pos))
-  (when pos (goto-char pos))
-  pos)
-
-(defun web-mode-element-child (&optional pos)
+  (web-mode-go (web-mode-element-parent-position (point))))
+
+(defun web-mode-element-child ()
   "Fetch child element."
   (interactive)
-  (unless pos (setq pos (point)))
-  (setq pos (web-mode-element-child-position pos))
-  (when pos (goto-char pos))
-  pos)
+  (web-mode-go (web-mode-element-child-position (point))))
 
 (defun web-mode-dom-traverse ()
   "Traverse html dom tree."
@@ -9016,124 +11244,155 @@
     )
    ((web-mode-element-sibling-next)
     )
-   ((web-mode-element-parent)
-    (unless (web-mode-element-sibling-next)
-      (goto-char (point-min)))
-    )
+   ((and (web-mode-element-parent)
+         (not (web-mode-element-sibling-next)))
+    (goto-char (point-min)))
+   (t
+    (goto-char (point-min)))
    ) ;cond
   )
 
-(defun web-mode-block-close (&optional pos)
-  "Close the first opened control block."
+(defun web-mode-closing-paren (limit)
+  (let ((pos (web-mode-closing-paren-position (point) limit)))
+    (if (or (null pos) (> pos limit))
+        nil
+      (goto-char pos)
+      pos)
+    ))
+
+(defun web-mode-part-next ()
+  "Move point to the beginning of the next part."
   (interactive)
-  (unless pos (setq pos (point)))
-  (let ((continue t) ctx h ctrl n closing-block)
-    (save-excursion
-      (setq h (make-hash-table :test 'equal))
-      (while (and continue (web-mode-block-previous))
-        (when (setq ctx (web-mode-is-active-block (point)))
-          (setq ctrl (car ctx))
-          (setq n (gethash ctrl h 0))
-          (if (cdr ctx)
-              (puthash ctrl (1+ n) h)
-            (puthash ctrl (1- n) h)
-            )
-          (when (> (gethash ctrl h) 0)
-            (setq continue nil))
-;;          (if ctx (message "(%S) %S : %S" (point) ctrl (gethash ctrl h)))
-          )
-        ) ;while
-      ) ;save-excursion
-    (when (and (null continue)
-               (setq closing-block (web-mode-closing-block ctrl)))
-      (insert closing-block)
-      (indent-for-tab-command)
-      )
-    ))
-
-(defun web-mode-closing-block (type)
-  "Return the closing block corresponding to TYPE"
-  (cond
-   ((string= web-mode-engine "django")
-    (concat "{% end" type " %}"))
-   ((string= web-mode-engine "ctemplate")
-    (concat "{{/" type "}}"))
-   ((string= web-mode-engine "blade")
-    (concat "@end" type))
-   ((string= web-mode-engine "dust")
-    (concat "{/" type "}"))
-   ((string= web-mode-engine "underscore")
-    "<% } %>")
-   ((string= web-mode-engine "erb")
-    "<% end %>")
-   (t
-    (cdr (assoc type web-mode-closing-blocks)))
-   ) ;cond
-  )
-
-(defun web-mode-block-previous (&optional pos)
+  (web-mode-go (web-mode-part-next-position (point))))
+
+(defun web-mode-part-beginning ()
+  "Move point to the beginning of the current part."
+  (interactive)
+  (web-mode-go (web-mode-part-beginning-position (point))))
+
+(defun web-mode-part-end ()
+  "Move point to the end of the current part."
+  (interactive)
+  (web-mode-go (web-mode-part-end-position (point)) 1))
+
+(defun web-mode-block-previous ()
   "Move point to the beginning of the previous block."
   (interactive)
-  (unless pos (setq pos (point)))
-  (setq pos (web-mode-block-previous-position pos))
-  (when pos (goto-char pos))
-  pos)
-
-(defun web-mode-block-next (&optional pos)
+  (web-mode-go (web-mode-block-previous-position (point))))
+
+(defun web-mode-block-next ()
   "Move point to the beginning of the next block."
   (interactive)
-  (unless pos (setq pos (point)))
-  (setq pos (web-mode-block-next-position pos))
-  (when pos (goto-char pos))
-  pos)
-
-(defun web-mode-part-next (&optional pos)
-  "Move point to the beginning of the next part."
-  (interactive)
-  (unless pos (setq pos (point)))
-  (setq pos (web-mode-part-next-position pos))
-  (when pos (goto-char pos))
-  pos)
-
-(defun web-mode-block-beginning (&optional pos)
+  (web-mode-go (web-mode-block-next-position (point))))
+
+(defun web-mode-block-beginning ()
   "Move point to the beginning of the current block."
   (interactive)
+  (web-mode-go (web-mode-block-beginning-position (point))))
+
+(defun web-mode-block-end ()
+  "Move point to the end of the current block."
+  (interactive)
+  (web-mode-go (web-mode-block-end-position (point)) 1))
+
+(defun web-mode-block-token-beginning ()
+  (web-mode-go (web-mode-block-token-beginning-position (point))))
+
+(defun web-mode-block-token-end ()
+  (web-mode-go (web-mode-block-token-end-position (point)) 1))
+
+(defun web-mode-part-token-beginning ()
+  (web-mode-go (web-mode-part-token-beginning-position (point))))
+
+(defun web-mode-part-token-end ()
+  (web-mode-go (web-mode-part-token-end-position (point)) 1))
+
+(defun web-mode-block-opening-paren (limit)
+  (web-mode-go (web-mode-block-opening-paren-position (point) limit)))
+
+(defun web-mode-block-string-beginning (&optional pos block-beg)
   (unless pos (setq pos (point)))
-  (setq pos (web-mode-block-beginning-position pos))
-  (when pos (goto-char pos))
-  pos)
-
-(defun web-mode-block-end (&optional pos)
-  "web-mode-block-beg"
-  (interactive)
+  (unless block-beg (setq block-beg (web-mode-block-beginning-position pos)))
+  (web-mode-go (web-mode-block-string-beginning-position pos block-beg)))
+
+(defun web-mode-block-statement-beginning (&optional pos block-beg)
+  (unless pos (setq pos (point)))
+  (unless block-beg (setq block-beg (web-mode-block-beginning-position pos)))
+  (web-mode-go (web-mode-block-statement-beginning-position pos block-beg)))
+
+(defun web-mode-block-args-beginning (&optional pos block-beg)
+  (unless pos (setq pos (point)))
+  (unless block-beg (setq block-beg (web-mode-block-beginning-position pos)))
+  (web-mode-go (web-mode-block-args-beginning-position pos block-beg)))
+
+(defun web-mode-block-calls-beginning (&optional pos block-beg)
+  (unless pos (setq pos (point)))
+  (unless block-beg (setq block-beg (web-mode-block-beginning-position pos)))
+  (web-mode-go (web-mode-block-calls-beginning-position pos block-beg)))
+
+(defun web-mode-javascript-string-beginning (&optional pos reg-beg)
+  (unless pos (setq pos (point)))
+  (unless reg-beg
+    (if (get-text-property pos 'block-side)
+        (setq reg-beg (web-mode-block-beginning-position pos))
+      (setq reg-beg (web-mode-part-beginning-position pos))))
+  (web-mode-go (web-mode-javascript-string-beginning-position pos reg-beg)))
+
+(defun web-mode-javascript-statement-beginning (&optional pos reg-beg)
   (unless pos (setq pos (point)))
-  (setq pos (web-mode-block-end-position pos))
+  (unless reg-beg
+    (if (get-text-property pos 'block-side)
+        (setq reg-beg (web-mode-block-beginning-position pos))
+      (setq reg-beg (web-mode-part-beginning-position pos))))
+  (web-mode-go (web-mode-javascript-statement-beginning-position pos reg-beg)))
+
+(defun web-mode-javascript-args-beginning (&optional pos reg-beg)
+  (unless pos (setq pos (point)))
+  (unless reg-beg
+    (if (get-text-property pos 'block-side)
+        (setq reg-beg (web-mode-block-beginning-position pos))
+      (setq reg-beg (web-mode-part-beginning-position pos))))
+  (web-mode-go (web-mode-javascript-args-beginning-position pos reg-beg)))
+
+(defun web-mode-javascript-calls-beginning (&optional pos reg-beg)
+  (unless pos (setq pos (point)))
+  (unless reg-beg
+    (if (get-text-property pos 'block-side)
+        (setq reg-beg (web-mode-block-beginning-position pos))
+      (setq reg-beg (web-mode-part-beginning-position pos))))
+  (let (pair)
+    (setq pair (web-mode-javascript-calls-beginning-position pos reg-beg))
+    (when pair (web-mode-go (car pair)))
+    ))
+
+(defun web-mode-go (pos &optional offset)
+  (unless offset (setq offset 0))
   (when pos
-    (setq pos (1+ pos))
+    (cond
+     ((and (> offset 0) (<= (+ pos offset) (point-max)))
+      (setq pos (+ pos offset)))
+     ((and (< offset 0) (>= (+ pos offset) (point-min)))
+      (setq pos (+ pos offset)))
+     ) ;cond
     (goto-char pos))
   pos)
 
-;;--- /nav ----------------------------------------------------------------------
-
-;;--- search
+;;---- SEARCH ------------------------------------------------------------------
 
 (defun web-mode-rsf-balanced (regexp-open regexp-close &optional limit noerror)
-  "web-mode-rsf-balanced in client."
   (unless noerror (setq noerror t))
   (let ((continue t)
         (level 1)
+        (pos (point))
         ret
         (regexp (concat regexp-open "\\|" regexp-close)))
-;;    (message "regexp=%S" regexp)
     (while continue
       (setq ret (re-search-forward regexp limit noerror))
-;;      (message "regexp=%S ret=%S pos=%S" regexp ret (point))
       (cond
        ((null ret)
         (setq continue nil)
         )
        (t
-;;        (message "%S" (match-string-no-properties 0))
         (if (string-match-p regexp-open (match-string-no-properties 0))
             (setq level (1+ level))
           (setq level (1- level)))
@@ -9143,46 +11402,154 @@
         ) ;t
        ) ;cond
       ) ;while
+    (when (not (= level 0)) (goto-char pos))
     ret))
 
-(defun web-mode-sb-client (regexp &optional limit noerror)
-  "search-backward in client."
+(defun web-mode-block-sb (expr &optional limit noerror)
+  (unless limit (setq limit (web-mode-block-beginning-position (point))))
   (unless noerror (setq noerror t))
   (let ((continue t) ret)
     (while continue
-      (setq ret (search-backward regexp limit noerror))
-      (if (or (null ret)
-              (not (get-text-property (point) 'block-side)))
-          (setq continue nil))
-      )
+      (setq ret (search-backward expr limit noerror))
+      (when (or (null ret)
+                (not (get-text-property (point) 'block-token)))
+        (setq continue nil)
+        ) ;when
+      ) ;while
     ret))
 
-(defun web-mode-rsb-client (regexp &optional limit noerror)
-  "re-search-backward outside blocks."
+(defun web-mode-block-sf (expr &optional limit noerror)
+  (unless limit (setq limit (web-mode-block-end-position (point))))
+  (unless noerror (setq noerror t))
+  (let ((continue t) ret)
+    (while continue
+      (setq ret (search-forward expr limit noerror))
+      (when (or (null ret)
+                (not (get-text-property (point) 'block-token)))
+        (setq continue nil)
+        ) ;when
+      ) ;while
+    ret))
+
+(defun web-mode-block-rsb (regexp &optional limit noerror)
+  (unless limit (setq limit (web-mode-block-beginning-position (point))))
   (unless noerror (setq noerror t))
   (let ((continue t) ret)
     (while continue
       (setq ret (re-search-backward regexp limit noerror))
-      (if (or (null ret)
-              (not (get-text-property (point) 'block-side)))
-          (setq continue nil))
-      )
+      (when (or (null ret)
+                (not (get-text-property (point) 'block-token)))
+        (setq continue nil)
+        ) ;when
+      ) ;while
     ret))
 
-(defun web-mode-rsf-client (regexp &optional limit noerror)
-  "re-search-forward outside blocks."
+(defun web-mode-block-rsf (regexp &optional limit noerror)
+  (unless limit (setq limit (web-mode-block-end-position (point))))
   (unless noerror (setq noerror t))
   (let ((continue t) ret)
     (while continue
       (setq ret (re-search-forward regexp limit noerror))
-      (if (or (null ret)
-              (not (get-text-property (match-beginning 0) 'block-side)))
-          (setq continue nil))
-      )
+      (when (or (null ret)
+                (not (get-text-property (point) 'block-token)))
+        (setq continue nil)
+        ) ;when
+      ) ;while
+    ret))
+
+(defun web-mode-part-sb (expr &optional limit noerror)
+  (unless limit (setq limit (web-mode-part-beginning-position (point))))
+  (unless noerror (setq noerror t))
+  (let ((continue t) ret)
+    (while continue
+      (setq ret (search-backward expr limit noerror))
+      (when (or (null ret)
+                (and (not (get-text-property (point) 'part-token))
+                     (not (get-text-property (point) 'block-side)))
+                )
+        (setq continue nil)
+        ) ;when
+      ) ;while
+    ret))
+
+(defun web-mode-part-sf (expr &optional limit noerror)
+  (unless limit (setq limit (web-mode-part-end-position (point))))
+  (unless noerror (setq noerror t))
+  (let ((continue t) ret)
+    (while continue
+      (setq ret (search-forward expr limit noerror))
+      (when (or (null ret)
+                (and (not (get-text-property (point) 'part-token))
+                     (not (get-text-property (point) 'block-side)))
+                )
+        (setq continue nil)
+        ) ;when
+      ) ;while
     ret))
 
-(defun web-mode-sf-client (expr &optional limit noerror)
-  "search-forward outside blocks."
+(defun web-mode-part-rsb (regexp &optional limit noerror)
+  (unless limit (setq limit (web-mode-part-beginning-position (point))))
+  (unless noerror (setq noerror t))
+  (let ((continue t) ret)
+    (while continue
+      (setq ret (re-search-backward regexp limit noerror))
+      (when (or (null ret)
+                (and (not (get-text-property (point) 'part-token))
+                     (not (get-text-property (point) 'block-side)))
+                )
+        (setq continue nil)
+        ) ;when
+      ) ;while
+    ret))
+
+(defun web-mode-part-rsf (regexp &optional limit noerror)
+  (unless limit (setq limit (web-mode-part-end-position (point))))
+  (unless noerror (setq noerror t))
+  (let ((continue t) ret)
+    (while continue
+      (setq ret (re-search-forward regexp limit t))
+      (when (or (null ret)
+                (and (not (get-text-property (point) 'part-token))
+                     (not (get-text-property (point) 'block-side)))
+                )
+        (setq continue nil)
+        ) ;when
+      ) ;while
+    ret))
+
+(defun web-mode-javascript-rsb (regexp &optional limit noerror)
+  (unless limit (setq limit (web-mode-part-beginning-position (point))))
+  (unless noerror (setq noerror t))
+  (let ((continue t) ret)
+    (while continue
+      (setq ret (re-search-backward regexp limit noerror))
+      (when (or (null ret)
+                (and (not (get-text-property (point) 'part-token))
+                     (not (get-text-property (point) 'block-side))
+                     (not (get-text-property (point) 'jsx-depth)))
+                )
+        (setq continue nil)
+        ) ;when
+      ) ;while
+    ret))
+
+(defun web-mode-javascript-rsf (regexp &optional limit noerror)
+  (unless limit (setq limit (web-mode-part-end-position (point))))
+  (unless noerror (setq noerror t))
+  (let ((continue t) ret)
+    (while continue
+      (setq ret (re-search-forward regexp limit t))
+      (when (or (null ret)
+                (and (not (get-text-property (point) 'part-token))
+                     (not (get-text-property (point) 'block-side))
+                     (not (get-text-property (point) 'jsx-depth)))
+                )
+        (setq continue nil)
+        ) ;when
+      ) ;while
+    ret))
+
+(defun web-mode-dom-sf (expr &optional limit noerror)
   (unless noerror (setq noerror t))
   (let ((continue t) ret)
     (while continue
@@ -9193,8 +11560,25 @@
       )
     ret))
 
+(defun web-mode-dom-rsf (regexp &optional limit noerror)
+  (unless noerror (setq noerror t))
+  (let ((continue t) (ret nil))
+    (while continue
+      (setq ret (re-search-forward regexp limit noerror))
+      ;;      (message "ret=%S point=%S limit=%S i=%S" ret (point) limit 0)
+      (cond
+       ((null ret)
+        (setq continue nil))
+       ((or (get-text-property (match-beginning 0) 'block-side)
+            (get-text-property (match-beginning 0) 'part-token))
+        )
+       (t
+        (setq continue nil))
+       ) ;cond
+      ) ;while
+    ret))
+
 (defun web-mode-rsb (regexp &optional limit noerror)
-  "re-search-backward not in comment or string."
   (unless noerror (setq noerror t))
   (let ((continue t) ret)
     (while continue
@@ -9205,7 +11589,6 @@
     ret))
 
 (defun web-mode-rsf (regexp &optional limit noerror)
-  "re-search-forward not in comment or string."
   (unless noerror (setq noerror t))
   (let ((continue t) ret)
     (while continue
@@ -9217,7 +11600,6 @@
     ret))
 
 (defun web-mode-sb (expr &optional limit noerror)
-  "re-search-backward not in comment or string."
   (unless noerror (setq noerror t))
   (let ((continue t) ret)
     (while continue
@@ -9228,7 +11610,6 @@
     ret))
 
 (defun web-mode-sf (expr &optional limit noerror)
-  "re-search-backward not in comment or string."
   (unless noerror (setq noerror t))
   (let ((continue t) ret)
     (while continue
@@ -9238,179 +11619,464 @@
           (setq continue nil)))
     ret))
 
-(defun web-mode-rsb-html (regexp &optional limit noerror)
-  "re-search-backward only in html."
-  (unless noerror (setq noerror t))
-  (let ((continue t) ret)
-    (while continue
-      (setq ret (re-search-backward regexp limit noerror))
-      (if (or (null ret)
-              (not (web-mode-is-part-token-or-server)))
-          (setq continue nil)))
-    ret))
-
-(defun web-mode-rsf-html (regexp &optional limit noerror)
-  "re-search-forward only in html."
-  (unless noerror (setq noerror t))
-  (let ((continue t) ret)
-    (while continue
-      (setq ret (re-search-forward regexp limit noerror))
-      (if (or (null ret)
-              (not (web-mode-is-part-token-or-server)))
-          (setq continue nil)))
-    ret))
-
-(defun web-mode-rsf-content (regexp &optional limit noerror)
-  "re-search-forward only in html content."
+(defun web-mode-content-rsf (regexp &optional limit noerror)
   (unless noerror (setq noerror t))
   (let ((continue t) ret beg end)
     (while continue
       (setq ret (re-search-forward regexp limit noerror)
             beg (if (null ret) (point) (match-beginning 0))
             end (if (null ret) (point) (1- (match-end 0))))
-;;      (message "pt=%S" pos)
       (if (or (null ret)
               (and (web-mode-is-content beg)
                    (web-mode-is-content end)))
           (setq continue nil)))
     ret))
 
-(defun web-mode-is-html-tag (&optional pos)
-  "Is point in an html tag."
-  (unless pos (setq pos (point)))
-  (member (get-text-property pos 'tag-type) '(start end void)))
-
-(defun web-mode-is-comment-or-string-line ()
-  "Detect if current line is in a comment or in a string."
-  (save-excursion
-    (let ((continue t) (counter 0))
-      (beginning-of-line)
-      (while (and continue (not (eolp)))
-        (if (web-mode-is-comment-or-string)
-            (setq counter (1+ counter))
-          (when (not (eq ?\s (following-char)))
-            (setq continue nil
-                  counter 0))
-          ) ;if
-        (forward-char)
-        ) ;while
-      (> counter 0)
-      )))
-
-(defun web-mode-is-part-token-or-server (&optional pos)
-  "Detect if POS is in a comment, a string or in server script."
-  (unless pos (setq pos (point)))
-  (not (null (or (get-text-property pos 'block-side)
-                 (get-text-property pos 'part-token))))
-  )
-
-(defun web-mode-is-part-token-line ()
-  "Detect if current line has only client tokens (string/comment) or server blocks."
+;;---- ADVICES -----------------------------------------------------------------
+
+(defadvice ac-start (before web-mode-set-up-ac-sources activate)
+  "Set `ac-sources' based on current language before running auto-complete."
+  (when (equal major-mode 'web-mode)
+    ;; set ignore each time to nil. User has to implement a hook to change it
+    ;; for each completion
+    (setq web-mode-ignore-ac-start-advice nil)
+    (run-hooks 'web-mode-before-auto-complete-hooks)
+    (unless web-mode-ignore-ac-start-advice
+      (when web-mode-ac-sources-alist
+        (let ((new-web-mode-ac-sources
+               (assoc (web-mode-language-at-pos)
+                      web-mode-ac-sources-alist)))
+          (setq ac-sources (cdr new-web-mode-ac-sources)))))))
+
+;;---- MINOR MODE ADDONS -------------------------------------------------------
+
+(defun web-mode-yasnippet-exit-hook ()
+  "Yasnippet exit hook"
+  (when (and (boundp 'yas-snippet-beg) (boundp 'yas-snippet-end))
+    (indent-region yas-snippet-beg yas-snippet-end)))
+
+(defun web-mode-imenu-index ()
+  (interactive)
+  "Returns imenu items."
+  (let (toc-index
+        line)
+    (save-excursion
+      (goto-char (point-min))
+      (while (not (eobp))
+        (setq line (buffer-substring-no-properties
+                    (line-beginning-position)
+                    (line-end-position)))
+        (let (found
+              (i 0)
+              item
+              regexp
+              type
+              type-idx
+              content
+              content-idx
+              content-regexp
+              close-tag-regexp
+              concat-str
+              jumpto
+              str)
+          (while (and (not found ) (< i (length web-mode-imenu-regexp-list)))
+            (setq item (nth i web-mode-imenu-regexp-list))
+            (setq regexp (nth 0 item))
+            (setq type-idx (nth 1 item))
+            (setq content-idx (nth 2 item))
+            (setq concat-str (nth 3 item))
+            (when (not (numberp content-idx))
+              (setq content-regexp (nth 2 item)
+                    close-tag-regexp (nth 4 item)
+                    content-idx nil))
+
+            (when (string-match regexp line)
+
+              (cond
+               (content-idx
+                (setq type (match-string type-idx line))
+                (setq content (match-string content-idx line))
+                (setq str (concat type concat-str content))
+                (setq jumpto (line-beginning-position)))
+               (t
+                (let (limit)
+                  (setq type (match-string type-idx line))
+                  (goto-char (line-beginning-position))
+                  (save-excursion
+                    (setq limit (re-search-forward close-tag-regexp (point-max) t)))
+
+                  (when limit
+                    (when (re-search-forward content-regexp limit t)
+                      (setq content (match-string 1))
+                      (setq str (concat type concat-str content))
+                      (setq jumpto (line-beginning-position))
+                      )
+                    )))
+               )
+              (when str (setq toc-index
+                              (cons (cons str jumpto)
+                                    toc-index)
+                              )
+                    (setq found t))
+              )
+            (setq i (1+ i))))
+        (forward-line)
+        (goto-char (line-end-position)) ;; make sure we are at eobp
+        ))
+    (nreverse toc-index)))
+
+;;---- UNIT TESTING ------------------------------------------------------------
+
+(defun web-mode-test ()
+  "Executes web-mode unit tests. See `web-mode-tests-directory'."
+  (interactive)
+  (let (files ret regexp)
+    (setq regexp "^[[:alnum:]][[:alnum:]._]+\\'")
+    (setq files (directory-files web-mode-tests-directory t regexp))
+    (dolist (file files)
+      (cond
+       ((eq (string-to-char (file-name-nondirectory file)) ?\_)
+        (delete-file file))
+       (t
+        (setq ret (web-mode-test-process file)))
+       ) ;cond
+      ) ;dolist
+    ))
+
+(defun web-mode-test-process (file)
+  (with-temp-buffer
+    (let (out sig1 sig2 success err)
+      (setq-default indent-tabs-mode nil)
+      (if (string-match-p "sql" file)
+          (setq web-mode-enable-sql-detection t)
+        (setq web-mode-enable-sql-detection nil))
+      (insert-file-contents file)
+      (set-visited-file-name file)
+      (web-mode)
+      (setq sig1 (md5 (current-buffer)))
+      (delete-horizontal-space)
+      (while (not (eobp))
+        (forward-line)
+        (delete-horizontal-space)
+        (end-of-line))
+      (web-mode-buffer-indent)
+      (setq sig2 (md5 (current-buffer)))
+      (setq success (string= sig1 sig2))
+      (setq out (concat (if success "ok" "ko") " : " (file-name-nondirectory file)))
+      (message out)
+      (setq err (concat (file-name-directory file) "_err." (file-name-nondirectory file)))
+      (if success
+          (when (file-readable-p err)
+            (delete-file err))
+        (write-file err)
+        (message "[%s]" (buffer-string))
+        ) ;if
+      out)))
+
+;;---- MISC --------------------------------------------------------------------
+
+(defun web-mode-set-engine (engine)
+  "Set the engine for the current buffer."
+  (interactive
+   (list (completing-read
+          "Engine: "
+          (let (engines)
+            (dolist (elt web-mode-engines)
+              (setq engines (append engines (list (car elt)))))
+            engines))))
+  (setq web-mode-content-type "html"
+        web-mode-engine (web-mode-engine-canonical-name engine)
+        web-mode-minor-engine engine)
+  (web-mode-on-engine-setted)
+  (web-mode-buffer-highlight))
+
+(defun web-mode-set-content-type (content-type)
+  "Set the content-type for the current buffer"
+  (interactive (list (completing-read "Content-type: " web-mode-part-content-types)))
+  (setq web-mode-content-type content-type)
+  (when (called-interactively-p 'any)
+    )
+  (web-mode-buffer-highlight))
+
+(defun web-mode-on-engine-setted ()
+  (let (elt elts engines)
+
+    (when (string= web-mode-engine "razor") (setq web-mode-enable-block-face t))
+    (setq web-mode-engine-attr-regexp (cdr (assoc web-mode-engine web-mode-engine-attr-regexps)))
+    (setq web-mode-engine-token-regexp (cdr (assoc web-mode-engine web-mode-engine-token-regexps)))
+
+    ;;(message "%S %S" web-mode-engine-attr-regexp web-mode-engine)
+
+    (when (null web-mode-minor-engine)
+      (setq web-mode-minor-engine "none"))
+
+    (setq elt (assoc web-mode-engine web-mode-engine-open-delimiter-regexps))
+    (if elt
+        (setq web-mode-block-regexp (cdr elt))
+      (setq web-mode-engine "none"))
+
+    (unless (boundp 'web-mode-extra-auto-pairs)
+      (setq web-mode-extra-auto-pairs nil))
+
+    (setq web-mode-auto-pairs
+          (append
+           (cdr (assoc web-mode-engine web-mode-engines-auto-pairs))
+           (cdr (assoc nil web-mode-engines-auto-pairs))
+           (cdr (assoc web-mode-engine web-mode-extra-auto-pairs))
+           (cdr (assoc nil web-mode-extra-auto-pairs))))
+
+    (unless (boundp 'web-mode-extra-snippets)
+      (setq web-mode-extra-snippets nil))
+
+    (setq elts
+          (append
+           (cdr (assoc web-mode-engine web-mode-extra-snippets))
+           (cdr (assoc nil             web-mode-extra-snippets))
+           (cdr (assoc web-mode-engine web-mode-engines-snippets))
+           (cdr (assoc nil             web-mode-engines-snippets))))
+
+    (dolist (elt elts)
+      (unless (assoc (car elt) web-mode-snippets)
+        (setq web-mode-snippets (append (list elt) web-mode-snippets)))
+      )
+
+    (setq web-mode-engine-font-lock-keywords
+          (symbol-value (cdr (assoc web-mode-engine web-mode-engines-font-lock-keywords))))
+
+    (when (and (string= web-mode-minor-engine "jinja")
+               (not (member "endtrans" web-mode-django-control-blocks)))
+      (add-to-list 'web-mode-django-control-blocks "endtrans")
+      (setq web-mode-django-control-blocks-regexp
+            (regexp-opt web-mode-django-control-blocks t))
+      )
+
+;;    (message "%S" (symbol-value (cdr (assoc web-mode-engine web-mode-engines-font-lock-keywords))))
+
+    ))
+
+(defun web-mode-detect-engine ()
   (save-excursion
-    (let ((continue t) (counter 0))
-      (beginning-of-line)
-      (while (and continue (not (eolp)))
-        (if (web-mode-is-part-token-or-server)
-            (setq counter (1+ counter))
-          (when (not (eq ?\s (following-char)))
-            (setq continue nil
-                  counter 0))
-          ) ;if
-        (forward-char)
-        ) ;while
-      (> counter 0)
-      )))
-
-(defun web-mode-is-content (&optional pos)
-  "Is pos in a html text."
-  (unless pos (setq pos (point)))
-  (not (or (get-text-property pos 'part-side)
-           (get-text-property pos 'tag-type)
-           (get-text-property pos 'block-side)
-           )))
-
-(defun web-mode-is-comment-or-string (&optional pos)
-  "Detect if point is in a comment or in a string."
-  (unless pos (setq pos (point)))
-  (not (null (or (eq (get-text-property pos 'tag-type) 'comment)
-                 (get-text-property pos 'block-token)
-                 (get-text-property pos 'part-token))))
-  )
-
-(defun web-mode-is-comment (&optional pos)
-  "Detect if point is in a comment."
-  (unless pos (setq pos (point)))
-  (not (null (or (eq (get-text-property pos 'tag-type) 'comment)
-                 (eq (get-text-property pos 'block-token) 'comment)
-                 (eq (get-text-property pos 'part-token) 'comment))))
-  )
-
-;;--- end search
+    (goto-char (point-min))
+    (when (re-search-forward "-\\*- engine:[ ]*\\([[:alnum:]-]+\\)[ ]*-\\*-" web-mode-chunk-length t)
+      (setq web-mode-minor-engine (match-string-no-properties 1))
+      (setq web-mode-engine (web-mode-engine-canonical-name web-mode-minor-engine)))
+    web-mode-minor-engine))
+
+(defun web-mode-guess-engine-and-content-type ()
+  (let (buff-name elt found)
+
+    (setq buff-name (buffer-file-name))
+    (unless buff-name (setq buff-name (buffer-name)))
+    (setq web-mode-is-scratch (string= buff-name "*scratch*"))
+    (setq web-mode-content-type nil)
+
+    (when (boundp 'web-mode-content-types-alist)
+      (setq found nil)
+      (dolist (elt web-mode-content-types-alist)
+        (when (and (not found) (string-match-p (cdr elt) buff-name))
+          (setq web-mode-content-type (car elt)
+                found t))
+        ) ;dolist
+      ) ;when
+
+    (unless web-mode-content-type
+      (setq found nil)
+      (dolist (elt web-mode-content-types)
+        (when (and (not found) (string-match-p (cdr elt) buff-name))
+          (setq web-mode-content-type (car elt)
+                found t))
+        ) ;dolist
+      ) ;unless
+
+    (when (boundp 'web-mode-engines-alist)
+      (setq found nil)
+      (dolist (elt web-mode-engines-alist)
+        (cond
+         ((stringp (cdr elt))
+          (when (string-match-p (cdr elt) buff-name)
+            (setq web-mode-engine (car elt))))
+         ((functionp (cdr elt))
+          (when (funcall (cdr elt))
+            (setq web-mode-engine (car elt))))
+         ) ;cond
+        ) ;dolist
+      ) ;when
+
+    (unless web-mode-engine
+      (setq found nil)
+      (dolist (elt web-mode-engine-file-regexps)
+        ;;(message "%S %S" (cdr elt) buff-name)
+        (when (and (not found) (string-match-p (cdr elt) buff-name))
+          (setq web-mode-engine (car elt)
+                found t))
+        )
+      )
+
+    (when (and (or (null web-mode-engine) (string= web-mode-engine "none"))
+               (string-match-p "php" (buffer-substring-no-properties
+                                      (line-beginning-position)
+                                      (line-end-position))))
+      (setq web-mode-engine "php"))
+
+    (when (and (string= web-mode-content-type "javascript")
+               (string-match-p "@jsx"
+                               (buffer-substring-no-properties
+                                (point-min)
+                                (if (< (point-max) web-mode-chunk-length)
+                                    (point-max)
+                                  web-mode-chunk-length)
+                                )))
+      (setq web-mode-content-type "jsx"))
+
+    (when web-mode-engine
+      (setq web-mode-minor-engine web-mode-engine
+            web-mode-engine (web-mode-engine-canonical-name web-mode-engine))
+      )
+
+    (when (and (or (null web-mode-engine)
+                   (string= web-mode-engine "none"))
+               web-mode-enable-engine-detection)
+      (web-mode-detect-engine))
+
+    (web-mode-on-engine-setted)
+
+    ))
+
+(defun web-mode-engine-canonical-name (name)
+  (let (engine)
+    (cond
+     ((null name)
+      nil)
+     ((assoc name web-mode-engines)
+      name)
+     (t
+      (dolist (elt web-mode-engines)
+        (when (and (null engine) (member name (cdr elt)))
+          (setq engine (car elt)))
+        ) ;dolist
+      engine)
+     )))
+
+(defun web-mode-on-after-save ()
+  (when web-mode-is-scratch
+    (web-mode-guess-engine-and-content-type)
+    (web-mode-buffer-highlight))
+  nil)
 
 (defun web-mode-on-exit ()
-  "Exit web-mode."
-  (interactive)
   (web-mode-with-silent-modifications
    (put-text-property (point-min) (point-max) 'invisible nil)
    (remove-overlays)
    (remove-hook 'change-major-mode-hook 'web-mode-on-exit t)
    ))
 
+(defun web-mode-file-link ()
+  "Insert a link to the file in html document. This function can be extended to support more filetypes by customizing `web-mode-file-extensions' and `web-mode-file-elements'."
+  (interactive
+   (let ((type nil)
+         (file (file-relative-name (read-file-name "Link file: ")))
+         (matched nil)
+         (point-line (line-number-at-pos))
+         (point-column (current-column)))
+     (dolist (type web-mode-file-extensions)		;for every element in web-mode-type-list
+       (when (string-match (nth 0 type) file)
+         (setq matched t)
+         ;; move to head if the link requires it
+         (when (nth 2 type)
+           (goto-char (point-min))
+           (search-forward "</head>")
+           (backward-char 7)
+           (open-line 1))
+         (insert (nth 0 (nth (nth 1 type) web-mode-file-elements)) file (nth 1 (nth (nth 1 type) web-mode-file-elements)))
+         (indent-for-tab-command)
+         ;; fix indentation and return point where it was
+         (when (nth 2 type)
+           (forward-line)
+           (indent-for-tab-command)
+           (forward-line (+ point-line 1))
+           (move-to-column point-column))))
+     (when (not matched)		;return an error if filetype is unknown
+       (error "Unknown file type")))))
+
 (defun web-mode-reload ()
   "Reload web-mode."
   (interactive)
   (web-mode-with-silent-modifications
-   (setq web-mode-time nil)
-   (put-text-property (point-min) (point-max) 'invisible nil)
-   (remove-overlays)
-   (unload-feature 'web-mode)
-;;   (setq web-mode-disable-css-colorization t)
-   (web-mode)
-   (if (fboundp 'web-mode-hook)
-       (web-mode-hook))))
+    (put-text-property (point-min) (point-max) 'invisible nil)
+    (remove-overlays)
+    (setq font-lock-unfontify-region-function 'font-lock-default-unfontify-region)
+    (load "web-mode.el")
+    (setq web-mode-change-beg nil
+          web-mode-change-end nil)
+    (web-mode)
+    ))
 
 (defun web-mode-trace (msg)
-  "Benchmark."
+  (let (sub)
+    ;;      (when (null web-mode-time) (setq web-mode-time (current-time)))
+    (setq sub (time-subtract (current-time) web-mode-time))
+    (when nil
+      (save-excursion
+        (let ((n 0))
+          (goto-char (point-min))
+          (while (web-mode-tag-next)
+            (setq n (1+ n))
+            )
+          (message "%S tags found" n)
+          )))
+    (message "%18s: time elapsed = %Ss %9Sµs" msg (nth 1 sub) (nth 2 sub))
+    ))
+
+(defun web-mode-reveal ()
+  "Display text properties at point."
   (interactive)
-  (let (sub trace)
-    (setq trace nil)
-    (when trace
-      (when (null web-mode-time) (setq web-mode-time (current-time)))
-      (setq sub (time-subtract (current-time) web-mode-time))
-      (message "%18s: time elapsed = %Ss %9Sµs" msg (nth 1 sub) (nth 2 sub))
-      )))
+  (let (symbols out)
+    (setq out (format
+               "[point=%S engine=%S minor=%S content-type=%S language-at-pos=%S]\n"
+               (point)
+               web-mode-engine
+               web-mode-minor-engine
+               web-mode-content-type
+               (web-mode-language-at-pos (point))))
+    (setq symbols (append web-mode-scan-properties '(font-lock-face face)))
+    (dolist (symbol symbols)
+      (when symbol
+        (setq out (concat out (format "%s(%S) " (symbol-name symbol) (get-text-property (point) symbol)))))
+      )
+    (message "%s\n" out)
+    ;;(message "syntax-class=%S" (syntax-class (syntax-after (point))))
+    (message nil)))
 
 (defun web-mode-debug ()
-  "Display informations useful for debuging"
+  "Display informations useful for debugging."
   (interactive)
-  (let (modes)
+  (let ((modes nil)
+        (customs '(web-mode-enable-current-column-highlight web-mode-enable-current-element-highlight indent-tabs-mode))
+        (ignore '(abbrev-mode auto-composition-mode auto-compression-mode auto-encryption-mode auto-insert-mode blink-cursor-mode column-number-mode delete-selection-mode display-time-mode electric-indent-mode file-name-shadow-mode font-lock-mode global-font-lock-mode global-hl-line-mode line-number-mode menu-bar-mode mouse-wheel-mode recentf-mode show-point-mode tool-bar-mode tooltip-mode transient-mark-mode)))
     (message "\n")
     (message "--- WEB-MODE DEBUG BEG ---")
     (message "versions: emacs(%S.%S) web-mode(%S)"
              emacs-major-version emacs-minor-version web-mode-version)
-    (message "vars: engine(%S) content-type(%S) file(%S)"
+    (message "vars: engine(%S) minor(%S) content-type(%S) file(%S)"
              web-mode-engine
+             web-mode-minor-engine
              web-mode-content-type
              (or (buffer-file-name) (buffer-name)))
     (message "system: window(%S) config(%S)" window-system system-configuration)
     (message "colors: fg(%S) bg(%S) "
              (cdr (assoc 'foreground-color default-frame-alist))
              (cdr (assoc 'background-color default-frame-alist)))
-    (message "modes: whitespace-mode(%S) global-whitespace-mode(%S) rainbow-mode(%S) idle-highlight-mode(%S) fic-mode(%S)"
-             (if (boundp 'whitespace-mode) whitespace-mode nil)
-             (if (boundp 'global-whitespace-mode) global-whitespace-mode nil)
-             (if (boundp 'rainbow-mode) rainbow-mode nil)
-             (if (boundp 'idle-highlight-mode) idle-highlight-mode nil)
-             (if (boundp 'fic-mode) fic-mode nil)
-             )
     (mapc (lambda (mode)
             (condition-case nil
-                (if (and (symbolp mode) (symbol-value mode))
+                (if (and (symbolp mode) (symbol-value mode) (not (member mode ignore)))
                     (add-to-list 'modes mode))
               (error nil))
             ) ;lambda
           minor-mode-list)
-    (message "%S" modes)
+    (message "minor modes: %S" modes)
+    (message "vars:")
+    (dolist (custom customs)
+      (message (format "%s=%S " (symbol-name custom) (symbol-value custom))))
     (message "--- WEB-MODE DEBUG END ---")
     (switch-to-buffer "*Messages*")
     (goto-char (point-max))
@@ -9419,9 +12085,9 @@
 
 (provide 'web-mode)
 
+;;; web-mode.el ends here
+
 ;; Local Variables:
 ;; coding: utf-8
 ;; indent-tabs-mode: nil
 ;; End:
-
-;;; web-mode.el ends here