diff --git a/Pyto.xcodeproj/project.pbxproj b/Pyto.xcodeproj/project.pbxproj index 4815316ea..eb306f881 100644 --- a/Pyto.xcodeproj/project.pbxproj +++ b/Pyto.xcodeproj/project.pbxproj @@ -142,6 +142,7 @@ C9E415312151531B00D64E32 /* REPL.py in Resources */ = {isa = PBXBuildFile; fileRef = C9E415302151531B00D64E32 /* REPL.py */; }; C9E509A221BC08F900261E3A /* docs in Resources */ = {isa = PBXBuildFile; fileRef = C9E509A121BC08F800261E3A /* docs */; }; C9EA55B3219F752D007546BC /* RelativePathForScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9EA55B2219F752D007546BC /* RelativePathForScript.swift */; }; + C9EA5C7D21F23558000F7FA7 /* Welcome to Pyto.md in Resources */ = {isa = PBXBuildFile; fileRef = C9EA5C7C21F23024000F7FA7 /* Welcome to Pyto.md */; }; C9EDF9AB21446FCA00382DCC /* ConsoleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9EDF9AA21446FCA00382DCC /* ConsoleViewController.swift */; }; C9F04E832173C33400BDD03E /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = C9F04E852173C33400BDD03E /* Localizable.strings */; }; C9F04E882173DD7300BDD03E /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C9F04E8A2173DD7300BDD03E /* Main.storyboard */; }; @@ -448,6 +449,7 @@ C9E509A121BC08F800261E3A /* docs */ = {isa = PBXFileReference; lastKnownFileType = folder; path = docs; sourceTree = SOURCE_ROOT; }; C9E7ABAA2152D0D30007BB87 /* libsqlite3.0.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.0.tbd; path = usr/lib/libsqlite3.0.tbd; sourceTree = SDKROOT; }; C9EA55B2219F752D007546BC /* RelativePathForScript.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelativePathForScript.swift; sourceTree = ""; }; + C9EA5C7C21F23024000F7FA7 /* Welcome to Pyto.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = "Welcome to Pyto.md"; sourceTree = ""; }; C9EDF9A4214461D300382DCC /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; }; C9EDF9A62144620A00382DCC /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; C9EDF9AA21446FCA00382DCC /* ConsoleViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsoleViewController.swift; sourceTree = ""; }; @@ -600,6 +602,7 @@ C9E415302151531B00D64E32 /* REPL.py */, C9B5D87521F214DC00E980BA /* body.html */, C9F04E852173C33400BDD03E /* Localizable.strings */, + C9EA5C7C21F23024000F7FA7 /* Welcome to Pyto.md */, C9E509A121BC08F800261E3A /* docs */, ); name = Resources; @@ -1015,6 +1018,7 @@ C9B5D84B21F119B100E980BA /* About.storyboard in Resources */, C9E509A221BC08F900261E3A /* docs in Resources */, C9C0E78B21F0B5770057C63E /* Theme Chooser.storyboard in Resources */, + C9EA5C7D21F23558000F7FA7 /* Welcome to Pyto.md in Resources */, C98472AE21A05CB1008E16AC /* README.md in Resources */, C9F04E882173DD7300BDD03E /* Main.storyboard in Resources */, C9FCFD6D21442FD700A07974 /* Untitled.py in Resources */, diff --git a/Pyto/View Controllers/DocumentBrowserViewController.swift b/Pyto/View Controllers/DocumentBrowserViewController.swift index 4e8cbdfb9..cd13b0a86 100644 --- a/Pyto/View Controllers/DocumentBrowserViewController.swift +++ b/Pyto/View Controllers/DocumentBrowserViewController.swift @@ -96,6 +96,10 @@ class DocumentBrowserViewController: UIViewController, UICollectionViewDataSourc if let iCloudURL = DocumentBrowserViewController.iCloudContainerURL, directory.pathComponents == DocumentBrowserViewController.localContainerURL.pathComponents { + if let welcome = Bundle.main.url(forResource: "Welcome to Pyto", withExtension: "md") { + files.append(welcome) + } + if !FileManager.default.fileExists(atPath: iCloudURL.path) { try? FileManager.default.createDirectory(at: iCloudURL, withIntermediateDirectories: true, attributes: nil) } diff --git a/Pyto/View Controllers/PlainTextEditorViewController.swift b/Pyto/View Controllers/PlainTextEditorViewController.swift index 0bfb55d48..451197dd0 100644 --- a/Pyto/View Controllers/PlainTextEditorViewController.swift +++ b/Pyto/View Controllers/PlainTextEditorViewController.swift @@ -25,17 +25,23 @@ class PlainTextEditorViewController: UIViewController, UITextViewDelegate { parent?.title = title textView.text = (try? String(contentsOf: url)) ?? "" + textView.contentTextView.isEditable = !isBundled + (parent as? MarkdownSplitViewController)?.previewer.load(markdown: textView.text, baseURL: url.deletingLastPathComponent()) } } } + /// Returns `true` if the file is in the app's bundle. + var isBundled: Bool { + return (url != nil && url!.path.hasPrefix(Bundle.main.bundlePath)) + } + /// Closes this View controller and saves. @objc func close() { dismiss(animated: true) { - do { - if let url = self.url { + if let url = self.url, !self.isBundled { try self.textView.text.write(to: url, atomically: true, encoding: .utf8) } DispatchQueue.main.async { diff --git a/Pyto/Views/FileCollectionViewCell.swift b/Pyto/Views/FileCollectionViewCell.swift index 7358f3ecf..fe3413510 100644 --- a/Pyto/Views/FileCollectionViewCell.swift +++ b/Pyto/Views/FileCollectionViewCell.swift @@ -229,12 +229,14 @@ class FileCollectionViewCell: UICollectionViewCell, UIDocumentPickerDelegate, Sy } override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool { - if isDirectory.boolValue { + if file?.path.hasPrefix(Bundle.main.bundlePath) == true { + return (action == #selector(open(_:))) + } else if isDirectory.boolValue || file?.pathExtension.lowercased() == "md" || file?.pathExtension.lowercased() == "markdown" { return ( action == #selector(remove(_:)) || - action == #selector(rename(_:)) || - action == #selector(copyFile(_:)) || - action == #selector(move(_:)) + action == #selector(rename(_:)) || + action == #selector(copyFile(_:)) || + action == #selector(move(_:)) ) } else { return ( @@ -261,7 +263,17 @@ class FileCollectionViewCell: UICollectionViewCell, UIDocumentPickerDelegate, Sy func didChangeSelectedRange(_ syntaxTextView: SyntaxTextView, selectedRange: NSRange) {} func lexerForSource(_ source: String) -> Lexer { - return Python3Lexer() + if file?.pathExtension.lowercased() == "py" { + return Python3Lexer() + } else { + struct PlainTextLexer: SourceCodeRegexLexer { + func generators(source: String) -> [TokenGenerator] { + return [] + } + } + + return PlainTextLexer() + } } // MARK: - Collection view data source diff --git a/Pyto/Welcome to Pyto.md b/Pyto/Welcome to Pyto.md new file mode 100644 index 000000000..8f8eb73d4 --- /dev/null +++ b/Pyto/Welcome to Pyto.md @@ -0,0 +1,23 @@ +# Welcome to Pyto + +Thank you for downloading Pyto. You can now write and run Python scripts. + +# Getting Started + +* Tap `⊞` to return to the file browser. + +* To create a new script, tap `+` at top right of the file browser. + +* To run a script open it and tap `►`. The output will be shown and the keyboard will appear when input will be required. + +* To pass arguments, tap the `args` button + +* To change the app's theme, press the `Info` button at top left of the file browser, then tap `Theme` and choose the theme you want. + +# Installing third party modules + +Pyto has a minimal version of pip. + +For installing a module, tap `pip` button on the file browser. Arguments will be asked. If you want to install a module, write `install ` and if you want to uninstall a module, then write `uninstall `. + +Pyto cannot compile modules and cannot link shared libraries from outside the app bundle. So `pip` will fail for packages like `pandas`, `scipy` or `numpy`. `numpy` is already included. I'm trying to include `pandas`, but it's not easy.