diff --git a/ChangeLog.txt b/ChangeLog.txt index 680ed3a..d9533a7 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -1,5 +1,6 @@ 0.2 (in progress): + - Added thumbnailing support - Changed .tex UTI to agree with TeXShop's. - - Let the system pick a different plugin if pygmentize fails + - (Try to) let the system pick a different plugin if pygmentize fails 0.1: Initial release \ No newline at end of file diff --git a/Common.h b/Common.h index e254533..6993641 100644 --- a/Common.h +++ b/Common.h @@ -16,6 +16,6 @@ #endif // Status is 0 on success, nonzero on error (like a shell command) -// If thumbnail is 1, only render the top 50 lines of the file +// If thumbnail is 1, only render enough of the file for a thumbnail NSData *colorizeURL(CFBundleRef myBundle, CFURLRef url, int *status, int thumbnail); diff --git a/Common.m b/Common.m index 524b400..22d4bac 100644 --- a/Common.m +++ b/Common.m @@ -65,12 +65,12 @@ else thumbString = "0"; NSString *cmd = [NSString stringWithFormat: - @"\"%s/colorize.sh\" \"%s\" \"%s\" %s", - rsrcDirBuf, rsrcDirBuf, targetBuf, thumbString]; + @"PYTHONPATH=\"%s:%s/pygments\" \"%s/colorize.py\" \"%s\" \"%s\" %s", + rsrcDirBuf, rsrcDirBuf, rsrcDirBuf, rsrcDirBuf, targetBuf, thumbString]; output = runTask(cmd, status); if (*status != 0) { - NSLog(@"QLColorCode: colorize.sh failed with exit code %d", *status); + NSLog(@"QLColorCode: colorize.py failed with exit code %d", *status); } done: free(targetBuf); diff --git a/GeneratePreviewForURL.m b/GeneratePreviewForURL.m index f48bd7f..13a1929 100644 --- a/GeneratePreviewForURL.m +++ b/GeneratePreviewForURL.m @@ -17,6 +17,7 @@ OSStatus GeneratePreviewForURL(void *thisInterface, QLPreviewRequestRef preview, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options) { + NSDate *startDate = [NSDate date]; n8log(@"Generating Preview"); if (QLPreviewRequestIsCancelled(preview)) return noErr; @@ -27,9 +28,12 @@ OSStatus GeneratePreviewForURL(void *thisInterface, QLPreviewRequestRef preview, CFBundleRef bundle = QLPreviewRequestGetGeneratorBundle(preview); int status; NSData *output = colorizeURL(bundle, url, &status, 0); + n8log(@"Generated preview html page in %.3f sec", -[startDate timeIntervalSinceNow] ); if (status != 0 || QLPreviewRequestIsCancelled(preview)) { +#ifndef DEBUG goto done; +#endif } // Now let WebKit do its thing CFDictionaryRef emptydict = @@ -40,6 +44,7 @@ OSStatus GeneratePreviewForURL(void *thisInterface, QLPreviewRequestRef preview, emptydict); done: + n8log(@"Finished preview in %.3f sec", -[startDate timeIntervalSinceNow] ); [pool release]; return noErr; } diff --git a/GenerateThumbnailForURL.m b/GenerateThumbnailForURL.m index 6da0adb..c9fcdae 100644 --- a/GenerateThumbnailForURL.m +++ b/GenerateThumbnailForURL.m @@ -25,6 +25,7 @@ OSStatus GenerateThumbnailForURL(void *thisInterface, if (maxSize.width < minSize || maxSize.height < minSize) return noErr; NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + NSDate *startDate = [NSDate date]; // Render as though there is an 600x800 window, and fill the thumbnail // vertically. This code could be more general. I'm assuming maxSize is @@ -43,8 +44,11 @@ OSStatus GenerateThumbnailForURL(void *thisInterface, CFBundleRef bundle = QLThumbnailRequestGetGeneratorBundle(thumbnail); NSData *data = colorizeURL(bundle, url, &status, 1); //NSLog(@"%s", [data bytes]); + n8log(@"Generated thumbnail html page in %.3f sec", -[startDate timeIntervalSinceNow] ); if (status != 0) { +#ifndef DEBUG goto done; +#endif } //NSRect previewRect; //previewRect.size = previewSize; @@ -78,6 +82,7 @@ OSStatus GenerateThumbnailForURL(void *thisInterface, CFRelease(context); } done: + n8log(@"Finished thumbnail after %.3f sec\n\n", -[startDate timeIntervalSinceNow] ); [pool release]; return noErr; } diff --git a/QLColorCode.xcodeproj/project.pbxproj b/QLColorCode.xcodeproj/project.pbxproj index f0b5035..90476bd 100644 --- a/QLColorCode.xcodeproj/project.pbxproj +++ b/QLColorCode.xcodeproj/project.pbxproj @@ -26,10 +26,10 @@ 0E8776580D08E004005040B4 /* Common.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E8776560D08E004005040B4 /* Common.m */; }; 0E8776B70D08ED64005040B4 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E8776B60D08ED64005040B4 /* WebKit.framework */; }; 0E8776BD0D08EDA4005040B4 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E8776BC0D08EDA4005040B4 /* AppKit.framework */; }; + 0EA91CB70D0E746600F04340 /* colorize.py in Resources */ = {isa = PBXBuildFile; fileRef = 0EA91CB60D0E746600F04340 /* colorize.py */; }; 0EC047970CFBDADF009C986E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0EC047960CFBDADF009C986E /* Foundation.framework */; }; 0ECBBA6C0CFCA37500416538 /* QLColorCode.qlgenerator in Copy to Library */ = {isa = PBXBuildFile; fileRef = 8D576316048677EA00EA77CD /* QLColorCode.qlgenerator */; }; - 0ECBBB5E0CFD447E00416538 /* colorize.sh in Resources */ = {isa = PBXBuildFile; fileRef = 0ECBBB5D0CFD447E00416538 /* colorize.sh */; }; - 0ECBBD090CFE4B9800416538 /* nautumn.py in Install Style */ = {isa = PBXBuildFile; fileRef = 0ECBBCFC0CFE4AC100416538 /* nautumn.py */; }; + 0ECBBD090CFE4B9800416538 /* nautumn.py in Resources */ = {isa = PBXBuildFile; fileRef = 0ECBBCFC0CFE4AC100416538 /* nautumn.py */; }; 2C05A19C06CAA52B00D84F6F /* GeneratePreviewForURL.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C05A19B06CAA52B00D84F6F /* GeneratePreviewForURL.m */; }; 61E3BCFB0870B4F2002186A0 /* GenerateThumbnailForURL.m in Sources */ = {isa = PBXBuildFile; fileRef = 61E3BCFA0870B4F2002186A0 /* GenerateThumbnailForURL.m */; }; 8D576312048677EA00EA77CD /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = 08FB77B6FE84183AC02AAC07 /* main.c */; settings = {ATTRIBUTES = (); }; }; @@ -62,17 +62,6 @@ name = "Copy to Library"; runOnlyForDeploymentPostprocessing = 0; }; - 0ECBBD060CFE4B1A00416538 /* Install Style */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = "pygments/Pygments-0.9-py2.5.egg/pygments/styles"; - dstSubfolderSpec = 7; - files = ( - 0ECBBD090CFE4B9800416538 /* nautumn.py in Install Style */, - ); - name = "Install Style"; - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ @@ -84,6 +73,7 @@ 0E8776560D08E004005040B4 /* Common.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Common.m; sourceTree = ""; }; 0E8776B60D08ED64005040B4 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = SDKs/MacOSX10.5.sdk/System/Library/Frameworks/WebKit.framework; sourceTree = DEVELOPER_DIR; }; 0E8776BC0D08EDA4005040B4 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = SDKs/MacOSX10.5.sdk/System/Library/Frameworks/AppKit.framework; sourceTree = DEVELOPER_DIR; }; + 0EA91CB60D0E746600F04340 /* colorize.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = colorize.py; sourceTree = ""; }; 0EC047960CFBDADF009C986E /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = SDKs/MacOSX10.5.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; 0EC047F20CFC000A009C986E /* Notes.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Notes.txt; sourceTree = ""; }; 0ECBBB360CFCF98600416538 /* ReadMe.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ReadMe.txt; sourceTree = ""; }; @@ -162,6 +152,7 @@ 2C05A19B06CAA52B00D84F6F /* GeneratePreviewForURL.m */, 08FB77B6FE84183AC02AAC07 /* main.c */, 0ECBBB5D0CFD447E00416538 /* colorize.sh */, + 0EA91CB60D0E746600F04340 /* colorize.py */, 0ECBBCFC0CFE4AC100416538 /* nautumn.py */, ); name = Source; @@ -198,7 +189,6 @@ 8D576311048677EA00EA77CD /* Sources */, 8D576313048677EA00EA77CD /* Frameworks */, 0EC048F20CFC103D009C986E /* Install Pygments */, - 0ECBBD060CFE4B1A00416538 /* Install Style */, 0ECBBA6F0CFCA39E00416538 /* Copy to Library */, ); buildRules = ( @@ -235,7 +225,8 @@ buildActionMask = 2147483647; files = ( 8D5B49A804867FD3000E48DA /* InfoPlist.strings in Resources */, - 0ECBBB5E0CFD447E00416538 /* colorize.sh in Resources */, + 0EA91CB70D0E746600F04340 /* colorize.py in Resources */, + 0ECBBD090CFE4B9800416538 /* nautumn.py in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -342,6 +333,7 @@ GCC_PRECOMPILE_PREFIX_HEADER = NO; INFOPLIST_FILE = Info.plist; INSTALL_PATH = /Library/QuickLook; + OTHER_CFLAGS = "-DDEBUG"; PRODUCT_NAME = QLColorCode; VALID_ARCHS = "i386 ppc"; WRAPPER_EXTENSION = qlgenerator; diff --git a/colorize.py b/colorize.py new file mode 100755 index 0000000..fac8ce8 --- /dev/null +++ b/colorize.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python +# +# colorize.py +# QLColorCode +# +# Created by Nathaniel Gray on 12/10/07. +# Copyright (c) 2007 Nathaniel Gray. All rights reserved. +# + +import os, sys +rsrcDir = sys.argv[1] +#os.chdir( rsrcDir + "/pygments") +#sys.path += '.' + +import fnmatch +import pygments +from pygments.lexers import get_lexer_for_filename, get_lexer_by_name +from pygments.formatters import HtmlFormatter +from pygments import highlight + +# If you want a different style change this to a string with one of these (e.g. "trac") +# Styles: manni, perldoc, borland, colorful, default, murphy, trac, fruity, autumn, +# emacs, pastie, friendly, native +#style = "autumn" +from nautumn import NautumnStyle +style = NautumnStyle + +overrides = ( + # pattern, pre-filter, lexer + ('*.plist', '/usr/bin/plutil -convert xml1 -o - "%s"', 'xml'), + ('*.mm', None, 'objc'), + ('*.c[cp]', None, 'c++'), + ('*.hh', None, 'c++'), + ('*.command', None, 'sh') + ) + +target = sys.argv[2] +thumb = sys.argv[3] + +thumbBytes = 1024*4 # Only read this many bytes for thumbnail generation + +if thumb == "1": + limit = thumbBytes +else: + limit = -1 + +# Use a custom formatter to add style info for the 'pre' tag +class customHtmlFormatter(HtmlFormatter): + def wrap(self, source, outfile): + return self._wrap_code(source) + + def _wrap_code(self, source): + yield 0, '
'
+        for i, t in source:
+            #if i == 1:
+                # it's a line of formatted code
+                #t += '\n'
+            yield i, t
+        yield 0, '
' + +# Find a matching lexer +match = None +for pattern, filter, lexer in overrides: + if fnmatch.fnmatch(target, pattern): + match = (filter % (target), get_lexer_by_name(lexer)) +if match == None: + try: + match = (None, get_lexer_for_filename(target)) + except ClassNotFound: + match = (None, get_lexer_for_filename("foo.txt")) + +# Create the formatter +formatter = customHtmlFormatter(outencoding="UTF-8", full=True, style=style) + +# Ok, everything is set up. Read the file, possibly filtering it +if match[0] == None: + inFile = open(target, "rt") +else: + (childIn, inFile) = os.popen4(match[0]) + childIn.close() + +contents = inFile.read(limit) +inFile.close() +highlight(contents, match[1], formatter, sys.stdout) diff --git a/colorize.sh b/colorize.sh index 4a0cba0..9821d05 100755 --- a/colorize.sh +++ b/colorize.sh @@ -23,35 +23,41 @@ thumb=$3 thumblines=50 export PYTHONPATH=$rsrcDir/pygments + pyg=$rsrcDir/pygments/pygmentize # Styles: manni, perldoc, borland, colorful, default, murphy, trac, fruity, autumn, # emacs, pastie, friendly, native # dark styles: native, fruity # autumn is almost nice, except that the comments are too light # don't like: murphy -pygOpts=(-f html -O outencoding=UTF-8,full=True,style=nautumn -P "cssstyles=font-size: small") +pygOpts=(-f html -O outencoding=UTF-8,full=True,style=autumn -P "cssstyles=font-size: small") font=Monaco case $target in *.plist ) if [ $thumb = "1" ]; then - pyg="head -n $thumblines | $pyg" + filter=(head -n $thumblines) + else + filter=cat fi - /usr/bin/plutil -convert xml1 -o - $target | $pyg -l xml $pygOpts \ + /usr/bin/plutil -convert xml1 -o - $target | $filter | $pyg -l xml $pygOpts \ | sed "s/pre *{/pre { font-family: $font; /" ;; * ) - if [ $thumb = "1" ]; then - #echo "making thumbnail: $thumb" >> ~/qlcc-debug.txt - tmpDir=`mktemp -d -t qlcolorcode-XXXXXX` - tgtBase="`basename $target`" - head -n $thumblines $target > $tmpDir/$tgtBase - target=$tmpDir/$tgtBase - fi - $pyg $pygOpts $target \ - | sed "s/pre *{/pre { font-family: $font; /" - if [ $thumb = "1" ]; then - rm -rf $tmpDir - fi + { + if [ $thumb = "1" ]; then + #echo "making thumbnail: $thumb" >> ~/qlcc-debug.txt + tmpDir=`mktemp -d -t qlcolorcode-XXXXXX` + tgtBase="`basename $target`" + head -n $thumblines $target > $tmpDir/$tgtBase + target=$tmpDir/$tgtBase + fi + $pyg $pygOpts $target \ + | sed "s/pre *{/pre { font-family: $font; /" + } always { + if [ $thumb = "1" ]; then + rm -rf $tmpDir + fi + } ;; esac