Skip to content

Commit

Permalink
Merge pull request #180 from Automattic/develop
Browse files Browse the repository at this point in the history
Simplenote Mark 1.3
  • Loading branch information
jleandroperez authored Feb 19, 2018
2 parents c98869e + 10e9db4 commit 28ed6c5
Show file tree
Hide file tree
Showing 47 changed files with 2,212 additions and 142 deletions.
2 changes: 1 addition & 1 deletion DB5/VSTheme.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
//

#import <Foundation/Foundation.h>

#import <AppKit/AppKit.h>

typedef NS_ENUM(NSUInteger, VSTextCaseTransform) {
VSTextCaseTransformNone,
Expand Down
59 changes: 59 additions & 0 deletions External/Notepad/Element.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
//
// Element.swift
// Notepad
//
// Created by Rudd Fawcett on 10/14/16.
// Copyright © 2016 Rudd Fawcett. All rights reserved.
//

import Foundation


/// A String type enum to keep track of the different elements we're tracking with regex.
public enum Element: String {
case unknown = "x^"

case h1 = "^(\\#[^\\#](.*))$"
case h2 = "^(\\#{2}(.*))$"

case body = ".*"

case bold = "(^|[\\W_])(?:(?!\\1)|(?=^))(\\*|_)\\2(?=\\S)(.*?\\S)\\2\\2(?!\\2)(?=[\\W_]|$)"
case italic = "(^|[\\W_])(?:(?!\\1)|(?=^))(\\*|_)(?=\\S)((?:(?!\\2).)*?\\S)\\2(?!\\2)(?=[\\W_]|$)"
case boldItalic = "(\\*\\*\\*\\w+(\\s\\w+)*\\*\\*\\*)"
case inlineCode = "\\`([^\\`].*?)\\`"

case url = "\\[([^\\]]+)\\]\\(([^\\)\"\\s]+)(?:\\s+\"(.*)\")?\\)"
case image = "\\!\\[([^\\]]+)\\]\\(([^\\)\"\\s]+)(?:\\s+\"(.*)\")?\\)"
case quote = "^(\\>[^\\>](.*))$"
case firstLine = "\\A.*"

/// Converts an enum value (type String) to a NSRegularExpression.
///
/// - returns: The NSRegularExpression.
func toRegex() -> NSRegularExpression {
return self.rawValue.toRegex()
}

/// Returns an Element enum based upon a String.
///
/// - parameter string: The String representation of the enum.
///
/// - returns: The Element enum match.
func from(string: String) -> Element {
switch string {
case "h1": return .h1
case "h2": return .h2
case "body": return .body
case "bold": return .bold
case "italic": return .italic
case "boldItalic": return .boldItalic
case "url": return .url
case "image": return .image
case "quote": return .quote
case "firstLine": return .firstLine
case "inlineCode": return .inlineCode
default: return .unknown
}
}
}
72 changes: 72 additions & 0 deletions External/Notepad/Extensions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
//
// Extensions.swift
// Notepad
//
// Created by Rudd Fawcett on 10/14/16.
// Copyright © 2016 Rudd Fawcett. All rights reserved.
//

import Foundation

#if os(iOS)
import struct UIKit.CGFloat
#elseif os(macOS)
import struct AppKit.CGFloat
#endif

extension UniversalColor {
/// Converts a hex color code to UIColor.
/// http://stackoverflow.com/a/33397427/6669540
///
/// - parameter hexString: The hex code.
convenience init(hexString: String) {
let hex = hexString.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
var int = UInt32()
Scanner(string: hex).scanHexInt32(&int)
let a, r, g, b: UInt32
switch hex.count {
case 3: // RGB (12-bit)
(a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17)
case 6: // RGB (24-bit)
(a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF)
case 8: // ARGB (32-bit)
(a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF)
default:
(a, r, g, b) = (255, 0, 0, 0)
}
self.init(red: CGFloat(r) / 255, green: CGFloat(g) / 255, blue: CGFloat(b) / 255, alpha: CGFloat(a) / 255)
}
}

extension String {
/// Converts a String to a NSRegularExpression.
///
/// - returns: The NSRegularExpression.
func toRegex() -> NSRegularExpression {
var pattern: NSRegularExpression = NSRegularExpression()

do {
try pattern = NSRegularExpression(pattern: self, options: .anchorsMatchLines)
} catch {
print(error)
}

return pattern
}

/// Converts a NSRange to a Range<String.Index>.
/// http://stackoverflow.com/a/30404532/6669540
///
/// - parameter range: The NSRange.
///
/// - returns: The equivalent Range<String.Index>.
func range(from nsRange: NSRange) -> Range<String.Index>? {
guard
let from16 = utf16.index(utf16.startIndex, offsetBy: nsRange.location, limitedBy: utf16.endIndex),
let to16 = utf16.index(from16, offsetBy: nsRange.length, limitedBy: utf16.endIndex),
let from = String.Index(from16, within: self),
let to = String.Index(to16, within: self)
else { return nil }
return from ..< to
}
}
24 changes: 24 additions & 0 deletions External/Notepad/Info.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>
16 changes: 16 additions & 0 deletions External/Notepad/Notepad-macOS.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// Notepad-macOS.swift
// Notepad
//
// Created by Christian Tietze on 2017-07-21.
// Copyright © 2017 Rudd Fawcett. All rights reserved.
//

#if os(macOS)
import AppKit

public class Notepad: NSTextView {

var storage: Storage = Storage()
}
#endif
145 changes: 145 additions & 0 deletions External/Notepad/Storage.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
//
// Storage.swift
// Notepad
//
// Created by Rudd Fawcett on 10/14/16.
// Copyright © 2016 Rudd Fawcett. All rights reserved.
//

#if os(iOS)
import UIKit
#elseif os(macOS)
import AppKit
#endif

@objc public class Storage: NSTextStorage {
/// The Theme for the Notepad.
public var theme: Theme? {
didSet {
let wholeRange = NSRange(location: 0, length: (self.string as NSString).length)

self.beginEditing()
self.applyStyles(wholeRange)
self.edited(.editedAttributes, range: wholeRange, changeInLength: 0)
self.endEditing()
}
}

/// The underlying text storage implementation.
var backingStore = NSTextStorage()

var markdownEnabled = false

override public var string: String {
get {
return backingStore.string
}
}

override public init() {
super.init()
}

@objc public class func newInstance() -> Storage {
let storage = Storage()
storage.theme = Theme(markdownEnabled: false)
return storage
}

override public init(attributedString attrStr: NSAttributedString) {
super.init(attributedString:attrStr)
backingStore.setAttributedString(attrStr)
}

required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

required public init(itemProviderData data: Data, typeIdentifier: String) throws {
fatalError("init(itemProviderData:typeIdentifier:) has not been implemented")
}

#if os(macOS)
required public init?(pasteboardPropertyList propertyList: Any, ofType type: NSPasteboard.PasteboardType) {
fatalError("init(pasteboardPropertyList:ofType:) has not been implemented")
}
#endif

/// Finds attributes within a given range on a String.
///
/// - parameter location: How far into the String to look.
/// - parameter range: The range to find attributes for.
///
/// - returns: The attributes on a String within a certain range.
override public func attributes(at location: Int, effectiveRange range: NSRangePointer?) -> [NSAttributedStringKey : Any] {
return backingStore.attributes(at: location, effectiveRange: range)
}

/// Replaces edited characters within a certain range with a new string.
///
/// - parameter range: The range to replace.
/// - parameter str: The new string to replace the range with.
override public func replaceCharacters(in range: NSRange, with str: String) {
self.beginEditing()
backingStore.replaceCharacters(in: range, with: str)
let change = str.utf16.count - range.length
self.edited(.editedCharacters, range: range, changeInLength: change)
self.endEditing()
}

override public func addAttribute(_ name: NSAttributedStringKey, value: Any, range: NSRange) {
self.beginEditing()
backingStore.addAttribute(name, value: value, range: range)
self.endEditing()
}

override public func removeAttribute(_ name: NSAttributedStringKey, range: NSRange) {
self.beginEditing()
backingStore.removeAttribute(name, range: range)
self.edited(.editedAttributes, range: range, changeInLength: 0)
self.endEditing()
}

/// Sets the attributes on a string for a particular range.
///
/// - parameter attrs: The attributes to add to the string for the range.
/// - parameter range: The range in which to add attributes.
override public func setAttributes(_ attrs: [NSAttributedStringKey : Any]?, range: NSRange) {
self.beginEditing()
backingStore.setAttributes(attrs, range: range)
self.edited(.editedAttributes, range: range, changeInLength: 0)
self.endEditing()
}

/// Processes any edits made to the text in the editor.
override public func processEditing() {
let backingString = backingStore.string
let nsRange = backingString.range(from: NSMakeRange(NSMaxRange(editedRange), 0))!
let indexRange = backingString.lineRange(for: nsRange)
let extendedRange: NSRange = NSUnionRange(editedRange, NSRange(indexRange, in: backingString))

applyStyles(extendedRange)
super.processEditing()
}

/// Applies styles to a range on the backingString.
///
/// - parameter range: The range in which to apply styles.
func applyStyles(_ range: NSRange) {
guard let theme = self.theme else { return }

let backingString = backingStore.string
backingStore.setAttributes(theme.body.attributes, range: range)

for (style) in theme.styles {
style.regex.enumerateMatches(in: backingString, options: .withoutAnchoringBounds, range: range, using: { (match, flags, stop) in
guard let match = match else { return }
backingStore.addAttributes(style.attributes, range: match.range(at: 0))
})
}
}

@objc public func applyStyle(markdownEnabled: Bool) {
self.theme = Theme(markdownEnabled: markdownEnabled)
}
}
28 changes: 28 additions & 0 deletions External/Notepad/Style.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// Style.swift
// Notepad
//
// Created by Rudd Fawcett on 10/14/16.
// Copyright © 2016 Rudd Fawcett. All rights reserved.
//

import Foundation

public struct Style {
var regex: NSRegularExpression!
var attributes: [NSAttributedStringKey: AnyObject] = [:]

init(element: Element, attributes: [NSAttributedStringKey: AnyObject]) {
self.regex = element.toRegex()
self.attributes = attributes
}

init(regex: NSRegularExpression, attributes: [NSAttributedStringKey: AnyObject]) {
self.regex = regex
self.attributes = attributes
}

init() {
self.regex = Element.unknown.toRegex()
}
}
Loading

0 comments on commit 28ed6c5

Please sign in to comment.