$include_dir="/home/hyper-archives/boost-commit/include"; include("$include_dir/msg-header.inc") ?>
Subject: [Boost-commit] svn:boost r61936 - trunk/tools/boostbook/xsl
From: steven_at_[hidden]
Date: 2010-05-12 18:29:49
Author: steven_watanabe
Date: 2010-05-12 18:29:47 EDT (Wed, 12 May 2010)
New Revision: 61936
URL: http://svn.boost.org/trac/boost/changeset/61936
Log:
rewrite the syntax highlighter and the c++ name lookup rules to make them faster
Text files modified: 
   trunk/tools/boostbook/xsl/lookup.xsl           |   344 ++++++++++++++++----------------------- 
   trunk/tools/boostbook/xsl/source-highlight.xsl |   143 ++++++----------                        
   2 files changed, 198 insertions(+), 289 deletions(-)
Modified: trunk/tools/boostbook/xsl/lookup.xsl
==============================================================================
--- trunk/tools/boostbook/xsl/lookup.xsl	(original)
+++ trunk/tools/boostbook/xsl/lookup.xsl	2010-05-12 18:29:47 EDT (Wed, 12 May 2010)
@@ -7,6 +7,7 @@
    http://www.boost.org/LICENSE_1_0.txt)
   -->
 <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+                xmlns:exsl="http://exslt.org/common"
                 version="1.0">
 
   <!-- Maximum length of directory and file names is 31 characters.
@@ -154,40 +155,65 @@
   <xsl:template name="build-fully-qualified-name">
     <xsl:param name="is.id" select="false()" />
 
-    <!-- The depth of qualified name element that we will print now-->
-    <xsl:param name="depth" select="1"/>
-
     <!-- Determine the set of ancestor namespaces -->
     <xsl:variable name="ancestors"
       select="ancestor::namespace|
                   ancestor::class|ancestor::struct|ancestor::union|
                   ancestor::class-specialization|ancestor::struct-specialization|ancestor::union-specialization"/>
 
+    <xsl:for-each select="$ancestors">
+      <xsl:apply-templates select="." mode="fast-print-id-part">
+        <xsl:with-param name="is.id" select="$is.id"/>
+      </xsl:apply-templates>
+      <xsl:choose>
+        <xsl:when test="$is.id"><xsl:text>.</xsl:text></xsl:when>
+        <xsl:otherwise><xsl:text>::</xsl:text></xsl:otherwise>
+      </xsl:choose>
+    </xsl:for-each>
+    <xsl:apply-templates select="." mode="fast-print-id-part">
+      <xsl:with-param name="is.id" select="$is.id"/>
+    </xsl:apply-templates>
+  </xsl:template>
+
+  <xsl:variable name="elements-with-ids">
+    <xsl:apply-templates select="namespace|class|struct|union|class-specialization|struct-specialization|union-specialization"
+                         mode="preprocess-ids"/>
+  </xsl:variable>
+  
+  <xsl:variable name="fast-elements" select="exsl:node-set($elements-with-ids)"/>
+  
+  <xsl:template match="*" mode="preprocess-ids">
+    <element>
+      <xsl:attribute name="id">
+        <xsl:value-of select="generate-id()"/>
+      </xsl:attribute>
+      <xsl:attribute name="part-id">
+        <xsl:call-template name="print-id-part"/>
+      </xsl:attribute>
+    </element>
+  </xsl:template>
+  
+  <xsl:template name="print-id-part">
+    <xsl:apply-templates select="." mode="print-id-part"/>
+  </xsl:template>
+  
+  <xsl:template match="*" mode="fast-print-id-part">
+    <xsl:param name="is.id"/>
     <xsl:choose>
-      <xsl:when test="$depth > count($ancestors)">
-        <xsl:apply-templates select="." mode="print-id-part">
-          <xsl:with-param name="is.id" select="$is.id"/>
-        </xsl:apply-templates>
+      <xsl:when test="not($is.id)">
+        <xsl:apply-templates select="." mode="print-name"/>
+      </xsl:when>
+      <xsl:when test="$fast-elements[@id=generate-id()]">
+        <xsl:value-of select="$fast-elements[@id=generate-id()]/@part-id"/>
       </xsl:when>
       <xsl:otherwise>
-        <xsl:if test="name($ancestors[$depth])='namespace' or
-                      count(ancestor::free-function-group)=0">
-          <xsl:apply-templates select="$ancestors[$depth]" mode="print-id-part">
-            <xsl:with-param name="is.id" select="$is.id"/>
-          </xsl:apply-templates>
-          <xsl:choose>
-            <xsl:when test="$is.id"><xsl:text>.</xsl:text></xsl:when>
-            <xsl:otherwise><xsl:text>::</xsl:text></xsl:otherwise>
-          </xsl:choose>
-        </xsl:if>
-        <xsl:call-template name="build-fully-qualified-name">
+        <xsl:apply-templates select="." mode="print-id-part">
           <xsl:with-param name="is.id" select="$is.id"/>
-          <xsl:with-param name="depth" select="$depth + 1"/>
-        </xsl:call-template>
+        </xsl:apply-templates>
       </xsl:otherwise>
     </xsl:choose>
   </xsl:template>
-
+  
   <!-- Print the part of a fully qualified name for a single element -->
   <xsl:template match="*" mode="print-id-part">
     <xsl:param name="is.id"/>
@@ -252,167 +278,93 @@
     <xsl:apply-templates select="specialization/template-arg" mode="print-name"/>
     <xsl:text>></xsl:text>
   </xsl:template>
+  
+  <xsl:template name="concat-directives">
+    <xsl:param name="directives"/>
+    <xsl:for-each select="$directives">
+      <xsl:apply-templates select="." mode="print-name"/>
+      <xsl:text>::</xsl:text>
+    </xsl:for-each>
+  </xsl:template>
 
-  <xsl:template name="name-matches-node">
-    <!-- The name we are looking for -->
+  <xsl:template name="get-name-qualifiers">
     <xsl:param name="name"/>
+    <xsl:param name="node"/>
+    
+    <!-- Find all the ancestor scopes of the node -->
+    <xsl:variable name="ancestors"
+      select="ancestor::namespace|
+                  ancestor::class|ancestor::struct|ancestor::union|
+                  ancestor::class-specialization|ancestor::struct-specialization|ancestor::union-specialization"/>
 
-    <!-- The name to display -->
-    <xsl:param name="display-name" select="$name"/>
+    <!-- concatenate their names together separated by .'s -->
+    <xsl:for-each select="$ancestors">
+      <xsl:apply-templates select="." mode="fast-print-id-part">
+        <xsl:with-param name="is.id" select="false()"/>
+      </xsl:apply-templates>
+      <xsl:text>::</xsl:text>
+    </xsl:for-each>
+
+  </xsl:template>
+  
+  <xsl:template name="find-nodes-matching-name">
+    <!-- The name we are looking for -->
+    <xsl:param name="name"/>
 
     <!-- The context in which this name occurs -->
     <xsl:param name="context"/>
 
     <!-- The node that we are checking against -->
-    <xsl:param name="node"/>
-
-    <!-- The mode we are in. Can be one of:
-           matches: emits the matches as they are found (for debugging)
-           link: link to the node that was found
-         -->
-    <xsl:param name="mode" select="'matches'"/>
-
-    <!-- The index into the list of using directives for the context node -->
-    <xsl:param name="index" select="1"/>
-
-    <!-- The prefix we should append to the name when checking this node -->
-    <xsl:param name="prefix" select="''"/>
-
-    <xsl:choose>
-      <xsl:when test="count($node) > 1">
-        <xsl:variable name="matches">
-          <xsl:call-template name="count-matches">
-            <xsl:with-param name="name" select="$name"/>
-            <xsl:with-param name="context" select="$context"/>
-            <xsl:with-param name="nodes" select="$node[position() = 1]"/>
-          </xsl:call-template>
-        </xsl:variable>
-
-        <xsl:choose>
-          <xsl:when test="$matches = 0">
-            <xsl:call-template name="name-matches-node">
-              <xsl:with-param name="name" select="$name"/>
-              <xsl:with-param name="display-name" select="$display-name"/>
-              <xsl:with-param name="context" select="$context"/>
-              <xsl:with-param name="node" select="$node[position() > 1]"/>
-              <xsl:with-param name="mode" select="$mode"/>
-            </xsl:call-template>
-          </xsl:when>
-          <xsl:otherwise>
-            <xsl:call-template name="name-matches-node">
-              <xsl:with-param name="name" select="$name"/>
-              <xsl:with-param name="display-name" select="$display-name"/>
-              <xsl:with-param name="context" select="$context"/>
-              <xsl:with-param name="node" select="$node[position() = 1]"/>
-              <xsl:with-param name="mode" select="$mode"/>
-            </xsl:call-template>
-          </xsl:otherwise>
-        </xsl:choose>
-      </xsl:when>
-      <xsl:when test="count($node) = 1">
-        <!-- The fully-qualified name of the node we are checking against -->
-        <xsl:variable name="fully-qualified-name">
-          <xsl:call-template name="fully-qualified-name">
-            <xsl:with-param name="node" select="$node"/>
-          </xsl:call-template>
-        </xsl:variable>
+    <xsl:param name="nodes"/>
 
-        <!-- The set of using directives for this context node -->
-        <xsl:variable name="directives"
-          select="$context/ancestor::*/using-namespace |
+    <!-- The set of using directives for this context node -->
+    <xsl:variable name="directives"
+      select="$context/ancestor::*/using-namespace |
                   $context/ancestor::namespace |
                   $context/ancestor::*/using-class |
                   $context/ancestor::class |
                   $context/ancestor::struct"/>
 
-        <!-- The name of the current directive -->
-        <xsl:variable name="this-context">
-          <xsl:apply-templates select="$directives[$index]" mode="print-name"/>
-        </xsl:variable>
-
-        <!-- Check if we have a match -->
-        <xsl:variable name="have-match"
-          select="$fully-qualified-name = concat($prefix, $name)"/>
-
-        <xsl:if test="$have-match">
-          <xsl:choose>
-            <xsl:when test="$mode='matches'">
-              Match in namespace ::<xsl:value-of select="$prefix"/>
-            </xsl:when>
-            <xsl:when test="$mode='link'">
-              <xsl:call-template name="internal-link">
-                <xsl:with-param name="to">
-                  <xsl:call-template name="generate.id">
-                    <xsl:with-param name="node" select="$node"/>
-                  </xsl:call-template>
-                </xsl:with-param>
-                <xsl:with-param name="text" select="$display-name"/>
-              </xsl:call-template>
-            </xsl:when>
-          </xsl:choose>
-        </xsl:if>
+    <xsl:variable name="directives-str">
+      <xsl:call-template name="concat-directives">
+        <xsl:with-param name="directives" select="$directives"/>
+      </xsl:call-template>
+    </xsl:variable>
 
-        <xsl:if test="(not($index > count($directives))) and
-                      (not($have-match) or ($mode = 'matches'))">
-          <xsl:variable name="first-branch">
-            <xsl:if test="not ($prefix = '')">
-              <!-- Recurse and append the current context node to the prefix -->
-              <xsl:call-template name="name-matches-node">
-                <xsl:with-param name="name" select="$name"/>
-                <xsl:with-param name="display-name" select="$display-name"/>
-                <xsl:with-param name="context" select="$context"/>
-                <xsl:with-param name="node" select="$node"/>
-                <xsl:with-param name="mode" select="$mode"/>
-                <xsl:with-param name="index" select="$index + 1"/>
-                <xsl:with-param name="prefix"
-                  select="concat($prefix, $this-context, '::')"/>
-              </xsl:call-template>
-            </xsl:if>
-          </xsl:variable>
-
-          <xsl:choose>
-            <xsl:when test="string($first-branch) != ''">
-              <xsl:copy-of select="$first-branch"/>
-            </xsl:when>
-            <xsl:otherwise>
-              <!-- Recurse with just the current context node -->
-              <xsl:call-template name="name-matches-node">
-                <xsl:with-param name="name" select="$name"/>
-                <xsl:with-param name="display-name" select="$display-name"/>
-                <xsl:with-param name="context" select="$context"/>
-                <xsl:with-param name="node" select="$node"/>
-                <xsl:with-param name="mode" select="$mode"/>
-                <xsl:with-param name="index" select="$index + 1"/>
-                <xsl:with-param name="prefix"
-                  select="concat($this-context, '::')"/>
-              </xsl:call-template>
-            </xsl:otherwise>
-          </xsl:choose>
-        </xsl:if>
-      </xsl:when>
-    </xsl:choose>
+    <xsl:apply-templates select="$nodes" mode="generate-cxx-links">
+      <xsl:with-param name="name" select="$name"/>
+      <xsl:with-param name="directives-str" select="$directives-str"/>
+    </xsl:apply-templates>
   </xsl:template>
-
-  <!-- Count the number of nodes in the set that match the given name and
-       lookup context -->
-  <xsl:template name="count-matches">
+  
+  <xsl:template match="*" mode="generate-cxx-links">
     <xsl:param name="name"/>
-    <xsl:param name="context"/>
-    <xsl:param name="nodes"/>
+    <xsl:param name="directives-str"/>
 
-    <xsl:variable name="match-string">
-      <xsl:for-each select="$nodes">
-        <xsl:variable name="does-match">
-          <xsl:call-template name="name-matches-node">
-            <xsl:with-param name="name" select="$name"/>
-            <xsl:with-param name="context" select="$context"/>
-            <xsl:with-param name="node" select="."/>
-          </xsl:call-template>
-        </xsl:variable>
-        <xsl:if test="not($does-match='')">X</xsl:if>
-      </xsl:for-each>
+    <xsl:variable name="qualifiers">
+      <xsl:call-template name="get-name-qualifiers">
+        <xsl:with-param name="name" select="$name"/>
+        <xsl:with-param name="node" select="."/>
+      </xsl:call-template>
     </xsl:variable>
-    <xsl:value-of select="string-length($match-string)"/>
+
+    <!-- Check if this node matches any visible namespace -->
+    <xsl:if test="contains($directives-str, $qualifiers)">
+      <xsl:variable name="myid">
+        <xsl:call-template name="generate.id">
+          <xsl:with-param name="node" select="."/>
+        </xsl:call-template>
+      </xsl:variable>
+      <cxx-link-helper>
+        <xsl:attribute name="id">
+          <xsl:value-of select="$myid"/>
+        </xsl:attribute>
+        <xsl:attribute name="namespace">
+          <xsl:value-of select="$qualifiers"/>
+        </xsl:attribute>
+        <xsl:text>random text</xsl:text>
+      </cxx-link-helper>
+    </xsl:if>
   </xsl:template>
 
   <xsl:template name="cxx-link-name">
@@ -433,18 +385,20 @@
 
     <!-- The list of nodes that match the lookup node in both name and type -->
     <xsl:param name="nodes"/>
-
-    <!-- Count the number of nodes that match -->
-    <xsl:variable name="matches">
-      <xsl:call-template name="count-matches">
+    
+    <!-- Filter the nodes to leave only the ones that are in scope. -->
+    <xsl:variable name="matches1">
+      <xsl:call-template name="find-nodes-matching-name">
         <xsl:with-param name="name" select="$name"/>
-        <xsl:with-param name="context" select="$lookup"/>
         <xsl:with-param name="nodes" select="$nodes"/>
+        <xsl:with-param name="context" select="$lookup"/>
       </xsl:call-template>
     </xsl:variable>
+    
+    <xsl:variable name="matches" select="exsl:node-set($matches1)//cxx-link-helper"/>
 
     <xsl:choose>
-      <xsl:when test="$matches = 0">
+      <xsl:when test="count($matches) = 0">
         <xsl:message>
           <xsl:text>Cannot find </xsl:text>
           <xsl:value-of select="$type"/>
@@ -454,39 +408,27 @@
         </xsl:message>
         <xsl:value-of select="$display-name"/>
       </xsl:when>
-      <xsl:when test="$matches = 1">
-        <xsl:for-each select="$nodes">
-          <xsl:call-template name="name-matches-node">
-            <xsl:with-param name="name" select="$name"/>
-            <xsl:with-param name="display-name" select="$display-name"/>
-            <xsl:with-param name="context" select="$lookup"/>
-            <xsl:with-param name="node" select="."/>
-            <xsl:with-param name="mode" select="'link'"/>
-          </xsl:call-template>
-        </xsl:for-each>
-      </xsl:when>
       <xsl:otherwise>
-        <xsl:message>
-          <xsl:text>Reference to </xsl:text>
-          <xsl:value-of select="$type"/>
-          <xsl:text> '</xsl:text>
-          <xsl:value-of select="$name"/>
-          <xsl:text>' is ambiguous. Found:</xsl:text>
-          <xsl:for-each select="$nodes">
-            <xsl:call-template name="name-matches-node">
-              <xsl:with-param name="name" select="$name"/>
-              <xsl:with-param name="context" select="$lookup"/>
-              <xsl:with-param name="node" select="."/>
-              <xsl:with-param name="mode" select="'matches'"/>
-            </xsl:call-template>
-          </xsl:for-each>
-        </xsl:message>
-        <xsl:call-template name="name-matches-node">
-          <xsl:with-param name="name" select="$name"/>
-          <xsl:with-param name="display-name" select="$display-name"/>
-          <xsl:with-param name="context" select="$lookup"/>
-          <xsl:with-param name="node" select="$nodes"/>
-          <xsl:with-param name="mode" select="'link'"/>
+        <!-- If we found more than one, print a message and take the first -->
+        <xsl:if test="count($matches) > 1">
+          <xsl:message>
+            <xsl:text>Reference to </xsl:text>
+            <xsl:value-of select="$type"/>
+            <xsl:text> '</xsl:text>
+            <xsl:value-of select="$name"/>
+            <xsl:text>' is ambiguous. Found:</xsl:text>
+            <xsl:for-each select="$matches">
+              <xsl:text>
+              Match in namespace ::</xsl:text>
+              <xsl:value-of select="@namespace"/>
+            </xsl:for-each>
+          </xsl:message>
+        </xsl:if>
+        <xsl:call-template name="internal-link">
+          <xsl:with-param name="to">
+            <xsl:value-of select="$matches[position() = 1]/@id"/>
+          </xsl:with-param>
+          <xsl:with-param name="text" select="$display-name"/>
         </xsl:call-template>
       </xsl:otherwise>
     </xsl:choose>
Modified: trunk/tools/boostbook/xsl/source-highlight.xsl
==============================================================================
--- trunk/tools/boostbook/xsl/source-highlight.xsl	(original)
+++ trunk/tools/boostbook/xsl/source-highlight.xsl	2010-05-12 18:29:47 EDT (Wed, 12 May 2010)
@@ -23,112 +23,79 @@
       </xsl:otherwise>
     </xsl:choose>
   </xsl:template>
-
-  <!-- Perform C++ keyword highlighting on the given text -->
-  <xsl:template name="highlight-text">
-    <xsl:param name="text" select="."/>
-    <xsl:param name="keywords"
-      select="'asm auto bool break case catch char class const const_cast continue default delete do double dynamic_cast else enum explicit export extern false float for friend goto if inline int long mutable namespace new operator private protected public register reinterpret_cast return short signed sizeof static static_cast struct switch template this throw true try typedef typeid typename union unsigned using virtual void volatile wchar_t while'"/>
-    <xsl:param name="best-match" select="''"/>
+  
+  <xsl:variable name="id-chars" select="'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_'"/>
+  <xsl:variable name="keywords"
+    select="' asm auto bool break case catch char class const const_cast continue default delete do double dynamic_cast else enum explicit export extern false float for friend goto if inline int long mutable namespace new operator private protected public register reinterpret_cast return short signed sizeof static static_cast struct switch template this throw true try typedef typeid typename union unsigned using virtual void volatile wchar_t while '"/>
     
-    <!-- Determine the current keyword -->
-    <xsl:variable name="keyword">
-      <xsl:choose>
-        <xsl:when test="contains($keywords, ' ')">
-          <xsl:value-of select="substring-before($keywords, ' ')"/>
-        </xsl:when>
-        <xsl:otherwise>
-          <xsl:value-of select="$keywords"/>
-        </xsl:otherwise>
-      </xsl:choose>
-    </xsl:variable>
-
-    <!-- Determine the set of keywords that are left -->
-    <xsl:variable name="keywords-left">
-      <xsl:if test="contains($keywords, ' ')">
-        <xsl:value-of select="substring-after($keywords, ' ')"/>
-      </xsl:if>
-    </xsl:variable>
-
-    <!-- The set of characters that can be identifiers -->
-    <xsl:variable name="id-chars" select="'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_'"/>
-
-    <xsl:variable name="X" select="'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'"/>
 
+  <xsl:template name="highlight-identifier">
+    <xsl:param name="identifier"/>
     <xsl:choose>
-      <!-- Have we exhausted all keywords without finding any to highlight? -->
-      <xsl:when test="$keyword='' and $best-match=''">
-        <!-- Just copy the text -->
-        <xsl:copy-of select="$text"/>
-      </xsl:when>
-
-      <!-- Have we exhausted all keywords, but have one to highlight? If so,
-           make sure we didn't just find part of an identifier. -->
-      <xsl:when 
-        test="$keyword='' and
-              not (starts-with(translate(substring-after($text, $best-match), 
-                                         $id-chars, $X), 'X')) and
-              not (substring(translate(substring-before($text, $best-match),
-                                       $id-chars, $X),
-                             string-length(substring-before($text, 
-                                                            $best-match)),
-                             1) = 'X')">
-        <!-- Copy text before this keyword -->
-        <xsl:value-of select="substring-before($text, $best-match)"/>
-
-        <!-- Highlight the keyword -->
+      <xsl:when test="contains($keywords, concat(' ', $identifier, ' '))">
         <xsl:call-template name="highlight-keyword">
-          <xsl:with-param name="keyword" select="$best-match"/>
-        </xsl:call-template>
-
-        <!-- Recurse on the rest of the text -->
-        <xsl:call-template name="highlight-text">
-          <xsl:with-param name="text" 
-            select="substring-after($text, $best-match)"/>
+          <xsl:with-param name="keyword" select="$identifier"/>
         </xsl:call-template>
       </xsl:when>
-
-      <!-- We thought we had a keyword to highlight, but it was part of an 
-           identifier. So output all of the text up to (but not including!)
-           the last letter of the identifier, and try again to
-           highlight. -->
-      <xsl:when test="$keyword=''">
-        <xsl:value-of select="substring-before($text, $best-match)"/>
-        <xsl:value-of 
-          select="substring($best-match, 1, string-length($best-match)-1)"/>
-        <xsl:call-template name="highlight-text">
-          <xsl:with-param name="text"
-            select="concat(substring($best-match, string-length($best-match), 
-                           1), substring-after($text, $best-match))"/>
+      <xsl:otherwise>
+        <xsl:value-of select="$identifier"/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+  
+  <xsl:template name="highlight-text-impl-ident">
+    <xsl:param name="text"/>
+    <xsl:param name="pos"/>
+    <xsl:choose>
+      <xsl:when test="string-length($text) + 1 = $pos">
+        <xsl:call-template name="highlight-identifier">
+          <xsl:with-param name="identifier" select="substring($text, 1, $pos - 1)"/>
         </xsl:call-template>
       </xsl:when>
-      
-      <!-- Does the text contain this keyword with a better match than we
-           previously had? -->
-      <xsl:when 
-        test="contains($text, $keyword) and
-              (($best-match = '') or 
-               (string-length(substring-before($text, $keyword)) <
-                string-length(substring-before($text, $best-match))))">
-        <!-- Recurse with the current keyword as the new best match -->
-        <xsl:call-template name="highlight-text">
+      <xsl:when test="contains($id-chars, substring($text, $pos, 1))">
+        <xsl:call-template name ="highlight-text-impl-ident">
           <xsl:with-param name="text" select="$text"/>
-          <xsl:with-param name="keywords" select="$keywords-left"/>
-          <xsl:with-param name="best-match" select="$keyword"/>
+          <xsl:with-param name="pos" select="$pos + 1"/>
         </xsl:call-template>
       </xsl:when>
-
-      <!-- Text does not contain this keyword. Just recurse normally -->
       <xsl:otherwise>
-        <xsl:call-template name="highlight-text">
+        <xsl:call-template name="highlight-identifier">
+          <xsl:with-param name="identifier" select="substring($text, 1, $pos - 1)"/>
+        </xsl:call-template>
+        <xsl:call-template name ="highlight-text-impl-root">
+          <xsl:with-param name="text" select="substring($text, $pos)"/>
+        </xsl:call-template>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+  
+  <xsl:template name="highlight-text-impl-root">
+    <xsl:param name="text"/>
+    <xsl:choose>
+      <xsl:when test="string-length($text) = 0"/>
+      <xsl:when test="contains($id-chars, substring($text, 1, 1))">
+        <xsl:call-template name="highlight-text-impl-ident">
           <xsl:with-param name="text" select="$text"/>
-          <xsl:with-param name="keywords" select="$keywords-left"/>
-          <xsl:with-param name="best-match" select="$best-match"/>
+          <xsl:with-param name="pos" select="2"/>
+        </xsl:call-template>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="substring($text, 1, 1)"/>
+        <xsl:call-template name="highlight-text-impl-root">
+          <xsl:with-param name="text" select="substring($text, 2)"/>
         </xsl:call-template>
       </xsl:otherwise>
     </xsl:choose>
   </xsl:template>
 
+  <!-- Perform C++ keyword highlighting on the given text -->
+  <xsl:template name="highlight-text">
+    <xsl:param name="text" select="."/>
+    <xsl:call-template name="highlight-text-impl-root">
+      <xsl:with-param name="text" select="$text"/>
+    </xsl:call-template>
+  </xsl:template>
+
   <xsl:template match="*" mode="highlight">
     <xsl:element name="{name(.)}">
       <xsl:for-each select="./@*">