Skip to content

Commit

Permalink
Add tar/gzip support
Browse files Browse the repository at this point in the history
  • Loading branch information
samuelmeuli committed Apr 21, 2020
1 parent f4edd67 commit f08b7f4
Show file tree
Hide file tree
Showing 8 changed files with 124 additions and 17 deletions.
4 changes: 4 additions & 0 deletions Glance.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
7E6EF1FD240CC802009E4199 /* Quartz.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7E6EF1FC240CC802009E4199 /* Quartz.framework */; };
7E6EF200240CC802009E4199 /* MainVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E6EF1FF240CC802009E4199 /* MainVC.swift */; };
7E6EF208240CC802009E4199 /* QLPlugin.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 7E6EF1FA240CC802009E4199 /* QLPlugin.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
7E7A9DAD244F24B900276E51 /* TARPreviewVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E7A9DAC244F24B900276E51 /* TARPreviewVC.swift */; };
7E8492E0244B8BA60013E55A /* chroma-v0.7.0 in Copy Files */ = {isa = PBXBuildFile; fileRef = 7E9F0D592416286A007F1008 /* chroma-v0.7.0 */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
7E8492E1244B8BA80013E55A /* nbtohtml-v0.4.0 in Copy Files */ = {isa = PBXBuildFile; fileRef = 7E8DEDAF242BEF3700F2DABB /* nbtohtml-v0.4.0 */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
7E8DEDAB242BE1FA00F2DABB /* Down in Frameworks */ = {isa = PBXBuildFile; productRef = 7E8DEDAA242BE1FA00F2DABB /* Down */; };
Expand Down Expand Up @@ -179,6 +180,7 @@
7E6EF1FF240CC802009E4199 /* MainVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainVC.swift; sourceTree = "<group>"; };
7E6EF204240CC802009E4199 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
7E6EF205240CC802009E4199 /* QLPlugin.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = QLPlugin.entitlements; sourceTree = "<group>"; };
7E7A9DAC244F24B900276E51 /* TARPreviewVC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TARPreviewVC.swift; sourceTree = "<group>"; };
7E8DEDAF242BEF3700F2DABB /* nbtohtml-v0.4.0 */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = "nbtohtml-v0.4.0"; sourceTree = "<group>"; };
7E8DEDBE242C20A000F2DABB /* OfflineWebView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OfflineWebView.swift; sourceTree = "<group>"; };
7E9282102449AB7100DDCFBF /* TablePreviewView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TablePreviewView.xib; sourceTree = "<group>"; };
Expand Down Expand Up @@ -336,6 +338,7 @@
7E413F7F2418DD6200CFBB1D /* CSVPreviewVC.swift */,
7EB7491824228549007265A4 /* JupyterPreviewVC.swift */,
7E1DC520240E6D8000D0A061 /* MarkdownPreviewVC.swift */,
7E7A9DAC244F24B900276E51 /* TARPreviewVC.swift */,
7EB36489244B665700D7F96F /* ZIPPreviewVC.swift */,
);
path = FileTypes;
Expand Down Expand Up @@ -805,6 +808,7 @@
7E9F0D7F24168870007F1008 /* WebAsset.swift in Sources */,
7E6EF200240CC802009E4199 /* MainVC.swift in Sources */,
7E9282152449B96600DDCFBF /* LoadableNib.swift in Sources */,
7E7A9DAD244F24B900276E51 /* TARPreviewVC.swift in Sources */,
7E1DC528240E6F4A00D0A061 /* PreviewVCFactory.swift in Sources */,
7EB36484244B0CCE00D7F96F /* TablePreviewVC.swift in Sources */,
7E1DC521240E6D8000D0A061 /* MarkdownPreviewVC.swift in Sources */,
Expand Down
2 changes: 1 addition & 1 deletion Glance/Credits.rtf
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
\ls2\ilvl0
\f0\b Archives\
\ls2\ilvl0
\f1\b0 .zip\
\f1\b0 .tar, .tar.gz, .zip\
\
\pard\tx220\tx720\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\li720\fi-720\pardirnatural\qc\partightenfactor0
\ls3\ilvl0
Expand Down
2 changes: 2 additions & 0 deletions QLPlugin/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@
<string>public.data</string>

<!-- Archive -->
<string>org.gnu.gnu-zip-archive</string> <!-- .gz -->
<string>public.tar-archive</string> <!-- .tar -->
<string>public.zip-archive</string> <!-- .zip -->

<!-- CSV -->
Expand Down
22 changes: 12 additions & 10 deletions QLPlugin/MainVC.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,16 +81,18 @@ class MainVC: NSViewController, QLPreviewingController {
/// Generates a preview of the selected file and adds the corresponding child view controller
private func previewFile(file: File) throws {
// Initialize `PreviewVC` for the file type
let previewVCType = PreviewVCFactory.getView(fileExtension: file.url.pathExtension)
let previewVC = previewVCType.init(file: file)
if let previewVCType = PreviewVCFactory.getView(fileURL: file.url) {
// Generate file preview
let previewVC = previewVCType.init(file: file)
try previewVC.loadPreview()

// Generate file preview
try previewVC.loadPreview()

// Add `PreviewVC` as a child view controller
addChild(previewVC)
previewVC.view.autoresizingMask = [.height, .width]
previewVC.view.frame = view.frame
view.addSubview(previewVC.view)
// Add `PreviewVC` as a child view controller
addChild(previewVC)
previewVC.view.autoresizingMask = [.height, .width]
previewVC.view.frame = view.frame
view.addSubview(previewVC.view)
} else {
os_log("Skipping preview for file %s: File type not supported", type: .debug, file.path)
}
}
}
94 changes: 94 additions & 0 deletions QLPlugin/Views/FileTypes/TARPreviewVC.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import Cocoa
import Foundation
import os.log
import SwiftExec

/// View controller for previewing tarballs (may be gzipped).
class TARPreviewVC: OutlinePreviewVC, PreviewVC {
let linesRegex = #"([\w-]{10}) \d+ .+ .+ + (\d+) (\w{3} \d+ +[\d:]+) (.*)"#

let byteCountFormatter = ByteCountFormatter()
let dateFormatter1 = DateFormatter()
let dateFormatter2 = DateFormatter()

override init(nibName nibNameOrNil: NSNib.Name?, bundle nibBundleOrNil: Bundle?, file: File) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil, file: file)
initDateFormatters()
}

/// Sets up `dateFormatter1` and `dateFormatter2` to parse date strings from `tar` output. Date
/// strings may be in one of the following formats:
///
/// - "MMM dd HH:mm", e.g. "Mar 28 15:36" (date is in current year)
/// - "MMM dd yyyy", e.g. "Dec 29 2018"
private func initDateFormatters() {
// Set default date to today to parse dates in current year
dateFormatter1.defaultDate = Date()

// Specify date formats
dateFormatter1.dateFormat = "MMM dd HH:mm"
dateFormatter2.dateFormat = "MMM dd yyyy"
}

/// Parses a date string from `tar` output to a `Date` object.
private func parseDate(dateString: String) -> Date? {
if dateString.contains(":") {
return dateFormatter1.date(from: dateString)
} else {
return dateFormatter2.date(from: dateString)
}
}

/// Parses the output of the `tar` command.
private func parseTARFileTree(file: File, lines: String) -> FileTree {
let fileTree = FileTree()

// Create node for root directory (not contained in `tar` output)
try! fileTree.addNode(
path: file.url.lastPathComponent.components(separatedBy: ".")[0],
isDirectory: true,
size: 0,
dateModified: file.attributes[FileAttributeKey.modificationDate] as? Date ?? Date()
)

// Content lines: "-rw-r--r-- 0 user staff 642 Dec 29 2018 my-tar/file.ext"
// - "-" as first character indicates a file, "d" a directory
// - Digits before date indicate number of bytes
let linesMatched = lines.matchRegex(regex: linesRegex)
for match in linesMatched {
do {
// Add file/directory node to tree
try fileTree.addNode(
path: match[4],
isDirectory: match[1].first == "d",
size: Int(match[2]) ?? -1,
dateModified: parseDate(dateString: match[3]) ?? Date()
)
} catch {
os_log("%s", type: .error, error.localizedDescription)
}
}

return fileTree
}

func loadPreview() throws {
// Run `tar` command
let result = try exec(
program: "/usr/bin/tar",
arguments: [
"--gzip", // Allows listing contents of `.tar.gz` files
"--list",
"--verbose",
"--file",
file.path,
]
)

// Parse command output
let fileTree = parseTARFileTree(file: file, lines: result.stdout ?? "")

// Load data into outline view
loadData(fileTree: fileTree, labelText: nil)
}
}
9 changes: 7 additions & 2 deletions QLPlugin/Views/PreviewVCFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,19 @@ import Foundation
/// Returns an instance of the `PreviewVC` subclass that should be used for generating previews of
/// files with the provided extension
class PreviewVCFactory {
static func getView(fileExtension: String) -> PreviewVC.Type {
switch fileExtension {
static func getView(fileURL: URL) -> PreviewVC.Type? {
switch fileURL.pathExtension {
case "csv", "tab", "tsv":
return CSVPreviewVC.self
case "gz":
// `gzip` is only supported for tarballs
return fileURL.path.hasSuffix(".tar.gz") ? TARPreviewVC.self : nil
case "md", "markdown", "mdown", "mkdn", "mkd":
return MarkdownPreviewVC.self
case "ipynb":
return JupyterPreviewVC.self
case "tar":
return TARPreviewVC.self
case "zip":
return ZIPPreviewVC.self
default:
Expand Down
2 changes: 1 addition & 1 deletion QLPlugin/Views/ViewTypes/OutlinePreviewVC.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class OutlinePreviewVC: NSViewController {
view = NSView()
}

func loadData(fileTree: FileTree, labelText: String) {
func loadData(fileTree: FileTree, labelText: String?) {
let outlineView = OutlinePreviewView(
frame: view.frame,
fileTree: fileTree,
Expand Down
6 changes: 3 additions & 3 deletions QLPlugin/Views/ViewTypes/OutlinePreviewView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ class OutlinePreviewView: NSView, LoadableNib {
@IBOutlet private var outlineView: NSOutlineView!

@objc dynamic var fileTreeNodes: [FileTreeNode]
let labelText: String
let labelText: String?

private let treeController = NSTreeController()

required init(frame: CGRect, fileTree: FileTree, labelText: String) {
required init(frame: CGRect, fileTree: FileTree, labelText: String?) {
fileTreeNodes = Array(fileTree.root.children.values)
self.labelText = labelText

Expand Down Expand Up @@ -49,7 +49,7 @@ class OutlinePreviewView: NSView, LoadableNib {
options: nil
)

label.stringValue = labelText
label.stringValue = labelText ?? ""
}

/// Expands all first-level tree nodes.
Expand Down

0 comments on commit f08b7f4

Please sign in to comment.