[bluefish] Some fixes from upstream

Paul Howarth pghmcfc at fedoraproject.org
Mon Dec 2 21:37:31 UTC 2013


commit 2ba8aff89a5529da9c9cba1f21db0dfe26c9d027
Author: Paul Howarth <paul at city-fan.org>
Date:   Mon Dec 2 21:29:07 2013 +0000

    Some fixes from upstream
    
    - Replace v8 jsmin implementation (which doesn't work with bluefish) with an
      MIT-licensed version that will be in bluefish 2.2.5
    - Add upstream fix for syntax highlighting problem (#983902, Gnome Bug #704108)

 bluefish-2.2.4-bz983902.patch |   13 +
 bluefish.spec                 |   17 +-
 jsmin.py                      |  502 +++++++++++++++++++----------------------
 3 files changed, 256 insertions(+), 276 deletions(-)
---
diff --git a/bluefish-2.2.4-bz983902.patch b/bluefish-2.2.4-bz983902.patch
new file mode 100644
index 0000000..dd9499e
--- /dev/null
+++ b/bluefish-2.2.4-bz983902.patch
@@ -0,0 +1,13 @@
+Index: src/bftextview2_patcompile.c
+===================================================================
+--- src/bftextview2_patcompile.c	(revision 7819)
++++ src/bftextview2_patcompile.c	(working copy)
+@@ -513,7 +513,7 @@
+ 					/* check if the last character of the regex is a symbol, if so the last state should not
+ 					   refer to the identstate for all non-symbols */
+ 					gint j;
+-					for (j = 0; j <= NUMSCANCHARS; j++) {
++					for (j = 0; j < NUMSCANCHARS; j++) {
+ 						if (characters[j] == 1
+ 							&& !character_is_symbol(st, context, j)) {
+ 							only_symbols = FALSE;
diff --git a/bluefish.spec b/bluefish.spec
index 3b198ed..26b4f5b 100644
--- a/bluefish.spec
+++ b/bluefish.spec
@@ -1,6 +1,6 @@
 %global pkgver 2.2.4
 #global prerel rc1
-%global rpmrel 3
+%global rpmrel 4
 
 Name:		bluefish
 Version:	%{pkgver}
@@ -14,7 +14,8 @@ URL:		http://bluefish.openoffice.nl/
 # To generate clean source, simply rm -rf data/jsmin.py
 # We provide a replacement in Source1.
 Source0:	bluefish-%{version}%{?prerel:-%{prerel}}-clean.tar.bz2
-Source1:	https://raw.github.com/v8/v8/master/tools/jsmin.py
+Source1:	jsmin.py
+Patch0:		bluefish-2.2.4-bz983902.patch
 BuildRoot:	%{_tmppath}/%{name}-%{version}-%{release}-root-%(id -nu)
 BuildRequires:	desktop-file-utils
 BuildRequires:	enchant-devel >= 1.4.2
@@ -78,8 +79,11 @@ Files common to every architecture version of %{name}.
 %prep
 %setup -q -n %{name}-%{version}%{?prerel:-%{prerel}}
 
+# Add upstream fix for syntax highlighting problem (#983902, Gnome Bug #704108)
+%patch0
+
+# Replace jsmin.py with free version (#1003849)
 cp -a %{SOURCE1} data/
-sed -i 's|python2.4|python|g' data/jsmin.py
 
 %build
 %configure	--disable-dependency-tracking \
@@ -163,8 +167,13 @@ fi
 %{_mandir}/man1/%{name}.1*
 
 %changelog
+* Mon Dec  2 2013 Paul Howarth <paul at city-fan.org> - 2.2.4-4
+- Replace v8 jsmin implementation (which doesn't work with bluefish) with an
+  MIT-licensed version that will be in bluefish 2.2.5
+- Add upstream fix for syntax highlighting problem (#983902, Gnome Bug #704108)
+
 * Tue Sep  3 2013 Tom Callaway <spot at fedoraproject.org> - 2.2.4-3
-- remove non-free jsmin.py code, replace with free jsmin.py
+- Remove non-free jsmin.py code, replace with free jsmin.py
 
 * Sun Jul 28 2013 Paul Howarth <paul at city-fan.org> - 2.2.4-2
 - Install docs to %%{_pkgdocdir} where available
diff --git a/jsmin.py b/jsmin.py
index 250dea9..36fa604 100644
--- a/jsmin.py
+++ b/jsmin.py
@@ -1,282 +1,240 @@
-#!/usr/bin/python2.4
+#!/usr/bin/env python
 
-# Copyright 2012 the V8 project authors. All rights reserved.
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
+# This code is original from jsmin by Douglas Crockford, it was translated to
+# Python by Baruch Even. It was rewritten by Dave St.Germain for speed.
 #
-#     * Redistributions of source code must retain the above copyright
-#       notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-#       copyright notice, this list of conditions and the following
-#       disclaimer in the documentation and/or other materials provided
-#       with the distribution.
-#     * Neither the name of Google Inc. nor the names of its
-#       contributors may be used to endorse or promote products derived
-#       from this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-"""A JavaScript minifier.
-
-It is far from being a complete JS parser, so there are many valid
-JavaScript programs that will be ruined by it.  Another strangeness is that
-it accepts $ and % as parts of identifiers.  It doesn't merge lines or strip
-out blank lines in order to ease debugging.  Variables at the top scope are
-properties of the global object so we can't rename them.  It is assumed that
-you introduce variables with var as if JavaScript followed C++ scope rules
-around curly braces, so the declaration must be above the first use.
-
-Use as:
-import jsmin
-minifier = JavaScriptMinifier()
-program1 = minifier.JSMinify(program1)
-program2 = minifier.JSMinify(program2)
-"""
-
-import re
-
-
-class JavaScriptMinifier(object):
-  """An object that you can feed code snippets to to get them minified."""
-
-  def __init__(self):
-    # We prepopulate the list of identifiers that shouldn't be used.  These
-    # short language keywords could otherwise be used by the script as variable
-    # names.
-    self.seen_identifiers = {"do": True, "in": True}
-    self.identifier_counter = 0
-    self.in_comment = False
-    self.map = {}
-    self.nesting = 0
-
-  def LookAtIdentifier(self, m):
-    """Records identifiers or keywords that we see in use.
-
-    (So we can avoid renaming variables to these strings.)
-    Args:
-      m: The match object returned by re.search.
-
-    Returns:
-      Nothing.
+# The MIT License (MIT)
+# 
+# Copyright (c) 2013 Dave St.Germain
+# 
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+# 
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+# 
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+
+import sys
+is_3 = sys.version_info >= (3, 0)
+if is_3:
+    import io
+else:
+    import StringIO
+    try:
+        import cStringIO
+    except ImportError:
+        cStringIO = None
+
+
+__all__ = ['jsmin', 'JavascriptMinify']
+__version__ = '2.0.8'
+
+
+def jsmin(js):
     """
-    identifier = m.group(1)
-    self.seen_identifiers[identifier] = True
-
-  def Push(self):
-    """Called when we encounter a '{'."""
-    self.nesting += 1
-
-  def Pop(self):
-    """Called when we encounter a '}'."""
-    self.nesting -= 1
-    # We treat each top-level opening brace as a single scope that can span
-    # several sets of nested braces.
-    if self.nesting == 0:
-      self.map = {}
-      self.identifier_counter = 0
-
-  def Declaration(self, m):
-    """Rewrites bits of the program selected by a regexp.
-
-    These can be curly braces, literal strings, function declarations and var
-    declarations.  (These last two must be on one line including the opening
-    curly brace of the function for their variables to be renamed).
-
-    Args:
-      m: The match object returned by re.search.
-
-    Returns:
-      The string that should replace the match in the rewritten program.
+    returns a minified version of the javascript string
     """
-    matched_text = m.group(0)
-    if matched_text == "{":
-      self.Push()
-      return matched_text
-    if matched_text == "}":
-      self.Pop()
-      return matched_text
-    if re.match("[\"'/]", matched_text):
-      return matched_text
-    m = re.match(r"var ", matched_text)
-    if m:
-      var_names = matched_text[m.end():]
-      var_names = re.split(r",", var_names)
-      return "var " + ",".join(map(self.FindNewName, var_names))
-    m = re.match(r"(function\b[^(]*)\((.*)\)\{$", matched_text)
-    if m:
-      up_to_args = m.group(1)
-      args = m.group(2)
-      args = re.split(r",", args)
-      self.Push()
-      return up_to_args + "(" + ",".join(map(self.FindNewName, args)) + "){"
-
-    if matched_text in self.map:
-      return self.map[matched_text]
-
-    return matched_text
-
-  def CharFromNumber(self, number):
-    """A single-digit base-52 encoding using a-zA-Z."""
-    if number < 26:
-      return chr(number + 97)
-    number -= 26
-    return chr(number + 65)
-
-  def FindNewName(self, var_name):
-    """Finds a new 1-character or 2-character name for a variable.
-
-    Enters it into the mapping table for this scope.
+    if not is_3:        
+        if cStringIO and not isinstance(js, unicode):
+            # strings can use cStringIO for a 3x performance
+            # improvement, but unicode (in python2) cannot
+            klass = cStringIO.StringIO
+        else:
+            klass = StringIO.StringIO
+    else:
+        klass = io.StringIO
+    ins = klass(js)
+    outs = klass()
+    JavascriptMinify(ins, outs).minify()
+    return outs.getvalue()
 
-    Args:
-      var_name: The name of the variable before renaming.
 
-    Returns:
-      The new name of the variable.
+class JavascriptMinify(object):
     """
-    new_identifier = ""
-    # Variable names that end in _ are member variables of the global object,
-    # so they can be visible from code in a different scope.  We leave them
-    # alone.
-    if var_name in self.map:
-      return self.map[var_name]
-    if self.nesting == 0:
-      return var_name
-    while True:
-      identifier_first_char = self.identifier_counter % 52
-      identifier_second_char = self.identifier_counter // 52
-      new_identifier = self.CharFromNumber(identifier_first_char)
-      if identifier_second_char != 0:
-        new_identifier = (
-            self.CharFromNumber(identifier_second_char - 1) + new_identifier)
-      self.identifier_counter += 1
-      if not new_identifier in self.seen_identifiers:
-        break
-
-    self.map[var_name] = new_identifier
-    return new_identifier
-
-  def RemoveSpaces(self, m):
-    """Returns literal strings unchanged, replaces other inputs with group 2.
-
-    Other inputs are replaced with the contents of capture 1.  This is either
-    a single space or an empty string.
-
-    Args:
-      m: The match object returned by re.search.
-
-    Returns:
-      The string that should be inserted instead of the matched text.
+    Minify an input stream of javascript, writing
+    to an output stream
     """
-    entire_match = m.group(0)
-    replacement = m.group(1)
-    if re.match(r"'.*'$", entire_match):
-      return entire_match
-    if re.match(r'".*"$', entire_match):
-      return entire_match
-    if re.match(r"/.+/$", entire_match):
-      return entire_match
-    return replacement
-
-  def JSMinify(self, text):
-    """The main entry point.  Takes a text and returns a compressed version.
-
-    The compressed version hopefully does the same thing.  Line breaks are
-    preserved.
-
-    Args:
-      text: The text of the code snippet as a multiline string.
 
-    Returns:
-      The compressed text of the code snippet as a multiline string.
-    """
-    new_lines = []
-    for line in re.split(r"\n", text):
-      line = line.replace("\t", " ")
-      if self.in_comment:
-        m = re.search(r"\*/", line)
-        if m:
-          line = line[m.end():]
-          self.in_comment = False
+    def __init__(self, instream=None, outstream=None):
+        self.ins = instream
+        self.outs = outstream
+
+    def minify(self, instream=None, outstream=None):
+        if instream and outstream:
+            self.ins, self.outs = instream, outstream
+        
+        self.is_return = False
+        self.return_buf = ''
+        
+        def write(char):
+            # all of this is to support literal regular expressions.
+            # sigh
+            if char in 'return':
+                self.return_buf += char
+                self.is_return = self.return_buf == 'return'
+            self.outs.write(char)
+            if self.is_return:
+                self.return_buf = ''
+
+        read = self.ins.read
+
+        space_strings = "abcdefghijklmnopqrstuvwxyz"\
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$\\"
+        starters, enders = '{[(+-', '}])+-"\''
+        newlinestart_strings = starters + space_strings
+        newlineend_strings = enders + space_strings
+        do_newline = False
+        do_space = False
+        doing_single_comment = False
+        previous_before_comment = ''
+        doing_multi_comment = False
+        in_re = False
+        in_quote = ''
+        quote_buf = []
+        
+        previous = read(1)
+        next1 = read(1)
+        if previous == '/':
+            if next1 == '/':
+                doing_single_comment = True
+            elif next1 == '*':
+                doing_multi_comment = True
+                previous = next1
+                next1 = read(1)
+            else:
+                write(previous)
+        elif not previous:
+            return
+        elif previous >= '!':
+            if previous in "'\"":
+                in_quote = previous
+            write(previous)
+            previous_non_space = previous
         else:
-          new_lines.append("")
-          continue
-
-      if not self.in_comment:
-        line = re.sub(r"/\*.*?\*/", " ", line)
-        line = re.sub(r"//.*", "", line)
-        m = re.search(r"/\*", line)
-        if m:
-          line = line[:m.start()]
-          self.in_comment = True
-
-      # Strip leading and trailing spaces.
-      line = re.sub(r"^ +", "", line)
-      line = re.sub(r" +$", "", line)
-      # A regexp that matches a literal string surrounded by "double quotes".
-      # This regexp can handle embedded backslash-escaped characters including
-      # embedded backslash-escaped double quotes.
-      double_quoted_string = r'"(?:[^"\\]|\\.)*"'
-      # A regexp that matches a literal string surrounded by 'double quotes'.
-      single_quoted_string = r"'(?:[^'\\]|\\.)*'"
-      # A regexp that matches a regexp literal surrounded by /slashes/.
-      # Don't allow a regexp to have a ) before the first ( since that's a
-      # syntax error and it's probably just two unrelated slashes.
-      # Also don't allow it to come after anything that can only be the
-      # end of a primary expression.
-      slash_quoted_regexp = r"(?<![\w$'\")\]])/(?:(?=\()|(?:[^()/\\]|\\.)+)(?:\([^/\\]|\\.)*/"
-      # Replace multiple spaces with a single space.
-      line = re.sub("|".join([double_quoted_string,
-                              single_quoted_string,
-                              slash_quoted_regexp,
-                              "( )+"]),
-                    self.RemoveSpaces,
-                    line)
-      # Strip single spaces unless they have an identifier character both before
-      # and after the space.  % and $ are counted as identifier characters.
-      line = re.sub("|".join([double_quoted_string,
-                              single_quoted_string,
-                              slash_quoted_regexp,
-                              r"(?<![a-zA-Z_0-9$%]) | (?![a-zA-Z_0-9$%])()"]),
-                    self.RemoveSpaces,
-                    line)
-      # Collect keywords and identifiers that are already in use.
-      if self.nesting == 0:
-        re.sub(r"([a-zA-Z0-9_$%]+)", self.LookAtIdentifier, line)
-      function_declaration_regexp = (
-          r"\bfunction"              # Function definition keyword...
-          r"( [\w$%]+)?"             # ...optional function name...
-          r"\([\w$%,]+\)\{")         # ...argument declarations.
-      # Unfortunately the keyword-value syntax { key:value } makes the key look
-      # like a variable where in fact it is a literal string.  We use the
-      # presence or absence of a question mark to try to distinguish between
-      # this case and the ternary operator: "condition ? iftrue : iffalse".
-      if re.search(r"\?", line):
-        block_trailing_colon = r""
-      else:
-        block_trailing_colon = r"(?![:\w$%])"
-      # Variable use.  Cannot follow a period precede a colon.
-      variable_use_regexp = r"(?<![.\w$%])[\w$%]+" + block_trailing_colon
-      line = re.sub("|".join([double_quoted_string,
-                              single_quoted_string,
-                              slash_quoted_regexp,
-                              r"\{",                  # Curly braces.
-                              r"\}",
-                              r"\bvar [\w$%,]+",      # var declarations.
-                              function_declaration_regexp,
-                              variable_use_regexp]),
-                    self.Declaration,
-                    line)
-      new_lines.append(line)
-
-    return "\n".join(new_lines) + "\n"
+            previous_non_space = ' '
+        if not next1:
+            return
+
+        while 1:
+            next2 = read(1)
+            if not next2:
+                last = next1.strip()
+                if not (doing_single_comment or doing_multi_comment)\
+                    and last not in ('', '/'):
+                    if in_quote:
+                        write(''.join(quote_buf))
+                    write(last)
+                break
+            if doing_multi_comment:
+                if next1 == '*' and next2 == '/':
+                    doing_multi_comment = False
+                    next2 = read(1)
+            elif doing_single_comment:
+                if next1 in '\r\n':
+                    doing_single_comment = False
+                    while next2 in '\r\n':
+                        next2 = read(1)
+                        if not next2:
+                            break
+                    if previous_before_comment in ')}]':
+                        do_newline = True
+                    elif previous_before_comment in space_strings:
+                        write('\n')
+            elif in_quote:
+                quote_buf.append(next1)
+
+                if next1 == in_quote:
+                    numslashes = 0
+                    for c in reversed(quote_buf[:-1]):
+                        if c != '\\':
+                            break
+                        else:
+                            numslashes += 1
+                    if numslashes % 2 == 0:
+                        in_quote = ''
+                        write(''.join(quote_buf))
+            elif next1 in '\r\n':
+                if previous_non_space in newlineend_strings \
+                    or previous_non_space > '~':
+                    while 1:
+                        if next2 < '!':
+                            next2 = read(1)
+                            if not next2:
+                                break
+                        else:
+                            if next2 in newlinestart_strings \
+                                or next2 > '~' or next2 == '/':
+                                do_newline = True
+                            break
+            elif next1 < '!' and not in_re:
+                if (previous_non_space in space_strings \
+                    or previous_non_space > '~') \
+                    and (next2 in space_strings or next2 > '~'):
+                    do_space = True
+                elif previous_non_space in '-+' and next2 == previous_non_space:
+                    # protect against + ++ or - -- sequences
+                    do_space = True
+                elif self.is_return and next2 == '/':
+                    # returning a regex...
+                    write(' ')
+            elif next1 == '/':
+                if do_space:
+                    write(' ')
+                if in_re:
+                    if previous != '\\' or next2 in 'gimy':
+                        in_re = False
+                    write('/')
+                elif next2 == '/':                    
+                    doing_single_comment = True
+                    previous_before_comment = previous_non_space
+                elif next2 == '*':
+                    doing_multi_comment = True
+                    previous = next1
+                    next1 = next2
+                    next2 = read(1)
+                else:
+                    in_re = previous_non_space in '(,=:[?!&|' or self.is_return # literal regular expression
+                    write('/')
+            else:
+                if do_space:
+                    do_space = False
+                    write(' ')
+                if do_newline:
+                    write('\n')
+                    do_newline = False
+                    
+                write(next1)
+                if not in_re and next1 in "'\"":
+                    in_quote = next1
+                    quote_buf = []
+
+            previous = next1
+            next1 = next2
+
+            if previous >= '!':
+                previous_non_space = previous
+
+if __name__ == '__main__':
+	import sys, os, glob
+
+#for f in sys.argv[1:]:
+#    with open(f, 'r') as js:
+#        minifier = JavascriptMinify(js, sys.stdout)
+#        minifier.minify()
+#    sys.stdout.write('\n')
+
+	minifier = JavascriptMinify(sys.stdin, sys.stdout)
+	minifier.minify()
+	sys.stdout.write('\n')


More information about the scm-commits mailing list