diff --git a/Applications/QuickLookGenerator/resources/Info.plist b/Applications/QuickLookGenerator/resources/Info.plist
index cbe5ca86..0b6f7885 100644
--- a/Applications/QuickLookGenerator/resources/Info.plist
+++ b/Applications/QuickLookGenerator/resources/Info.plist
@@ -47,5 +47,11 @@
+ QLThumbnailMinimumSize
+ 32
+ QLSupportsConcurrentRequests
+
+ QLNeedsToBeRunInMainThread
+
diff --git a/Applications/QuickLookGenerator/src/generate.mm b/Applications/QuickLookGenerator/src/generate.mm
index c89bed40..64a0094d 100644
--- a/Applications/QuickLookGenerator/src/generate.mm
+++ b/Applications/QuickLookGenerator/src/generate.mm
@@ -17,6 +17,8 @@
OAK_EXTERN_C_BEGIN
+static std::string const kMacClassicThemeUUID = "71D40D9D-AE48-11D9-920A-000D93589AF6";
+
static void initialize (CFBundleRef generatorBundle)
{
static dispatch_once_t onceToken;
@@ -42,12 +44,113 @@ static void initialize (CFBundleRef generatorBundle)
});
}
+static std::string URLtoString (CFURLRef url)
+{
+ std::string filePath = NULL_STR;
+ if(CFStringRef path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle))
+ {
+ filePath = cf::to_s(path);
+ CFRelease(path);
+ }
+ return filePath;
+}
+
// =========================
// = QLGenerator interface =
// =========================
OSStatus TextMateQuickLookPlugIn_GenerateThumbnailForURL (void* instance, QLThumbnailRequestRef request, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options, CGSize maxSize)
{
+ initialize(QLThumbnailRequestGetGeneratorBundle(request));
+
+ // Load file
+ ng::buffer_t buffer;
+ std::string filePath = URLtoString(url);
+ std::string fileContents = file::read_utf8(filePath, nullptr, 1024); // 1Kb should be more than enough
+
+ // Trim to 50 lines, since we don't need more
+ size_t maxLines = 50;
+ fileContents.erase(std::find_if(fileContents.begin(), fileContents.end(), [&maxLines](char ch) { return ch == '\n' && --maxLines == 0; }), fileContents.end());
+ buffer.insert(0, fileContents);
+
+ // Check if cancelled
+ if(QLThumbnailRequestIsCancelled(request))
+ return noErr;
+
+ // Apply appropriate grammar
+ std::string fileType = file::type(filePath, std::make_shared(fileContents.data(), fileContents.size(), false));
+ if(fileType != NULL_STR)
+ {
+ for(auto item : bundles::query(bundles::kFieldGrammarScope, fileType, scope::wildcard, bundles::kItemTypeGrammar))
+ {
+ buffer.set_grammar(item);
+ break;
+ }
+ }
+
+ // Check if cancelled
+ if(QLThumbnailRequestIsCancelled(request))
+ return noErr;
+
+ // Apply appropriate theme
+ theme_ptr theme = parse_theme(bundles::lookup(kMacClassicThemeUUID));
+ if(!theme)
+ return noErr;
+
+ NSFont* font = [NSFont userFixedPitchFontOfSize:4];
+ theme = theme->copy_with_font_name_and_size(to_s([font fontName]), [font pointSize]);
+
+ // Check if cancelled
+ if(QLThumbnailRequestIsCancelled(request))
+ return noErr;
+
+ // Perform syntax highlighting
+ buffer.wait_for_repair();
+ std::map scopes = buffer.scopes(0, buffer.size());
+
+ // Construct RTF output
+ NSMutableAttributedString* output = (__bridge_transfer NSMutableAttributedString*)CFAttributedStringCreateMutable(kCFAllocatorDefault, buffer.size());
+ size_t from = 0;
+ for(auto pair = scopes.begin(); pair != scopes.end(); )
+ {
+ styles_t styles = theme->styles_for_scope(pair->second);
+
+ size_t to = ++pair != scopes.end() ? pair->first : buffer.size();
+
+ [output appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithCxxString:buffer.substr(from, to)] attributes:@{
+ NSForegroundColorAttributeName : [NSColor tmColorWithCGColor:styles.foreground()],
+ NSBackgroundColorAttributeName : [NSColor whiteColor],
+ NSFontAttributeName : font,
+ NSUnderlineStyleAttributeName : @(styles.underlined() ? NSUnderlineStyleSingle : NSUnderlineStyleNone),
+ }]];
+
+ from = to;
+ }
+
+ // Check if cancelled
+ if(QLThumbnailRequestIsCancelled(request))
+ return noErr;
+
+ // w/e the 3rd parameter, the context will always be a bitmap context
+ CGContextRef bitmapContext = QLThumbnailRequestCreateContext(request, maxSize, true, NULL);
+ if(bitmapContext)
+ {
+ NSGraphicsContext* context = [NSGraphicsContext graphicsContextWithGraphicsPort:bitmapContext flipped:YES];
+ if(context)
+ {
+ [NSGraphicsContext saveGraphicsState];
+ [NSGraphicsContext setCurrentContext:context];
+ CGContextSaveGState(bitmapContext);
+ CGContextTranslateCTM(bitmapContext, 0.0, maxSize.height);
+ CGContextScaleCTM(bitmapContext, 1.0, -1.0);
+ [output drawAtPoint:(CGPoint){0.0, 0.0}];
+ CGContextRestoreGState(bitmapContext);
+ [NSGraphicsContext restoreGraphicsState];
+ }
+ QLThumbnailRequestFlushContext(request, bitmapContext);
+ CGContextRelease(bitmapContext);
+ }
+
return noErr;
}
@@ -61,14 +164,7 @@ OSStatus TextMateQuickLookPlugIn_GeneratePreviewForURL (void* instance, QLPrevie
// Load file
ng::buffer_t buffer;
-
- std::string filePath = NULL_STR;
- if(CFStringRef path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle))
- {
- filePath = cf::to_s(path);
- CFRelease(path);
- }
-
+ std::string filePath = URLtoString(url);
std::string fileContents = file::read_utf8(filePath, nullptr, 20480);
buffer.insert(0, fileContents);
diff --git a/Applications/QuickLookGenerator/target b/Applications/QuickLookGenerator/target
index 9e3ddbc9..73be01b1 100644
--- a/Applications/QuickLookGenerator/target
+++ b/Applications/QuickLookGenerator/target
@@ -1,7 +1,7 @@
TARGET_NAME = TextMateQL
SOURCES = src/*.{c,mm}
CP_Resources = resources/*
-FRAMEWORKS = CoreFoundation QuickLook
+FRAMEWORKS = CoreFoundation QuickLook AppKit
LINK += layout buffer bundles file cf ns OakAppKit OakFoundation
LN_FLAGS -= -Wl,-dead_strip_dylibs